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を使わない開発環境の場合は、これで何も問題ありません。
ただGradle
DLのユーザーは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]&複数アプリ開発向け
今後の構想
gradle-wrapper.properties
をコピーして、そこからGradle
バージョン取得する
Docker Build時に取得したバージョンのGradle
をDocker Imageに入れれば、環境差分問題とGradle
バージョンのメンテナンス問題解決できるんじゃないかと思っています。
ただgradle-wrapper.properties
からGradle
バージョン取得するのが、どれだけ可読性に影響するかまだ検証できないので保留です。
これ出来ればオールオッケーな気がしています。
この記事を読んでくださった方向け
正直この構成も完璧だと思っておりません。
改善案あれば、この記事のコメントまたはこちらにご意見頂けると幸いです。
Discussion