Android向けCI回す時、Gradle意識していますか?
TL;DR
- CIでGradleを忘れがちで各JobでGradle DLして時間もったいない
- Dockerfile内で直接Gradle versionやPATHを設定しているのはちょくちょく見る
-
./gradlew --gradle-user-home ./.gradleHomeでGradle展開先を指定するのが複数アプリ開発ではよさげ
よく見るAndroid向けDockerfile
GitLab CIのテンプレートを見ると、Android SDKはインストールしているがGradleについては全く触れていない。
# Packages installation before running script
before_script:
...
- wget --no-verbose --output-document=$ANDROID_HOME/cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS}_latest.zip
- unzip -q -d "$ANDROID_HOME/cmdline-tools" "$ANDROID_HOME/cmdline-tools.zip"
- mv -T "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/tools"
- export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/cmdline-tools/tools/bin
# use yes to accept all licenses
- yes | sdkmanager --licenses > /dev/null || true
- sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}"
- sdkmanager "platform-tools"
- sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}"
...
✅Good Point
- スクリプトがシンプルで読みやすくメンテナンスしやすい
❌Bad Point
- 各JobでGradleがDLされてCI時間がぞうかしてしまう
- どうなるかというと、
Build・Lint・Testなどの各JobでGradleをDLしてしまいます
- どうなるかというと、
Graldeも同梱されているDockerfile
BitriseのAndroid向けDockerfileを見ると、Android SDKに加えてGradleもインストールしている。
インストールするGradleのバージョンについては、環境変数GRADLE_VERSIONで指定している模様。
...
# --- Install Gradle from PPA
# Gradle PPA
ENV GRADLE_VERSION=6.3
ENV PATH=$PATH:"/opt/gradle/gradle-6.3/bin/"
RUN wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip -P /tmp \
&& unzip -d /opt/gradle /tmp/gradle-*.zip \
&& chmod +775 /opt/gradle \
&& gradle --version \
&& rm -rf /tmp/gradle*
...
✅Good Point
-
GraldeもDocker Imageとして配布されるため環境差分が発生しない
❌Bad Point
-
GRADLE_VERSIONのメンテを忘れると、古いGradle使い続けることになる- ちなみに通常
Gradleバージョンは、gradle-wrapper.propertiesで定義されている
- ちなみに通常
結局どうしたのか
よく見るAndroid向けDockerfileとGraldeも同梱されているDockerfileを見て、
「これCIのキャッシュに任せたほうがいいんじゃない」っと思ったので下記のようにして見ました。
debugBuild:
stage: debug
image: $CI_REGISTRY_IMAGE:latest
script:
- ./gradlew --gradle-user-home ./.gradleHome assembleDebug
cache:
key: ${CI_COMMIT_REF_SLUG}
policy: pull-push
paths:
- ./.gradleHome
Workingディレクトリ配下の.gradleHomeに、GradleをDLするように指定して、CIのキャッシュに.gradleHomeを設定しています。
これで後続のJobは.gradleHomeをキャッシュとして再利用します。
Gradleは、ユーザーホームディレクトリにGradleが既に存在する場合はDLを行わず、本来のJobを実行します。
ポイント
ポイントは下記2つ。
-
--gradle-user-homeオプションでGradleユーザーホームディレクトリを指定すること - CIキャッシュに、
--gradle-user-homeオプションのディレクトリを指定すること
--gradle-user-home
--gradle-user-homeオプションとは、Gradleのユーザーホームディレクトリを指定するオプションです。
このオプションを指定しなかった場合、Gradleはユーザディレクトリ配下の.gradleすなわち、~/.gradleにDLされます。
Dockerを使わない開発環境の場合は、これで何も問題ありません。
ただGradleDLのユーザーはroot・Build時のユーザーは一般ユーザーといった構成だと~/.gradleが、一致しないことがあります。
--gradle-user-homeオプション[^4]を使って明示的にGradleユーザーホームディレクトリを指定するのが良いでしょう。
ちなみに後述のdistributionBaseよりこちらの設定のほうが優先されます。
distributionBase
Gradleユーザーホームディレクトリを指定するには、gradle-wrapper.properties内のdistributionBaseで指定する方法もあります。
通常distributionBaseは、GRADLE_USER_HOMEで設定されています。
そうです、このGRADLE_USER_HOMEが、ホームディレクトリ配下の.gradleを指しています。
じゃあ、gradle-wrapper.properties内のdistributionBaseを変更すればいいじゃんと思いました。
一番分かりやすいのは、distributionBaseをリポジトリRootディレクトリ配下の.gradleに変更することです。
ただ複数のAndroidアプリを開発している場合、リポジトリ毎にGradleがDLされます。
各リポジトリで同じバージョンのGradleを使用していたら、ディスク圧迫[1]してしまってう〜んですね。
残る問題点😕
CIのキャッシュを使うことで、2番目以降のJobは、1番目JobのGradleを使うのでDLが発生しません。
ただこれ逆を言えば、1番目のJobは必ずGradleのDLが発生してしまいます。
ただ下記メリットのほうが重要だと思って今はこの構成にしています。
-
DockerfileやGitLab CIスクリプトにGradleバージョンを設定しなくて良い -
gradle-wrapper.propertiesを変更しないので、ローカル開発[2]&複数アプリ開発向け
今後の構想
Docker Build時にgradle-wrapper.propertiesをコピーして、そこからGradleバージョン取得する
取得したバージョンのGradleをDocker Imageに入れれば、環境差分問題とGradleバージョンのメンテナンス問題解決できるんじゃないかと思っています。
ただgradle-wrapper.propertiesからGradleバージョン取得するのが、どれだけ可読性に影響するかまだ検証できないので保留です。
これ出来ればオールオッケーな気がしています。
この記事を読んでくださった方向け
正直この構成も完璧だと思っておりません。
改善案あれば、この記事のコメントまたはこちらにご意見頂けると幸いです。
Discussion