読者です 読者をやめる 読者になる 読者になる

Dokumi (日本語)

(English version here)

技術部モバイル基盤グループのヴァンサン(@vincentisambart)です。今日は最近作ったツール「Dokumi」の話をしようと思います。

紹介

他部署のエンジニアの仕事をもっと楽にすることが、技術部の重要な目的の1つです。その中で、Dokumiはモバイル開発者のコードレビューの負荷を減らすためのツールです。

なぜ「毒味」という名前にしたかと言うと、人間がレビューする前に、コードに毒(バグ、不自然なコードなど)が入っているかどうか毒味するツールだからです。別の言葉で言うと、少し進化したCI用のlintツールですね。pull requestが出る度に、Jenkinsがそのpull requestにDokumiをかけます。現在はDokumiはiOSアプリだけに対応してしていますが、今後はAndroidアプリへの対応も考えています。

現時点でDokumiは以下のことをやっています。

  • 静的解析(Xcodeの「Analyze」機能)をかけます。 f:id:vincentisambart:20150603121414p:plain
  • 自動テストを走らせます。 f:id:vincentisambart:20150603121409p:plain
  • コミットされたけどXcodeのバージョンデータしか変わっていないXIB/Storyboardファイルを指摘します。 f:id:vincentisambart:20150603121400p:plain
  • pull requestにmilestoneが指定されていないとそれを指摘します。 f:id:vincentisambart:20150603121419p:plain
  • 静的解析や自動テストを走らせるためにアプリをビルドするので、ビルド中に出ている警告やエラーも同時に拾います。

以上のキャプチャーで見られるように、Dokumiは、GitHub Enterpriseと連動して、指摘をpull requestのコメントとして投稿します。指摘の対象がこのpull requestで変わる行であれば、行にコメントを付けます。対象が変わっていない行であれば、pull requestと関係ありそうなものは普通のコメントにまとめて投稿します。pull requestと関係なさそうなもの(例えばこのpull requestで変わっていないファイルに出る警告)は無視されます。

メリット

XcodeのTestやAnalyzeは、開発者が自分自身でかけることができますが、時間かかるし、かけるのを忘れる時もあります。また、 gitに変更を入れるのを忘れることもあります。なのでDokumiはすべてのpull requestに強制的にかけます。因みにXcodeの静的解析のお陰で人間がレビューしたコードでも不具合や変なコードを見つけることができたのでやってみることをおすすめします。

また、直接GitHub Enterpriseのpull requestにコメントを付けるメリットも大きいと思います。pull requestの度に自動テストを走らせるのは、以前から行われていました。しかし、失敗したテストを知るには、Jenkinsのログのxcodebuildの出力を見るしかなく、簡単ではありませんでした。

実は元々Dokumiの結果はウェブページとして整えようと思っていたけれど、良いデザインが思いつかなかったし、わざわざデザイナーに頼んでもな…と思っていたら、単に、普通の人間のレビューと同じようにすればいいだけだと気づきました。

困ったこと

GitHubのAPIを使って実装しようとした時に、特定の行にコメントを付けるには現時点(2015年6月頭)のAPIでは不足しているところがあると気づきました。

特定の行にコメントを付けるエンドポイントの説明を見ると、指定する必要あるのは「The line index in the diff to comment on.」です。ファイルの行番号ではなくdiffの中の行番号ですね。diffから計算するしかない…そしてこれだけだったらまだしも、そもそもAPIでdiffを取得する方法がない…

GitHubはpull requestのURLに.diffや.patchを付けるとdiffを取得できるのですが、そのdiffはpull requestのコメントに使われているdiffと相違があります:ファイル名変更の対応が違います。.diff/.patchで取得できるdiffはファイル名変更が:

  • 旧ファイル名の削除
  • 新ファイル名の追加

になります。pull requestのコメントで使われているdiffはファイル名変更が:

  • ファイル名変更自体
  • 変わっている行だけの追加・削除

になります。gitをコマンドラインで使うと、git diff--find-renames (略して-M) を付けるかどうかの違いと同じです。

GitHubのAPIで取得できるdiffに find-renames を指定できないならどうすればいいのでしょうか。いまDokumiはレポジトリを手元でcloneして、Ruggedというライブラリを使ってdiffを計算します。もし計算のやり方がGitHubのと違っていれば、ズレが出てくる可能性がありますが、それでも大きな問題にならないでしょう。

pull requestでnew-featuremasterにマージしたいと仮定すると、僕の理解が合っていれば、pull requestのページに出ているdiffをコマンドラインで見たいときは:

$ git diff --find-renames `git merge-base new-feature master`..new-feature

質問コーナー

どの言語で実装されていますか? Rubyですね。僕自身も昔からRubyを使っていますし、社内にはRubyエンジニアが多いからです。

どうやって警告やエラーを拾っているのですか? 主に正規表現を使ってxcodebuildの出力から拾っているだけです。静的解析の指摘はxcodebuild analyzeがビルドディレクトリに生成したplistファイルを見ています。

オープンソース化の予定はありますか? まだ未定です。どの環境でも動くようにするにはコストが高いと思いますし、他人が使えないものをオープンソースする意味もあまり感じないですね。でも実装に関する質問あればお答えします。ソースコードがGitHubにて公開されました

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/