前回の続きです。
今回の目標
予告はFirebaseをつかった認証とDatabaseとしていましたが、ちょっと方針転換で、CI(継続的インテグレーション)をやります。
テストが割と長くなってきたので、サーバーにお任せしたいな、と。
それと、apkファイルへの署名についてもやっていきます。
CIとは
CIとは、継続的インテグレーションです。Continuous Ingegrationの略です。
ざっくばらんに言うと、ビルドとテストを自動化してどっかクラウドとか別サーバーに繰り返しやらせましょうねってことです。
やらせる契機は、ブランチへのpushだったり、PR(プルリク)だったり、masterへのマージだったり、いろいろ指定できるものですが、今回は、masterを除くブランチへのpushで毎回ビルドとデバッグテストを行うというのをやってみようと思います。
1. CIツール
いくつかのCIツールについて紹介します。
今回やるのは、
- Github Actions
ですが、他にも、
- Jenkins
- CircleCI
- TravisCI
- bitrise
- Azure Pipelines
- GitLab CI/CD
- Bitbucket Pipelines
などがあります。
クラウドで出来るものと、オンプレミス(自分でサーバーやローカルマシンに環境構築が必要)なものとありますが、いずれも、オープンソースならば利用が無料なところが多いです。ただ、単にソースコードを公開しているだけではだめな場合もあるので、よく規約や料金プランを確認して下さいね。
(2) CIツールでやること
基本的には、CIは以下のことを行うことが目的です。
- ビルド
- 生成物(Androidの場合、apkファイル)の保存
- テスト(Unit Test/自動化されたUI Test)
- 結果レポートの保存と閲覧
ビルドは、Androidの場合、デバッグビルドだけやるのを紹介している記事がほとんどです。
これには理由があります。
それは、リリースビルドのapkには「署名」が必要だからです。(実際にはデバッグビルドでも必要ですが、それについては後述)
で、署名に必要な情報をそのままリポジトリにアップロードしてしまうと、パスワードを平文で公開している状態となって非常に危険なので、通常、署名関連の情報はリポジトリには登録しないという運用を取ります。
そうすると、CIツールの方で、今度は、署名に必要な情報を参照することが困難になります。(CIツールは基本的にリポジトリをインターネット経由で参照します。リポジトリに上がっていないファイルは、どうやったって使えないですよね)
ということで、CIという範疇では、デバッグ用apkが出来ればいいだろうということで、そこまでしかやっていない紹介記事が多いのです。
リリース署名を行う、というのは、CIの先のステップとも言えるので、確かに十分ではあるんですよね。
ただ、今回せっかくCIツールについて調べるので、「リリースapkの署名まで自動化したいなあ」という思いがあり、一緒に調べることにしました。
ということで、CIツールのお試しに入る前に、apkへの署名についてもここでやってしまいましょう。
アプリ署名について
Androidアプリは、最終的に以下のどちらかの形式でアプリファイルを配布します。
- apk (すべての端末向けの1ファイル)
- abb (Android App Bundle/端末仕様毎に必要なリソースを分けて配布)
どちらにしても、証明書で署名する必要があります。
また、最近は、Googleがアプリに署名してくれるGoogle Play アプリ署名というのが推奨になっていて、これは証明書をGoogleが保管してくれるというものなのですが、とはいえ、アップロード鍵としてやはり証明書を作って署名する、という手順は結局変わらず必要です。
違うのは、アップロード鍵はGoogleさんに「失くした!」と泣きつけば、再発行できるというメリットがあることですね。
以前の、「開発者が証明書を管理して署名する」だと、証明書が行方不明になると万事休すでした。二度と同じアプリをアップデート配信できなかったんですね。
そのリスクがなくなるという意味では、このGoogle App Signingというのを今後は使って行くべきでしょうね。
アップロード鍵での署名にせよ、アプリ署名にせよ、やることは同じです。
コマンドラインから作ることも出来ますが、ここではAndroid Studioでまず署名できることを確認しましょう。ただ、バージョン3.5から妙なエラーが出るんだけど、今のところ無視しても大丈夫なようです。
(1) debugビルドへの署名
実は、debugビルドのアプリも、署名されてるんです。AndroidSDKが自動的にデバッグ証明書で署名しています。
デバッグ証明書(keystore)はデフォルトでは、下記の場所にあります。
-
Mac/Linux
~/.android/debug.keystore
-
Windows
C:¥Users¥<user>¥.android¥debug.keystore
デバッグ用証明書は、開発機毎に異なります。証明書が異なると、上書きインストールできません。従って、チームなどで開発用のスマホ実機を使い回していたりすると、他の人がデバッグ実行した後のスマホで、別の人がデバッグ実行しようとすると、アンインストールが一度必要になります。
また、今回、CIツールでapkをビルドし、それをダウンロードして使うことを考えると、クラウド型のツールを使うと、毎回デバッグ用署名が違うことになってしまいます。
その度に、いちいち一度アンインストールしてから再インストールしなければならなくなり、ちょっと面倒そうです。
なので、デバッグ証明書も作って、自前で署名するステップを入れておきます。
1.debugビルドのパッケージ名を変える
さて、デバッグ証明書と、リリース用の証明書は変えなければなりません。となると、それぞれのapkの署名が一致しなくなってしまいます。
で、何が困るかというと、debugビルドのアプリがインストール済の端末に、releaseビルドのアプリを上書きインストール出来ないということになります。
(署名が一致しないと同じアプリと見なされない)
回避する方法は、
- debugビルドとreleaseビルドで同じ署名を使う
- =上書きインストール出来るようにする
- パッケージ名を変えて上書きインストールにならないようにする
- =別のアプリとして認識させる
のどちらかになります。
で、個人的には、両方同時インストールできる後者のパターンの方が好きです。
ただし、アプリ内課金などがある場合には使えない手段なので、用心が必要ですが。
今回は、アプリ内課金は今のところ予定していないので、別パッケージにして、同時にインストールしておけるようにします。
テストを流すとデータ消えちゃうし、releaseビルドのアプリを本格的に使ってるとそれだと問題ですから^^;
app/build.gradle
に以下の記述を追加します。
android{
...
buildTypes {
debug{
applicationIdSuffix ".debug"
minifyEnabled false
}
....
}
....
}
これで、debugビルドの時のパッケージ名には、suffixとして".debug"が付くようになります。
つまり、jp.les.kasa.sample.mykotlinapp
がデフォルトのパッケージ名なので、デバッグ時にはjp.les.kasa.sample.mykotlinapp.debug
となります。
2.アプリ名をビルドタイプ別に変更する
それと、両方同時インストール出来ると、今度は、「どっちがどっちのアプリか分からなくなる」問題が発生します。
分かりやすくするため、私はランチャーが表示するアプリ名を変更することをよくやります。
buildTypes {
debug{
resValue "string", "app_name", "(d)歩数計記録アプリ"
...
}
release{
resValue "string", "app_name", "歩数計記録アプリ"
...
}
これは、文字列リソースをbuild.gradle
で定義する方法です。
<string name="app_name">PedometerSample</string>
こんな感じでapp_name
という文字列リソースを定義していたと思いますが、これをbuild.gradle
で定義できるんですね。便利です。
以前(大昔)は、debugビルドとreleaseビルドでリソースを分ける場合、app/src/debug/res/values/strings.xml
とapp/src/main(or release)/res/values/strings.xml
とでリソースファイルを別に用意する必要がありましたが、gradleに移行したことでこういうことが出来るようになりました。リソースファイルをビルドタイプ別に用意するのは、同時に比較するのが難しく、ファイル数が増えて管理が煩雑になる、という問題点がありました。
gradleファイルに書いてあれば、debugビルドとreleaseビルドでの値の違いが、とても分かりやすく、管理も楽になりました。
ということで、リソースファイルのapp_name
は削除して大丈夫です。
Sync Nowしておきましょう。
3.デバッグ証明書を作成する
AndroidStudioから作成します。
-
続くダイアログは、いったんデフォルトのままで[Next]をクリック
(証明書作成までしかしないので、今は無関係) -
[Create New]をクリック
-
フォルダアイコンをクリック
-
証明書を置くフォルダ(
app/
下)を選び、ファイル名は debug.jksとする(ファイル名は任意。デバッグ用だと分かりやすくしておく) -
[Save]をクリック
-
以下の内容でセットする
設定項目 | 設定内容 |
---|---|
鍵ストア・パスワード | android |
キー・エイリアス | androiddebugkey |
鍵パスワード | android |
First and Last Name | Android Debug |
Organizatoin | Android |
Country Code | US |
以前は、AndroidStudioが署名するのと同じパスワード、エイリアスで無いとデバッグ実行できませんでしたが、最近はその必要は無く、何でも良くなっています。
なので上記の通りに設定する必要は、本来はないです。
ただ、このデバッグ証明書のパスワードなどの情報はソース管理のリポジトリに上げてしまいますので、全世界に公開されます。デバッグ証明書で署名したAPKはPlayストアに登録できないのですが、パスワードやエイリアス情報を見ていると思われます。なので、ここは公開されているデバッグ鍵と同じ情報にしておくのが無難でしょう。
証明書が指定したフォルダに作成されます。
証明書が出来たら、今はここまでで良いので、[Cancel]をクリックして閉じます。
4.デバッグ署名を設定する
-
メニューの[File] - [Project Structure...]を選ぶ
-
左側のパネルで[Modules] を選び、右側のタブで [Signing Configs]を選ぶ
-
先ほど作成した
debug.jks
のパスを選ぶ -
パスワード、エイリアス情報を入力して [Apply]をクリック
-
Syncが終わるのを待って、左側のパネルで[Build Variants]を選ぶ
-
右側の[Build Types]タブで debugを選び、下にスクロールして、Signing Configの右のドロップダウンから、$signingConfigs.debugを選ぶ
-
OKをクリック
Gradle Syncが終わったら、app/build.gradle
を開いてみましょう。
こんな項目が追記されているはずです。
signingConfigs {
debug {
storeFile file('debug.jks')
storePassword 'android'
keyAlias = 'androiddebugkey'
keyPassword 'android'
}
}
storeFile
のパスは、絶対パスだとチームの他の人やCIツールで一致しなくなって困るので、app/
からの相対パスに変更しておきます。
また、buildTypes
のdebugビルド内にも、以下のような設定が追記されています。
buildTypes {
debug{
...
signingConfig signingConfigs.debug
}
これでデバッグ実行してみて下さい。今までと署名が変わったので、上書きインストールが出来ないはずです。ここは素直にアンインストールしてからインストールしましょう。
インストールが出来てアプリが起動すればデバッグ署名はOKです。
(2) releaseビルドに署名する
1. 証明書を作ってビルドする
今度は証明書を作ってビルドまで、全部実行してみます。
-
AndroidStudioのメニューから、[Build]-[Generate Signed Bundle/APK...]を選択
-
[APK]を選ぶ
※最終的には App Bundleにするかどうかは任意です。
-
[Create New]を選ぶ
-
app/
下に任意のファイル名(例:release.jks
)で作成する -
任意の情報で設定する
ストアパスワード、キーエイリアス、エイリアスパスワード、国名(JP等)が必須項目です。
それ以外はなくてもいいですが、後で証明書を差し替えるのは大変なので、個人であっても氏名と地域情報は入れておいた方が良いでしょうね。
-
[OK]をクリック
-
以下のエラーが表示されるが、今は無視して大丈夫
-
(任意)[Remember passwords]にチェックを入れて、[Next]をクリック
※ [Remember passwords]にチェックを入れても、パスワードは覚えておきましょう
-
[Build Variants]の
release
を選び、V2 (Full APK Signature)
にチェックを入れる※V1は、古い開発ツール時代から署名していたアプリ用のオプションです。
-
[Finish]をクリック
ビルドが始まります。しばらく待ちましょう。
成功しましたか?
APKは、app/release
下に出力されています。
2. 署名情報の設定ファイルを作成する
さて、releaseビルド用の証明書のパスワードやらをbuild.gradle
に書いてしまうわけにはいきません。他の人がアプリを改ざんしほうだいになってしまいます!
色々やり方はあると思いますが、Googleさんも公式に推奨している、Gradleのプロパティファイルにいったん逃がす方法を応用して使おうと思います。
- プロジェクトルートのディレクトリに
keystore.properties
というファイルを作り、各情報を設定する
storePassword=ストアパスワード
keyPassword=キーパスワード
keyAlias=キーエイリアス
storeFile=release.jks
ストアパスワード、キーパスワード、エイリアスは、先ほどリリース用証明書を作ったときに使ったものを入力して下さい。
(まさかもうパスワード忘れたとかないよね〜)
-
app/build.gradle
に以下を追記する
apply plugin: xxx
def keystorePropertiesFile = rootProject.file("keystore.properties")
android {
...
signingConfigs {
debug {
...
}
release {
if (keystorePropertiesFile.exists()) {
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
}
keystore.properties
ファイルが無かったときにビルドが失敗しないようにしてあります。
-
app/build.gradle
のbuildTypes{}
に以下を追記する
buildTypes {
debug {
...
}
release {
...
if (keystorePropertiesFile.exists()) {
signingConfig signingConfigs.release
}
}
}
こちらも、keystore.properties
ファイルが無かったときにビルドが失敗しないようにしてあります。
※keystore.properties
ファイルが無かったときは、未署名のapkが出力されます。
これでビルドが通るかどうかは、ちょっと後回し。
3. 無視ファイルの設定変更
まず、output.json
がソースコード管理に入ってしまうと面倒なので、気になる人はプロジェクトルートの無視ファイル(.gitignore
)に追加しておきましょう。
※Macの人は、Finderで**command + shift + .**とすると隠しファイルが表示されます。
output.json
それから、デバッグ用証明書はコード管理に追跡させたいですが、リリース用証明書と、そのパスワード情報などが書かれたkeystore.properties
はGithubで公開されては困ります。
(Privateリポジトリなら上げてしまってもいいかも知れませんが、万が一の漏洩リスクを考えると、上げない方が良いように思います。)
ということで、これらについても.gitignore
ファイルに追記します。
release.jks
keystore.properties
ファイルを保存して、git status
などで見てみて下さい。
無視する設定にしたファイルが表示されていないことをしっかり確認してから、コミットしましょう。
コマンドラインからの実行
コマンドラインからビルド、テストが出来るのを確認します。なぜなら、CIツールではこれらを使うからです。先ほどのリリース証明書での署名が成功するかのテストにもなります。
(1) ビルド
-
debugビルド
$ ./gradlew assembleDebug
- 出力は、
app/build/outputs/apk/debug
- 出力は、
-
releaseビルド
$ ./gradlew assembleRelease
- 出力は、
app/build/outputs/apk/release
- AndroidStudioから[Generate Signed Bundle/APK...]でやったときに出力されたフォルダとは違うので要注意
- 出力は、
-
両方ビルド
$ ./gradlew assemble
(2) Lintチェック
言語的、あるいはAndroidのお作法的推奨事項を持っているか、無駄な変数、リソースが無いかのチェック等をしてくれます。
-
debugビルドのLintチェック
$ ./gradlew lintDebug
-
releaseビルドのLintチェック
$ ./gradlew lintRelease
-
両方Lintチェック
$ ./gradlew lint
ログに出るので分かると思いますが、レポート結果の出力先は app/build/reports/
です。
結構いろいろ出てますね(汗)
対応するかどうかはお任せします。
(3) UnitTest
JUnitテスト、Robolectricテストが実行されるテストです。
- debugビルドのUnitTest
$ ./gradlew testDebugUnitTest
- 結果は、
app/build/reports/tests/
に出力
- 結果は、
./gradlew test
でもUnitTestが実行できるのですが、これはreleaseビルドのUnitTestまで実行してしまいます。今回、releaseビルドは事情があって実行できないので(動かしてみると理由が分かると思います)、debugビルドに限定するコマンドで実行します。
注意点としては、一度テストをした後は、コードが変わっていないと、clean
するまでテストが実行されません。
クラウド型のCIならば、恐らく毎回clean状態なので大丈夫だとは思いますが、Jenkinsなどで行う場合は、cleanさせる設定を入れておく必要があるでしょうね。
なお、./gradlew check
で、Lintチェック + testが実行出来ます。
また、./gradlew build
で、ビルド + Lintチェック + testが実行出来ます。
リリースビルドでUnitTestが行われてしまっても大丈夫なときは、build
コマンド1つが楽かも知れません。
(4) Instumentationテスト
今回はエミュレーターで実行してみて下さい。ほとんどのCIでは必然的にエミュレーターを使うことになりますので。
API Level 28 のエミュレーターを作り、起動しておきます。
(※今現在、compileSdkVersion
と targetSdkVersion
を28
にしているから。29
に上げないと2020/8/3以降はPlayストアにアプリを登録することが出来ないので、要注意。なおアップデートの場合は、11/2以降不可になります)
初期設定が終わるのを待ちましょう。
その後、以下のコマンドでInstumentationテストが実行されます。
$ ./gradlew connectedCheck
このコマンドは、debugビルドだけみたいです。
結果は、app/build/reports/androidTests/connected/
に出力されます。
全部通過していると気持ちいいですね。
各タスクの実行コマンド、結果の保存場所や見方が分かったところで、いよいよ、CIツールの出番です。
Github Actions
GithubがいよいよCIに乗り出してリリースされた機能です。
中身はAzureで動いているようですね。そういや、Microsoftに買収されたんだっけか・・・
パブリックリポジトリはGithub Actionsの利用は無料、と言っているので、自由に使わせて貰うことにします。
プライベートリポジトリの場合は、いろいろと制約、制限、課金があるようですので、ご注意下さい。
(1) ActionsからWorkflowを作る
早速、workflowを作ってみましょう。
Githubの該当のリポジトリページを表示し、masterブランチになっているのを確認し、上のタブ(ちょっと分かりづらいですが)の**[Actions]**を選びます。
※後で無視するブランチなどを追加していきますが、その際、いったんmaster
ブランチに入っていないとだめみたいなので、まずはmasterに追加する必要がありました。
1. デバッグビルド
まずはデバッグビルドが出来るWorkflowを作成します。
-
[New workflow]をクリック
-
下にスクロールして、**[Workflows for Python, Maven, Docker and more...]**をクリック
-
Android CIをクリック
こんなファイルが表示されるはずです。
name: Android CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: ./gradlew build
これは、[push]がされたら、ubuntu
上で、チェックアウトしてきて、JDK1.8の設定をして、gradlew build
コマンドを実行する、という設定になっています。
relaseビルドでのUnitTestが実行されると困るので、まずはdebugビルドだけするように、run:
のコマンドの部分を、./gradlew assembleDebug
に変更します。
- name: Build with Gradle
run: ./gradlew assembleDebug
ページ右上にある、[Start commit]をクリックしてみて下さい。
コミットメッセージは任意で、[Commit directly to the master
branch]を選択して、[Commit new file]をクリックします。
早速Workflowが流れるはずですが、私の場合、master
ブランチは空っぽなので、ビルドエラーになります。
すでにmaster
ブランチにコードがある方は、デバッグビルドが流れて成功するはずですね。
2. masterブランチをローカルにpullする
masterブランチをローカルにpullしてきます。
(どうでも良いですが私はGitのGUIツールでは、Source Treeが好きです)
これからメインにpushしていきたい開発ブランチ(私の場合、feature/qiita_10
)に、マージしてpushします。
すると、pushしたブランチで、Workflowが走るはずです。
今度は私の環境でもビルドが成功しました。
(2) 無視するブランチを設定する
とりあえず、私のように、master
が空だったりするときや、実験用などで毎回Workflowが動くと困るようなブランチがある時のために、特定ブランチへのpushは無視するという設定が出来ます。
再び、master
ブランチ上の.github/workflows/android.yml
を編集します。
push
の階層下に、branches-ignore:
で追加できます。
name: Android CI
on:
push:
branches-ignore:
- 'master'
- 'feature/qiita_10_sub/**'
私の場合、今後、他のCIツールをお試しするときのブランチをfeature/qiita_10_sub/xxx
みたいに作っていく予定なので、それも設定しました。このように、正規表現が使えます。
これとは逆に、特定のブランチのみビルドしたい場合(devブランチのみで回すなど)は、branches:
で指定できます。
先ほどと同じく、master
ブランチにそのままコミットし、ローカルにpullしてきて開発ブランチにマージします。
このままpushしてもいいですが、せっかくなので次の設定も追加してからにしましょう。
(3) APKをアーカイブする
ビルドしたAPKファイルを、アーティファクトとして保存してダウンロード出来るようにしましょう。
ここからは、開発用ブランチで行って大丈夫です。
- name: Archive Debug Apk
if: success()
uses: actions/upload-artifact@v1
with:
name: debugApk
path: app/build/outputs/apk/debug/app-debug.apk
インデントが重要なので、気をつけて下さいね。
if: success()
は、前のタスクが成功したときのみ実行するという条件です。
actions/upload-artifact@v1
というアクションを使うと、成果物(artifact)をアップロードできます。
ここでは、debugApk
という名前で、path:
で指定したファイルをアップロードしています。
ymlファイルを保存して、コミットし、pushしてみましょう。
ビルドが成功して、Workflowのページを見ると、Artifactsの所に、apkファイルへのリンクがあるはずです。クリックするとDL出来るかと思います。DLしてくるのはZipファイルになっているので、解凍すると、中にapp-debug.apk
が入っています。
上書きインストールをしてみて下さい。
デバッグ用証明書が共通化されたので、他のマシンでビルドしたapkでも上書きインストールが出来るはずです。
(4) テストの設定を追加する
次はテストが実行されるようにWorkflowを編集します。
1. Checkタスクを追加
Lintと、UnitTest/ReobolectricTestが実行されるようにタスクを追加します。
ymlファイルの一番下に以下を追記します。
- name: Check
if: success()
run: ./gradlew lint testDebugUnitTest
- name: Archive results
if: always()
uses: actions/upload-artifact@v1
with:
name: test-reports
path: app/build/reports
Check
という名前のジョブで、lint
とtestDebugUnitTest
のgradleコマンドを実行しています。
Archive results
という名前のジョブで、Lint、テストの実行結果レポートをアップロードしています。
コミットしてpushしてみましょう。
Workflowのジョブリストに、Check
というのが追加されているでしょうか?
終わると、Artifactのところに、test-reportsというのが出来ているはずです。Zipファイルをダウンロードして、解凍して中身を確認しましょう。
ローカルでtestDebugUnitTest
としたときと同じレポートが見られるはずです。
2. 仮想マシンを変更する
さて、エミュレーターでのInstrumentationテストもやって欲しいですよね。
これが出来ると、フルテストを実行中、スマホが使えなくなってしまうことも無くなります。
テスト中に電話が掛かってきたりpush通知で画面占有されたりして「あああテストが失敗やり直し・・・」なんてことも無くなります。
Github Actionsにはちゃんとそのためのアクションを既に作って公開して下さっている方がいるのですが、1つだけその前に直す必要があるところがあります。
仮想マシンのOSタイプです。
デフォルトだと、**Ubuntu(Linux)**になっていますが、エミュレーターは、HAXMというのを使わないととてもじゃないけど重くて、これはUbuntu向けにはどうやら今は用意されていないようなのです。
なので、ymlファイルのruns-on
にある設定を、MacOS用に変更します。
runs-on: macOS-latest
これだけです。
※本来、WindowsでもエミュレーターはHAXMで動かせるはずですが、参考記事はみんなmacOSを指定してました。理由までは追ってないですが、Github側が用意しているWindows仮想マシンが、HAXMに対応していないのかも知れません。誰か理由をご存じの方や、試してみて動いたor動かなかった等あったら教えて下さい!
3. エミュレーターテストのタスクを追加する
Github Actions android emulator test
等でググると、いくつか情報が見つかりますが、バージョンが正式リリースされていてメンテも継続されていそうな、こちらを使わせて頂くことにします。
Android Emulator Runner
https://github.com/marketplace/actions/android-emulator-runner
ymlファイルに以下を追記します。追加するのは、Check
ジョブとArchive results
ジョブの間です。
- name: Android Emulator Runner
if: always()
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 28
profile: Nexus 6
script: ./gradlew connectedCheck
profile
に大きめの画面のものを設定しないと、どうやら非表示のウィジェットが出来るようでテストが失敗するものがあるので、ここではNexus 6
を指定しました。
ただ、「$ANDROID_HOME/tools/bin/avdmanager list
でとれる端末リストの名前を指定できる」と書いてあるのですが、Pixel
を指定してもそんなAVD無いと言われてしまいました。
なので、ここは無難にサンプルに上がっていたNexus 6
にしています。
他に何が使えるかは、試していません。
ymlファイルをコミットしてpushしましょう。
エミュレーターテストは時間かかるので、その間に他のことをして待ちましょう。
自分のスマホや開発マシン(PC)は空いたので、何でも出来ますね(笑)
(5) 通知を設定する
Workflowが終わったら、どこかに通知して欲しいですよね。
Slackとか、チャットワークとか。
今回は、Slackにしてみます。
1. SlackにWebHookアプリを追加する
Slackに既に利用中のワークスペースが無い場合は、自分用に作っておきましょう。
この手順は割愛します。
SlackのIncoming WebHookアプリのページへ行きます。
https://slack.com/apps/A0F7XDUAZ-incoming-webhooks
その他の設定は任意です。
- [設定を保存する]をクリック
これでSlackの準備は出来ました。
2. GithubにWebhook URLを設定
Githubに先ほどコピーしたWebhook URLをSecretsとして保存します。
Githubの自分のリポジトリページへ行きます。
-
タブ[Settings]を選ぶ(分かりにくい)
-
左側の[Options]メニューの中から、[Secrets]を選ぶ
-
[Add a new secret]をクリック
-
Name
に任意の名前を入力 -
Value
に先ほどコピーしたWebhook URLを貼り付ける -
[Add secret]をクリック
準備はこれで終わりです。
3. Actionsに追加
Slack通知してくれるGithub ActionはGithub Marketplaceに多数上がっていて、いくつか試したのですが、ほとんどがLinux(Ububntu)
上でしか動かせないものでした。
エミュレーターテストのため、macOS
にしていますから、IncomingWebhookを使い、かつmacOSで動かせるものを探して、ようやく見つけたのが、こちらです。
作者様、ありがとうございます!
日本の方のようです。Qiita記事からたどり着きました。最後に参考リンクを貼ってありますので興味ある方はご覧になってください。
ということで、上記のページのサンプルを参考に、ymlの最後に以下を追記しました。
- name: Slack notification
uses: homoluctus/slatify@master
with:
type: ${{ job.status }}
job_name: '*Build and DebugCheck*'
channel: '#general'
url: ${{ secrets.SLACK_WEBHOOK_URL }}
${{ job.status }}
で、ジョブの完了ステータスを教えてくれます。
Slackへの投稿は、見た目はこんな感じになりました。
エラーだとこんな感じ。
良い感じです!
これで、テストが終わったか何度もPCを覗きに来る必要がなくなりました。
※1 Slackへの通知は、単純にcurlコマンド
を書いても出来ないことはないので、「curlコマンド大好き」って方は、それでも良いでしょう(笑)
私はどなたか作ってくれて無いかなーと半分意地になって探しました^^;
※2 ジョブを分けて、そちらのジョブではOSにUbuntu使うようにすることで、Linux(Ububntu)
上でしか動かせないアクションでも動かせます。qiita_10_sub/multi_jobs_os_change
ブランチにslack.yml
というのをサンプルで入れてありますのでご参照下さい。
https://github.com/le-kamba/qiita_pedometer/blob/feature/qiita_10_sub/multi_jobs_os_change/.github/workflows/slack.yml
でも、マシンのビルドや設定が走って遅いので、出来るなら同じOSの1つのマシンで動かしたいですよね。
(6) ステータスバッジをつける
1. ステータスバッジをREADMEに追加する
せっかくなので、README.md
ファイルにステータスバッジをつけようと思います。
ステータスバッジとは、CIツールなどでのビルドやテストが通っていることを示す画像のことです。
大きなGithubプロジェクトのページに行くと、よく貼ってあると思います。
いろんなCIツールがバッジを提供しています。
Github Actionsでは次の手順でつけられます。
- ActionsのページでバッジをつけたいWorkflowを選ぶ
- 右上の[Create status badge]というボタンをクリック
- ブランチを選ぶ(任意)
- [Copy status badge Markdown]をクリック
-
README.md
ファイルを開く- Github上で開いてしまっても可
- 先ほどコピーしたテキストを貼り付けて保存
- 場所は任意だけどだいたい先頭に置かれていることが多い
- コミット
- push(ローカルで編集した場合)
こんな風に表示されます。
なんかちゃんと運用されているプロジェクトっぽくなりました(笑)
2. トリガーを無視するファイルを設定する
さて、README.md
だけを編集してコミット、pushしたときに、Workflowが動いてしまいますね。
ビルドには全く関係ないファイルの更新で、テストまで走ってしまうのはかなりの無駄です。
そこで、トリガーを無視するファイルの設定を行っておきます。
それには、paths-ignore
を使います。
on:
push:
paths-ignore:
- '**.md'
branches-ignore:
- master
- 'feature/qiita_10_sub/**'
ドキュメント(マークダウンファイル(.md))はすべて無視するようにしました。
これで、コミットファイルが一致するものしか無い場合は、Workflowが起動しないはずです。
※確認するには、先にandroid.yml
をコミットしてpushした後、README.md
を編集してコミット&pushしてみてください。
(7) リリースビルドを追加する
さて、リリースビルドも一緒に出来るようにしていこうと思います。
ただ問題は、署名ファイルです。
Githubに上げるわけにはいきません。
先ほどのSecrets
を使って上手くいかないかなあと思っていたのですが、いわゆる「秘密ファイル」の登録は出来ないようで、悩みました。
解決策としては、以下の2つくらいしか無さそうです。
A. Sign Android Release Actionを使ってみる
B. 以下の内容を参考に、証明書を暗号化したファイルをコミットして使う
https://help.github.com/ja/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets
せっかく作って下さっているので、Aを試してみようと思います。
Sign Android Release ActionのMarketページによると、以下の値が必須だと書いてあります。
変数名 | 内容 |
---|---|
releaseDirectory | 署名したapkの出力フォルダへの相対パス |
signingKeyBase64 | 証明書ファイル(.jks ファイル)をbase64エンコードした文字列 |
alias | 証明書のエイリアス |
keyStorePassword | 証明書の鍵ストアパスワード |
keyPassword | 証明書の鍵パスワード |
releaseDirectory
以外は、すべてGithubのSecretsに保存して使います。
signingKeyBase64の用意が必要ですね。でも親切にコマンドを書いてくれているので、そのまま実行させましょう。Macだと多分デフォルトで入っているはず。Windowsのかたはすみません、自分で調べて入れて下さい。
ターミナルなどでrelease.jks
ファイルがあるフォルダに移動し、以下のコマンドを叩きます。
$ openssl base64 < release.jks | tr -d '\n' | tee release.jks.base64.txt
release.jks.base64.txt
が同じフォルダに出力されているので、ファイルを開いて中身をすべてコピーし、Secretsに登録します。
Name : SIGNING_KEY
Value : コピーした文字列を貼り付け
登録したら、release.jks.base64.txt
がコミットされてしまわないよう、ファイルは削除しておきましょう。
後はkeystore.properties
に設定した情報を参考に、Secretsに1つずつ設定すればOK。
Name | Value |
---|---|
ALIAS | 証明書のエイリアス |
KEY_STORE_PASSWORD | 証明書の鍵ストアパスワード |
KEY_PASSWORD | 証明書の鍵パスワード |
android.yml
に以下のように追加します。場所はデバッグ用apkをアーティファクトに上げているタスクの後で良いでしょう。
- name: Sign Release Apk
if: success()
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/build/outputs/apk/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
ファイルをコミット、pushして、Actionを流します。
署名が成功しましたか?
keystore.properties
が完全にローカルビルド用になっちゃいますが、設定としてはすべてSecretsで対応でき、シンプルで良いですね。
Bの方法は、証明書ファイルとkeystore.properties
を暗号化した上でGithubに上げてしまい、Actionsのタスクの中でそれらを復号化して使う、ということになるかと思います。
結構煩雑そうなのでここではやりませんが、せっかく作ったkeystore.properties
を活用できる方法かなとも思うので興味があればやってみても良いかも知れません。
(8) リリースAPKをアーカイブする
さて、署名が出来たら、apkをアーティファクトに上げれば、実行後にActionsのページからダウンロードできるようになります。
先ほどのSign Android Release Actionが、
Outputs
signedReleaseFile
The path to the signed release file from this action
と、署名済みファイル名を出力してくれているので、こんな風にアクセスして使えます。
path: ${{ steps.<id>.outputs.signedReleaseFile }}
<id>
は、タスクにidを付けておいて、そのタスクの出力を使う、という構文です。
なのでまず、署名タスクにidを付けます。
その上で、アーティファクト部分のタスクを書きます。
- name: Sign Release Apk
if: success()
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/build/outputs/apk/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
id: sign-apk # 追加
- name: Archive Release Apk
if: success()
uses: actions/upload-artifact@v1
with:
name: releaseApk
path: ${{ steps.sign-apk.outputs.signedReleaseFile }}
コミット、pushして、Actionを流します。
署名とアーティファクトのアップロードが成功しました。
ただ、ここで出来た署名済みapkをDLしてから、ふと思いました。
publicなリポジトリで、署名済みのリリース用apk上げちゃったら、ダウンロードし放題だよね?
もしかしたら、悪意のある誰かが、そのapkを自分のapkとしてPlayストアにアップロードしてしまうかもしれません。
今は公開しようと思うような出来のアプリではありませんが(汗)、実際にストア公開しようと思っているアプリならば、publicなリポジトリに署名済みapkがあるのはまずい状態ですね。アップロード鍵とする場合には、後から変更する手段はありますが・・・でも同じパッケージ名のアプリはPlayストアに登録できないので、誰かが先に登録してしまうともう登録できないというのもあります。
privateなリポジトリでやるか(従量課金なので気をつける必要あり)、releaseビルドはローカルで行う運用にする、自分でCIサーバーを立ててそこで動かすなどすべきでしょう。
もしくは、/app/build/outputs/apk
全体をパスワード付きzipとかにして別の場所に格納し、それをアーティファクトとして保存する、というのも良さそうですね。
ひとまず、リリースビルドについては、CD(継続的デプロイ)の範疇になってくるので、今回のところは、いったんアーティファクトに上げるところをコメントアウトしておきます。既に上がってしまっている物は逐一削除で^^;
まとめ
GithubActionsでビルド、テストの一連を自動化することが出来ました。生成物(apk)やテスト結果レポートをアーカイブ、ダウンロード出来るようになりました。
個人的には、基本的なことは出来るので、これで十分な気がします。
テスト結果をいちいちDLして解凍してからじゃ無いと見られないのが、他のツールと比べて少し面倒でしょうかね。
ここまでのソースは以下のブランチにアップしてあります。
https://github.com/le-kamba/qiita_pedometer/tree/feature/qiita_10
おまけ
"[ci skip] 〜〜"
というコメントでコミットすると、CIツールがトリガーされないように出来る機能がCircleCI等にあるようで、GithubActionsでもないかなと調べたのですが、ないようです。
で、こちらの方が紹介している、Jobs.if
という構文を使う方法で入れてみてあります。
- GitHub Actions で [ci skip] できるようにしました
https://srz-zumix.blogspot.com/2019/10/github-actions-ci-skip.html
予告
CIツールの第2回目をやります。
- Jenkins
- CircleCI
についても、一応やってみたので、次回、ご紹介します。
参考ページなど
- About billing for GitHub Actions
https://help.github.com/ja/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions - GitHub ActionsでGradleのユニットテストの結果を取得する
https://qiita.com/tkymato/items/6b498b66687b2b0d641d - GitHub Action - Android Emulator Runner
https://github.com/marketplace/actions/android-emulator-runner - GitHub Actionsでエミュレータテストをする
https://satoshun.github.io/2019/12/github-actions-emulator-test/ - 【GitHub Actions】Slack通知用のActionsをTypeScriptで開発してみた
https://qiita.com/homines22/items/0bc6c17e038b35fc8113