TL;DR
- GitHubのPull Request (PR) を作成したときに、PRのDiffに含まれるKotlinコードのスタイルを自動的にチェックして、問題があればPRに自動的にコメントさせる方法の説明です
- 以下のツールを組み合わせて使っています
- サンプル: Pull Request #3 · kafumi/android-danger-ktlint-sample
導入のきっかけ
- Android Studioでフォーマッターをかける (Command + Alt + L) ことをチーム内のルールにして、コードスタイルの統一を図っているのですが、ただ、そこは悲しい人の性、フォーマッターをかけ忘れたコードがときどきコミットされて、コードレビュー時に指摘されたりします
- コードスタイルに対して神経質なレビュワーは、レビュー対象コードをチェックアウトして、Android Studioでフォーマットし、差分がでないことを確かめたりしてます (私です)
- コードスタイルなど、本質的でないことに対するレビューの労力は極力減らしたい!
- 最近よく目にする Danger を使ってみたら、コードスタイルのチェックが自動化できて幸せになりました
仕組みの全体像
- GitHubでPRを作成したとき、Travis CIのビルドが走る
- Travis CIのビルドの中で、ktlintの実行、Dangerの実行を行う
- ktlintはGradleから実行する (ktlint-gradle プラグインを使う)
- ktlintが出力する Checkstyle 形式のレポートを、Dangerが danger-checkstyle_format プラグインを使って読み込む
- Dangerが、ktlintが報告した警告をGitHubのPRにコメントする
Dangerとは
- http://danger.systems/ruby/
- コードレビューbotのフレームワーク
- Ruby製で、多くのCIシステム、コードホスティングサービスをサポートしている
- プラグイン方式をとっており、プラグインによって様々な機能を追加できる
- この記事では説明しませんが、Android Lint を実行するためのプラグイン danger-android_lint もあります
- 参考
ktlintとは
- https://ktlint.github.io/
- KotlinコードのLinter
- Lint結果を様々な形式で出力できる (プレーンテキスト, JSON, Checkstyle XMLなど)
- フォーマッターが組み込まれており、コードの自動フォーマットも可能
- 様々なインストール方法・実行方法のオプションがある
ktlint
コマンドをコマンドラインで実行する- Mavenに組み込んで実行する
-
Gradleに組み込んで実行する
- タスクを定義するのが割と面倒なので ktlint-gradle プラグインや kotlinter-gradle プラグインを使うのが吉
設定手順
環境
- 今回は、筆者が実際に使っている環境を例に説明します
- DangerがサポートしているCIシステム、コードホスティングサービスなら、どの組み合わせでも設定可能です
- Dangerがサポートしているシステムは、Danger Webサイトの "WHAT MAGIC IS THIS" - "SUPPORTS" に列挙されています
Travis CIの準備
まずは、GitHubでPull Requestが作成されたときに、Travis CI でビルドを走らせる必要があります。
基本的には、Androidプロジェクトビルド用の .travis.yml
ファイルを作成してリポジトリにコミットしておき、Travis CIの設定画面で当該リポジトリに対するビルドを有効にすればOKです。.travis.yml
は、典型的には次のようなファイルになります。
# .travis.yml
language: android
branches:
only:
- master
android:
components:
- tools
- platform-tools
- build-tools-26.0.2
- android-26
- extra-android-m2repository
script:
- ./gradlew assembleDebug
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
この .travis.yml
には、まだ、Dangerの実行は含まれていません。Dangerの実行を追加する方法は、この後のセクションで説明します。
もし、Travis CIを使ったことがなく、上記のYAMLファイルが何を設定しているのかわからない場合は、Travis CIの次のDocumentページを参考にしてみてください。この3つを読めばだいたい分かるようになると思います。
- Getting started - Travis CI
- Customizing the Build - Travis CI
- Building an Android Project - Travis CI
ktlintの設定
次に、ktlint の設定を行います。Androidプロジェクトに対してktlintを実行する場合、主に次の方法があります。
- コマンドライン
- Gradle
- Gradleの依存関係にktlintを追加して、ktlintを実行するGradleタスクを自分で定義する
-
ktlint-gradle プラグインや kotlinter-gradle プラグインを使う
- これらのプラグインは、ktlintを実行するGradleタスク定義を自動的に行なってくれます
Gradleで完結していると実行環境の準備が楽チンなので、Gradleを使う方法、特に、Gradleプラグインを使う方法がおすすめです。この記事では、ktlint-gradle プラグインを使います。
ktlint-gradleプラグインの導入
JLLeitschuh/ktlint-gradle の How to use に書かれているとおり、ktlint-gradle
をGradleの依存先に追加し、apply plugin
すれば、ktlint-gradle
プラグインが導入できます。下の例では、2017年12月10日現在で最新の 2.3.0
を使用しています。プラグインの最新バージョンは、Gradle Plugin Registry で確認できます。
// build.gradle
buildscript {
repositories {
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "gradle.plugin.org.jlleitschuh.gradle:ktlint-gradle:2.3.0"
}
}
// app/build.gradle
apply plugin: "org.jlleitschuh.gradle.ktlint"
ktlint-gradleプラグインの設定
次に、Dangerと組み合わせて使うために、Configuration で説明されている方法を使って ktlint-gradle
プラグインの設定を変更します。また、あわせて、Android Kotlin Style Guide に準拠したLintを行うように設定します。
(2017年12月10日編集: ktlint-gradle
プラグインのバージョンアップ2.1.1 → 2.3.0にあわせて、設定内容を変更しました)
// app/build.gradle
ktlint {
version = "0.14.0"
android = true
reporter = "checkstyle"
ignoreFailures = true
}
-
version
: 利用するktlintのバージョンを指定します。ktlint-gradle
プラグインの2.3.0は、デフォルトではktlintの 0.9.2 を使用するのですが、ktlint 0.9.2は Android Kotlin Style Guide に準拠したLintをサポートしていません (ktlint 0.12.0 で追加された機能です)。Android Kotlin Style Guide に準拠したLintを行いたい場合、0.12.0より新しいバージョンのktlintを使う必要があります。ここでは、2017年12月10日時点で最新の 0.14.0 を使うようにしています -
android
: ktlintの--android
オプションを有効にするかどうかを指定します。--android
オプションが有効な場合、ktlintは Android Kotlin Style Guide に準拠したスタイルチェックを行うようになります -
reporter
: レポートの出力形式を指定します。今回は、Dangerで扱えるようにCheckstyle形式の出力を得たいので、checkstyle
を指定します -
ignoreFailures
: 警告があってもビルドを継続するかどうかを指定します。ktlint-gradle
は、デフォルト設定だと、ktlintからの指摘が1件でもあるとGradleタスクの結果を失敗として報告するようになっています (ビルドがエラーで止まります)。一般的に、コードスタイルに関する指摘があっただけでビルド失敗にしたくないと思うので、指摘があってもビルドを継続する (Gradleタスクは成功とする) ために、true
を指定します
以上の設定を行なうと、Gradleに ktlintCheck
と ktlintFormat
というタスクが追加されます。
-
ktlintCheck
: ktlintによるKotlinコードのLintが実行される- Lintレポートは
app/build/reports/ktlint/ktlint-<sourceSet>.xml
に出力される-
main
ソースセットの場合、出力先はapp/build/reports/ktlint/ktlint-main.xml
となる
-
- Lintレポートは
-
ktlintFormat
: ktlintによるKotlinコードの自動フォーマットが実行される- 本記事ではこちらのタスクは使っていません
Dangerの設定
Danger公式サイト の Getting Set Up に従って、Dangerを設定します。
なお、Gem danger
をインストールした状態で bundle exec danger init
を実行すると、テキストベースの設定ウィザードが実行できるのですが、Getting Set Up で説明されていることが表示されるだけなので、特に実行しなくて問題ありません (ただし、実行すると Dangerfile
の雛形を作成してくれるという利点はあります)。
Gemfile
を作成する
DangerはRuby製であり、プラグインも含めた依存関係を Gemfile
に定義し、Bundler でインストールします。まずは、Androidアプリプロジェクトのルートディレクトリで bundle init
を実行し、Gemfile
の雛形を生成します。
$ bundle init
生成された Gemfile
をエディターで開き、#gem 'rails'
の部分を必要なGemで置き換えます。今回は、danger
と danger-checkstyle_format
の2つのGemを追加します。
# Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
gem "danger"
gem "danger-checkstyle_format"
上記の編集が終わったら、bundle init
を実行したのと同じディレクトリで bundle install
を実行し、上記のGemをインストールします。
$ bundle install
Dangerfile
の作成
Dangerは、Dangerfile
というファイルの内容にもとづいて検査を実行します。Dangerfile
はRuby DSL形式のファイルで、DangerコアやDangerプラグインが提供するAPIを使って、実行内容を定義します。利用できるAPIや記述例は、以下のページで紹介されています。
- Dangerコアが提供する機能: Danger - Reference
- Dangerプラグインが提供する機能: Danger の "PLUGINS" セクション
今回は、ktlintの指摘を報告する他に、Pull Requestのタイトルに [WIP]
が入っていたらコメントで警告する (WIP中なのがわかりやすくなる) ように設定してみます。Gemfile
と同じディレクトリに、 以下の内容で Dangerfile
というファイルを作成します。
# Ignore inline messages which lay outside a diff's range of PR
github.dismiss_out_of_range_messages
# Make it more obvious that a PR is a work in progress and shouldn't be merged yet
warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]"
# ktlint
checkstyle_format.base_path = Dir.pwd
checkstyle_format.report 'app/build/reports/ktlint/ktlint-main.xml'
上記の定義の意味は次の通りです。
-
github.dismiss_out_of_range_messages
は、Pull RequestのDiff以外に対する指摘を無視する (PRに報告しないようにする) 設定です -
warn
で始まる行は、Pull Requestのタイトルに[WIP]
が入っているかどうかで警告を出すための設定です - 最後の2行は、
danger-checkstyle_format
プラグインを使って、ktlintが出力したCheckstyle形式のレポートにもとづいてPRにコメントするための設定です-
2017年12月10日編集:
ktlint-gradle
プラグインのバージョンアップ2.1.1 → 2.3.0にあわせて、レポートファイルのパスを変更しました
-
2017年12月10日編集:
GitHubアクセストークンの設定
DangerがPull Requestにコメントする際に使用するGitHubアカウント・アクセストークンを作成します。
GitHubアカウントの作成
Dangerが使うGitHubアカウントを作成します。自分のGitHubアカウントをDangerに使わせることもできますが、セキュリティのリスクを抑える (botアカウントなら権限を必要最小限にしぼっておける、アカウントの機密情報が漏洩したときにアカウント削除の手段がとりやすい、など) ために、別のアカウントを作成したほうがよいです。別アカウントだと、botによるコメントであることがわかりやすい、という利点もあります。
- Dangerの検査対象がPublicレポジトリである場合: botアカウントを当該レポジトリのCollaboratorに追加する必要はありません。アカウントを作成するだけでOKです
- Dangerの検査対象がPrivateレポジトリである場合: 作成したbotアカウントを当該レポジトリのCollaboratorや、レポジトリが属するOrganizationのメンバーに追加し、当該レポジトリへの "Write" 権限を与えてください
GitHubアクセストークンの発行
作成したbotアカウントでログインした状態で Generate new token のページにアクセスし、アクセストークンを発行します。
- Dangerの検査対象がPublicレポジトリである場合: 権限
public_repo
だけをトークンに付与します - Dangerの検査対象がPrivateレポジトリである場合:
repo
スコープの権限全てをトークンに付与します
発行されたトークンはこの後、環境変数に設定するときに使うので、必ず控えておいてください。
アクセストークンの環境変数への設定
Dangerは、環境変数 DANGER_GITHUB_API_TOKEN
からアクセストークンを取得します。
Travis CIにおいて、機密情報を含む環境変数を設定する方法は2つあります。
- Travis CIの各リポジトリの設定ページ (
https://travis-ci.org/[user]/[repo]/settings
) で、環境変数を追加する -
travis
コマンドで値を暗号化して、.travis.yml
に環境変数として定義する
Dangerの Getting Set Up ページでは方法1が紹介されているのですが、Travis CIの設定情報を .travis.yml
に集約しておきたい、という思いがあり、個人的には方法2が好みです。ここでは、方法2を紹介します。
まず、travis
コマンドをインストールします (まだインストールしていない場合)。
$ gem install travis
次に、検査対象レポジトリのルートディレクトリに行き、travis encrypt
コマンドを実行します。もし、検査対象レポジトリがPrivateレポジトリの場合は、ここで --pro
オプションをつけてください。
$ travis encrypt DANGER_GITHUB_API_TOKEN=[アクセストークン]
暗号化に成功すると、secure:
から始まる長い行が出力されるので、それを .travis.yml
の env.global
配列の要素として追加します。
# .travis.yml
env:
global:
- secure: "R+epV1sw... 中略 .../A0sGJ8="
Travis CIでのDangerの実行
これで、Travis CI実行環境で Danger を実行する準備が整いました。あとは、Travis CIのビルド中に、Dangerを実行するだけです。
まず、Dangerを実行する前に、必要なgemをインストールさせる必要があります。Travis CIの install
フェーズで bundle install
を実行します。
# .travis.yml
install:
- bundle install
あとは、script
フェーズで、Gradleの ktlintCheck
タスクを実行してktlintを実行した後、Dangerを実行すれば、ktlintが見つけた問題がPull Requestにコメントされます。
# .travis.yml
script:
- ./gradlew assembleDebug ktlintCheck
- bundle exec danger
サンプルプロジェクト
ここまでの設定を終えて、作成物をレポジトリにコミット・プッシュすれば完了です。
実際にひととおりの設定を行い、ktlintで問題を報告させてみたサンプルプロジェクトをGitHubにコミットしてありますので、良ければ参考にしてみてください。
- レポジトリ: kafumi/android-danger-ktlint-sample
- Pull Requestの例: Pull Request #3
Appendix: 設定ファイルの例
今回、説明した設定ファイルは、サンプルプロジェクトのリポジトリ kafumi/android-danger-ktlint-sample にコミットしてあります。
- Gradle
- Travis
- Danger