🤖

Android で全バリアントのコンパイルチェックを手短にする

2024/06/23に公開

Android アプリの開発で、リリース間近にリリース向けのビルドや普段つかっていないバリアント(フレーバーとビルドタイプの組み合わせ)でビルドしようとすると、ビルドできなくてあたふたするようなことがよくある。

それを防止する為にこれまで、プルリクやマージキュー、マージ時(develop ブランチや main ブランチ等への push 時)の CI で次のように assmble タスクを走らせていた。

$ ./gradlew app:assemble

ちなみに補足すると、assemble タスクは assembleDebugassembleStagingassembleStagingDebug のようにしてバリアントで絞ることもできるが、目的は全バリアント網羅なので絞っていない。また $ ./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

でよいと思う。

脚注
  1. java モジュールを含む場合は $ ./gradlew app:test debugUnitTest java-mod-a:test java-mod-b:test --continue のように java モジュールを羅列する必要がある。 ↩︎

Discussion