お品書き
- AndroidアプリのCI環境をBitriseで構築
- DangerでPull Request時のLintチェック
- Jacoco+Codecovでテストのカバレッジ可視化
- ビルド完了はSlackに通知
環境
- macOS Mojave 10.14.5
- Android Studio 3.4.1
AndroidアプリのCI環境をBitriseで構築
とりあえずデフォルトプロジェクトをEmpty Activityで作成してユニットテスト用に簡単なクラスを用意します。
今回はLocal Unit Testのみです。
ソースコードはGitHubに上げていますので、詳細はそちらをご参照ください。
これをBitriseに追加していきます。
- Bitriseのアカウント未作成の場合
- アカウント登録後、「Add First App」から追加をします
- Bitriseのアカウント作成済みの場合
- Bitriseのダッシュボードを開き、下図の右上にある「Add New App」ボタンを押します。
あとは、アカウント、サービス、リポジトリなどを順に設定していくと、プロジェクトのプラットフォームを自動で判別してくれます。
とりあえず、Project build configurationなどは、普通に作成していればそのままで問題ありません。
今回のサンプルではvariantも特に指定しません。
WebHookについてはPull Requestをトリガーに自動でビルドしたいので登録しておきましょう。
全て完了したら、「We've kicked of your first test build for you!」とあるのでそこをクリックしてビルド画面に移動します。
最初は「primary」というワークフローにRepository clone, build, lint, unit testなど一通り実行される設定がされています。
このビルドが通るのが確認できたら基本設定は完了です。
Dangerでプルリク時のLintチェック
大まかな手順としては以下の通りです。
- ローカルでDangerfileを作成する
- GitHubでPersonal access tokenを作成する
- Bitriseで作成したAccess tokenを設定する
- BitriseでDangerを動かすWorkflowを追加する
- トリガーを変更する
以下、それぞれの手順です。
1. ローカルでDangerfileを作成する
Bundler
を利用する場合と利用しない場合の2種類のセットアップがあります
どちらかの方法でDangerfile
を作成後、以下のサンプルのようにDangerfile
を編集してコミットしてください。
-
Dangerfileのサンプル
- 「WIP」ラベルが付与されているか
- 500行を超えるプルリクか
- android_lintは問題ないか
Bundler
を利用しない場合
gem install danger
danger init
Bundler
を利用する場合
gem install bundler
cd {プロジェクトルート}
bundle init
echo "gem \"danger\"" >> Gemfile
echo "gem \"danger-android_lint\"" >> Gemfile
bundle install --path vendor/bundle
echo "
# vendor
vendor/
" >> .gitignore
bundle exec danger init
{プロジェクトルート}
の部分はAndroidプロジェクトのルートディレクトリです。
vender/bundle配下はGitにコミットしたくないので、.gitignoreにパスを追加します。
Bundlerを使う場合の注意点
個人的にはBundler
を利用する方が好みですが、その場合の注意点としてはBitrise上のBundler
は1.x
に対して、今ローカルでインストールすると2.x
がインストールされます。
(2019/07/01現在ではBitriseが1.17.3で、最新が2.0.2です)
2.x
で作ったGemfile.lock
でBitriseで動かすと以下のようにエラーになります。
Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
installing your bundle as root will break this application for all non-root
users on this machine.
You must use Bundler 2 or greater with this lockfile.
この場合の対処は以下があります
-
Bundler
で管理するのをやめる - ローカルの
Bundler
もBitriseと同じ1.17.3
にする - ローカルにもBitriseと同じバージョンの
Bundler
をインストールして、bundle _1.17.3_ install --path vendor/bundle
のようにバージョン指定して使う- 複数バージョンの
Bundler
を扱うには以下の記事が参考になります
- 複数バージョンの
なお、後述するDangerを実行するStepで「Run Danger」を利用する場合はBundler
での管理は必須です。
(Gemfile
をコミットしないと動きません)
2. GitHubでPersonal access tokenを作成する
- GitHubのアカウント設定のPersonal access tokensの画面を開く
- Settings > Developer settings > Personal access tokens
- 「Generate new token」からtokenを作成する
- scopeはrepoのみフル権限あれば十分です
- 生成されたtokenを控えておく
- わからなくなった場合はtokenを作成しなおす必要があります
3. Bitriseで作成したAccess tokenを設定する
- 作成したアプリケーションをDashbordから選択し、Workflowのタブを選択する
- 「Secrets」のタブを選択する
- 「Secrets」に登録する情報は、BitriseのBuildコンソール上からも表示されません。
- Access Tokenなど隠しておきたいものはここに登録すると良いです
- 「Add New」ボタンから追加する
- Saveボタンを押下する
4. BitriseでDangerを動かすWorkflowを追加する
まずはDanger実行用のWorkflowを作成します
- 作成したアプリケーションをDashbordから選択し、Workflowのタブを選択する
- 「+ Workflow」からDanger用のWorkFlowを作成する
- 名前は任意で大丈夫です
- BASED ON を「primary」にします
- Dangerを実行するStepを追加する
BitriseでDangerを動かすには以下の2種類の方法があります。(他にもあるかもしれませんが)
どちらかのStepを「Android Unit Test」Stepの後に追加しましょう。
Run Danger
Stepを使う
BitriseではDanger用のStepがあります。
こちらで実行する場合はBundler
での管理が必要になります。
また、設定項目としては以下の通り、GitHubのアクセストークン設定をすればOKです
- GitHubの項目をクリックして設定項目を開き、「Access token for your project」の項目をクリックするとさらに詳細が表示されます
- 「Select secret variable」を選択して、登録してあるGitHubのAccess tokenを選択する
Script
Stepを使う
こちらは普通にScriptを実行するStepです。Bundler
を使うか使わないかで以下のように設定してください。
ただし、danger実行時に参照されるリポジトリのURLがSSHのものが設定されている必要があるので、環境変数GIT_REPOSITORY_URL
にSSHのパスを設定する必要があります。
以下の例は、サンプルプロジェクトの場合の設定です。
Bundler
を利用しない場合
export GIT_REPOSITORY_URL=git@github.com:sadashi-ota/android-bitrise-sample.git
gem install danger
gem install danger-android_lint
danger
Bundler
を利用する場合
export GIT_REPOSITORY_URL=git@github.com:sadashi-ota/android-bitrise-sample.git
bundle install
bundle exec danger
5. トリガーを変更する
- 作成したアプリケーションをDashbordから選択し、Workflowのタブを選択する
- 「Triggers」のタブを選択する
- 「PULL REQUEST」で実行するWorkflowをDangerを設定したWorkflowに変更する
- 右上の「⌘+S」ボタンを押す
ついでに、「PUSH」で実行されるbranchもmaster
のみに変更しておいても良いと思います。
(プルリク作るときにPUSHでもHookされてビルドが走るので)
これでDanger
の設定は完了です。
実行結果
LintのチェックでエラーがあるとDangerがこんな感じでコメントしてくれます。
Jacoco+Codecovでテストのカバレッジ可視化
次にJacocoを導入してユニットテストのカバレッジを可視化していきます。
Jacoco導入
Jacoco導入の設定例です
@@ -10,8 +10,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ classpath "org.jacoco:org.jacoco.core:0.8.4"
}
}
@@ -1,6 +1,11 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
+apply plugin: 'jacoco'
+
+jacoco {
+ toolVersion = "0.8.4"
+}
android {
compileSdkVersion 28
@@ -13,6 +18,9 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
+ debug {
+ testCoverageEnabled true
+ }
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@@ -33,3 +41,23 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
+
+task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
+ group = "Reporting"
+ description = "Generate Jacoco coverage reports after running tests."
+ reports {
+ xml.enabled = true
+ html.enabled = true
+ }
+
+ def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
+ def debugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
+ def mainSrc = "$project.projectDir/src/main/java"
+
+ sourceDirectories = files([mainSrc])
+ classDirectories = files([debugTree])
+ executionData = fileTree(dir: project.buildDir, includes: [
+ 'jacoco/testDebugUnitTest.exec',
+ 'outputs/code-coverage/connected/*coverage.ec'
+ ])
+}
これでjacocoTestReport
というタスクを実行するとhtmlとxmlが出力されます。
おそらく出力先はapp/build/reports/jacoco/jacocoTestReport/
配下になっていると思います。
Codecovの設定
Codecovの登録
「Add new repository」からリポジトリを登録します
Repository Upload Tokenが発行されますので、それをBitriseで設定します。
Bitriseの設定
「Android Unit Test」Stepの後に以下のStepを追加します
- jacocoのレポート作成Stepを追加する
- 「Script」のStepを選択し、
./gradlew :app:jacocoTestReport
を実行する
- 「Script」のStepを選択し、
- その後に「Codecov」のStepを追加する
- Codecovで発行したtokenを
Input variables
>Codecov.io token
に登録します - Keyは任意で大丈夫ですが、今回は
CODECOV_TOKEN
に設定します
- Codecovで発行したtokenを
これをprimary
とDangerを設定したWorkflowに追加すれば完了です。
サンプルのリポジトリではわざとカバレッジが足りないようにしてありますので、一度primaryのWorkflowでビルドします。
完了後、Codecovに現在のカバレッジ等が表示されるようになります。
この状態からブランチを作成して、カバレッジが増えるような修正をしてみましょう。
そのプルリクがこちらです。
マージするbaseブランチでCodecovが実行済みだと、compareブランチとの差分が表示されます。
もちろんテストのカバレッジはCodecovからでも詳細にみれます。
以上でJacocoおよびCodecovの設定も完了です!
ビルド完了はSlackに通知
これは以下のブログで紹介されていました
手順は以下の通りです
- Slackの「アプリを追加する」から「Incoming Webhook」を選ぶ
- Slackの設定を整える
- 通知先のチャンネルとか、アイコンとか
- 設定画面の「WebHook URL」をコピーする
- Bitriseで「Send a Slack message」のStepをWorkflowの最後に追加する
- 「Slack WebHook URL」に先ほどコピーしたURLを設定する
- 環境変数は任意で大丈夫です
- 「Target Slack channel, group or username」で通知するチャンネルを設定する
- ビルドが失敗した時の通知は「If Build Failed」の項目を開いて「Target Slack channel, group or username」を設定する
デフォルト設定だと結果はこんな形で表示されます
以上でSlackの通知も完了です。
あとがき
いかがでしたでしょうか?
BitriseはGUIで設定ができるので、簡単にWorkflowが作れるのはメリットですね。
初期構築で少しつまづきやすいポイントも紹介してみましたので、初めて触る方のお役に立てば幸いです。
また、CI環境の構築に慣れている方は、「こんなStepもあったら楽なのに」って思うようなものがあれば、コントリビュートしてみるのも面白いかもしれませんね。