Posted at

AndroidアプリでBitrise+Danger+Jacoco+CodecovのCI環境セットアップ


お品書き


  • 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に上げていますので、詳細はそちらをご参照ください。

android-bitrise-sample

これをBitriseに追加していきます。


  • Bitriseのアカウント未作成の場合


    • アカウント登録後、「Add First App」から追加をします



  • Bitriseのアカウント作成済みの場合



あとは、アカウント、サービス、リポジトリなどを順に設定していくと、プロジェクトのプラットフォームを自動で判別してくれます。

とりあえず、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チェック

大まかな手順としては以下の通りです。


  1. ローカルでDangerfileを作成する

  2. GitHubでPersonal access tokenを作成する

  3. Bitriseで作成したAccess tokenを設定する

  4. BitriseでDangerを動かすWorkflowを追加する

  5. トリガーを変更する

以下、それぞれの手順です。


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上のBundler1.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のようにバージョン指定して使う



なお、後述するDangerを実行するStepで「Run Danger」を利用する場合はBundlerでの管理は必須です。

Gemfileをコミットしないと動きません)


2. GitHubでPersonal access tokenを作成する


  1. GitHubのアカウント設定のPersonal access tokensの画面を開く


    • Settings > Developer settings > Personal access tokens



  2. 「Generate new token」からtokenを作成する


    • scopeはrepoのみフル権限あれば十分です



  3. 生成されたtokenを控えておく


    • わからなくなった場合はtokenを作成しなおす必要があります




3. Bitriseで作成したAccess tokenを設定する


  1. 作成したアプリケーションをDashbordから選択し、Workflowのタブを選択する

  2. 「Secrets」のタブを選択する


    • 「Secrets」に登録する情報は、BitriseのBuildコンソール上からも表示されません。

    • Access Tokenなど隠しておきたいものはここに登録すると良いです



  3. 「Add New」ボタンから追加する


    • Key : DANGER_GITHUB_API_TOKEN

    • Value : 控えたおいたGitHubのAccess token

    • 「Expose for Pull Request」はONにする


      • 結構これを忘れがちです

      • これをONにしないとPull Requestからこの環境変数が参照できず、Dangerが失敗します
        Expose_for_Pull_Request.png





  4. Saveボタンを押下する


4. BitriseでDangerを動かすWorkflowを追加する

まずはDanger実行用のWorkflowを作成します


  1. 作成したアプリケーションをDashbordから選択し、Workflowのタブを選択する

  2. 「+ Workflow」からDanger用のWorkFlowを作成する


    • 名前は任意で大丈夫です

    • BASED ON を「primary」にします



  3. Dangerを実行するStepを追加する

BitriseでDangerを動かすには以下の2種類の方法があります。(他にもあるかもしれませんが)

どちらかのStepを「Android Unit Test」Stepの後に追加しましょう。


Run DangerStepを使う

BitriseではDanger用のStepがあります。

こちらで実行する場合はBundlerでの管理が必要になります。

また、設定項目としては以下の通り、GitHubのアクセストークン設定をすればOKです


  1. GitHubの項目をクリックして設定項目を開き、「Access token for your project」の項目をクリックするとさらに詳細が表示されます

  2. 「Select secret variable」を選択して、登録してあるGitHubのAccess tokenを選択する


ScriptStepを使う

こちらは普通に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. トリガーを変更する


  1. 作成したアプリケーションをDashbordから選択し、Workflowのタブを選択する

  2. 「Triggers」のタブを選択する

  3. 「PULL REQUEST」で実行するWorkflowをDangerを設定したWorkflowに変更する

  4. 右上の「⌘+S」ボタンを押す

ついでに、「PUSH」で実行されるbranchもmasterのみに変更しておいても良いと思います。

(プルリク作るときにPUSHでもHookされてビルドが走るので)

これでDangerの設定は完了です。


実行結果

LintのチェックでエラーがあるとDangerがこんな感じでコメントしてくれます。


Jacoco+Codecovでテストのカバレッジ可視化

次にJacocoを導入してユニットテストのカバレッジを可視化していきます。


Jacoco導入

Jacoco導入の設定例です


build.gradle

@@ -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"
}
}


app/build.gradle

@@ -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/配下になっていると思います。

code_coverage.png

code_coverage2.png


Codecovの設定


Codecovの登録

https://codecov.io/gh

「Add new repository」からリポジトリを登録します

Repository Upload Tokenが発行されますので、それをBitriseで設定します。


Bitriseの設定

「Android Unit Test」Stepの後に以下のStepを追加します


  1. jacocoのレポート作成Stepを追加する


    • 「Script」のStepを選択し、./gradlew :app:jacocoTestReportを実行する



  2. その後に「Codecov」のStepを追加する


    • Codecovで発行したtokenをInput variables > Codecov.io tokenに登録します

    • Keyは任意で大丈夫ですが、今回はCODECOV_TOKENに設定します



これをprimaryとDangerを設定したWorkflowに追加すれば完了です。

サンプルのリポジトリではわざとカバレッジが足りないようにしてありますので、一度primaryのWorkflowでビルドします。

完了後、Codecovに現在のカバレッジ等が表示されるようになります。

この状態からブランチを作成して、カバレッジが増えるような修正をしてみましょう。

そのプルリクがこちらです。

マージするbaseブランチでCodecovが実行済みだと、compareブランチとの差分が表示されます。

もちろんテストのカバレッジはCodecovからでも詳細にみれます。

以上でJacocoおよびCodecovの設定も完了です!


ビルド完了はSlackに通知

これは以下のブログで紹介されていました

手順は以下の通りです


  1. Slackの「アプリを追加する」から「Incoming Webhook」を選ぶ

  2. Slackの設定を整える


    • 通知先のチャンネルとか、アイコンとか



  3. 設定画面の「WebHook URL」をコピーする

  4. Bitriseで「Send a Slack message」のStepをWorkflowの最後に追加する

  5. 「Slack WebHook URL」に先ほどコピーしたURLを設定する


    • 環境変数は任意で大丈夫です



  6. 「Target Slack channel, group or username」で通知するチャンネルを設定する

  7. ビルドが失敗した時の通知は「If Build Failed」の項目を開いて「Target Slack channel, group or username」を設定する

デフォルト設定だと結果はこんな形で表示されます

slack.png

以上でSlackの通知も完了です。


あとがき

いかがでしたでしょうか?

BitriseはGUIで設定ができるので、簡単にWorkflowが作れるのはメリットですね。

初期構築で少しつまづきやすいポイントも紹介してみましたので、初めて触る方のお役に立てば幸いです。

また、CI環境の構築に慣れている方は、「こんなStepもあったら楽なのに」って思うようなものがあれば、コントリビュートしてみるのも面白いかもしれませんね。