Android で全バリアントのコンパイルチェックを手短にする
Android アプリの開発で、リリース間近にリリース向けのビルドや普段つかっていないバリアント(フレーバーとビルドタイプの組み合わせ)でビルドしようとすると、ビルドできなくてあたふたするようなことがよくある。
それを防止する為にこれまで、プルリクやマージキュー、マージ時(develop ブランチや main ブランチ等への push 時)の CI で次のように assmble タスクを走らせていた。
$ ./gradlew app:assemble
ちなみに補足すると、assemble タスクは assembleDebug
や assembleStaging
、assembleStagingDebug
のようにしてバリアントで絞ることもできるが、目的は全バリアント網羅なので絞っていない。また $ ./gradlew assemble
のようにモジュール指定無しで実行してもよいのだが、マルチモジュール構成の際に各モジュールで assemble の成果物が生成されるのが無駄な気がするし多少は時間もかかるだろうと思って敢えてモジュールを指定している。
ただ assemble タスクは時間もかかるし、どうにかならないかなと日頃、思っていたのだが、最近、下記でよいのではと気づいた。
$ ./gradlew app:test
assemble タスクより実行時間は短いし、コンパイルできないような問題がある場合はテスト前にエラーとなる。コンパイルのチェックを目的としているので、app モジュールのテストコードは無くてもよい。
CI に組み込む際には、上記のタスクを単独で実行してもよいのだが、app モジュール以外に対する unit test フェーズと同時に実行してしまうのが現実的であると思う[1]。
$ ./gradlew app:test testDebugUnitTest --continue
この例では、どこかのバリアントやモジュールでタスクが失敗しても継続するように --continue
オプションを指定しているが、早々に切り上げて早く失敗をレポートしてしまいたい場合はオプションは無くてもよいと思う。
Q & A
compileXXX 系のタスクでダメなのか
hilt系のタスクが走らないし、バリアントを網羅するようなこともしてくれない。
Android Lint でもいいのでは
全バリアントを網羅することもしてくれない(例えば $ ./gradlew app:lint
としても代表1バリアントしか対象とならない)のと、そもそも Android Lint は重く実行時間がかかる。
app モジュールのテストの実行時間が長いのでは
マルチモジュール構成の場合は app モジュールにテストコードは通常は無いか、あっても少量と思われる。また、シングルモジュールで作っているようなレガシー?なプロジェクトは、そもそもテストコードを書いていなさそうではあるが、恐らく Junit で実行するテストを絞るようなことができたと思う。
assemble でしかチェックできないこともあるのでは
たしかにそうで、Crashlytics 向けの mapping ファイルの作成や google-services.json まわりのタスクは、assemble でしかチェックできない?ような感じはした。
ただ、これらは開発者のコードが引き起こすコンパイルエラーに比べたら頻度は少ないことと、大概はアプリのバイナリの配布のワークフローが別途あるだろうから、そちらで担保されると思う。ただ気になるなら、assemble でのチェックを CI でしてもよいとは思うが、マージキューかマージ時だけに実行する・スケジュールで定期実行でする、必要なバリアントだけに絞る(リリースに関わるバリアントのみにする等)などして、実行時間のコストを最小限にするとよいと思う。
補足
シングルモジュール構成であったりそもそもフレーバーが存在しない場合は debug/release ビルドぐらいしかないであろうから、ややこしいことはせず単純に
$ ./gradlew test --continue
でよいと思う。
-
java モジュールを含む場合は
$ ./gradlew app:test debugUnitTest java-mod-a:test java-mod-b:test --continue
のように java モジュールを羅列する必要がある。 ↩︎
Discussion