- It builds a Docker container from Google Cloud Source Repositories or GitHub with Google Cloud Build.
- It publishes the image to the Container Registry as
eu.gcr.io/$PROJECT_ID/cloudbuild-android
. - It has OpenJDK, Android
sdkmanager
, Gradle, as well as a simple Android application for testing. - It supports publishing to Bucket & Firebase App Distribution with Cloud KMS decryption for credentials.
- Android NDK and Firebase Crashlytics NDK crash reporting can be enabled by uncommenting a few lines.
- This image first needs to be built itself, in order to build Android applications with it.
- Hosting the built image would be a) less customizable and b) the traffic would be charged.
- In order to get started, import to Cloud Source Repositories and set up a build trigger there.
- After having successfully built it, a new container should show up below
eu.gcr.io/$PROJECT_ID/cloudbuild-android
. - This container can then be used in another Android project's (or another Git branch's)
cloudbuild.yaml
, in order not to build it every time.
An important difference is, that:
- when the
Dockerfile
runs./gradlew build
, the components and dependencies in thebuild.gradle
get pre-installed. - when the
Dockerfile
runs./gradlew
, only the Gradle wrapper gets pre-installed (this is the current situation).
One can pre-install SDK packages with the sdkmanager
, when passing _ANDROID_SDK_PACKAGES
.
And one can change the version of the Gradle wrapper, when passing _GRADLE_WRAPPER_VERSION
.
At the moment these are both statically set in cloudbuild.yaml
, but the code is there.
_ANDROID_SDK_PACKAGES
~platform-tools platforms;android-33 build-tools;33.0.2
_GRADLE_WRAPPER_VERSION
~7.4.2
These examples assume that you already have the image in your project's private container registry.
Hostname eu.gcr.io
(also bucket name eu.artifacts
) can be replaced with us.gcr.io
or gcr.io
.
a) This uploads debug APK files with gsutil
to gs://eu.artifacts.$PROJECT_ID.appspot.com/android/
:
# cloudbuild.yaml
steps:
- name: eu.gcr.io/$PROJECT_ID/cloudbuild-android
id: 'docker-pull'
args: ['cp', '-a', '.', '/persistent_volume']
volumes:
- name: data
path: /persistent_volume
- name: gcr.io/cloud-builders/docker
id: 'gradle-build'
volumes:
- name: data
path: /persistent_volume
args: ['run', '-v', 'data:/workspace', '--rm', 'eu.gcr.io/$PROJECT_ID/cloudbuild', '/bin/sh', '-c', 'cd /workspace && ./gradlew mobile:assembleDebug && mv mobile/build/outputs/apk/debug/mobile-debug.apk mobile/build/outputs/apk/debug/$REPO_NAME-$SHORT_SHA-debug.apk && ls -la mobile/build/outputs/apk/debug/$REPO_NAME-$SHORT_SHA-debug.apk']
- name: gcr.io/cloud-builders/gsutil
id: 'publish-gsutil'
args: ['cp', '/persistent_volume/mobile/build/outputs/apk/debug/$REPO_NAME-$SHORT_SHA-debug.apk', 'gs://eu.artifacts.$PROJECT_ID.appspot.com/android/']
volumes:
- name: data
path: /persistent_volume
timeout: 1200s
b) Cloud KMS can be used decrypt files; this requires IAM roles/cloudkms.cryptoKeyEncrypterDecrypter
for the service account:
The first step mounts volume data
. The second step runs gcloud kms decrypt
(there are scripts in the /scripts
directory, for encrypting the *.enc
files). The Gradle task in the third step runs mobile:assembleRelease mobile:appDistributionUploadRelease
, which uploads a signed release APK to Firebase App Distribution. This requires a separate service account with a google-service-account.json
, because it is not possible to access the Cloud Build service account credentials.
# cloudbuild.yaml
steps:
- name: eu.gcr.io/$PROJECT_ID/cloudbuild-android
id: 'docker-pull'
args: ['cp', '-a', '.', '/persistent_volume']
volumes:
- name: data
path: /persistent_volume
- name: gcr.io/cloud-builders/gcloud
id: 'kms-decode'
entrypoint: 'bash'
waitFor: ['docker-pull']
args:
- '-c'
- |
mkdir -p /persistent_volume/.android
gcloud kms decrypt --ciphertext-file=credentials/keystore.properties.enc --plaintext-file=/persistent_volume/keystore.properties --location=global --keyring=android-gradle --key=default
gcloud kms decrypt --ciphertext-file=credentials/google-service-account.json.enc --plaintext-file=/persistent_volume/credentials/google-service-account.json --location=global --keyring=android-gradle --key=default
gcloud kms decrypt --ciphertext-file=credentials/google-services.json.enc --plaintext-file=/persistent_volume/mobile/google-services.json --location=global --keyring=android-gradle --key=default
gcloud kms decrypt --ciphertext-file=credentials/debug.keystore.enc --plaintext-file=/persistent_volume/.android/debug.keystore --location=global --keyring=android-gradle --key=default
gcloud kms decrypt --ciphertext-file=credentials/release.keystore.enc --plaintext-file=/persistent_volume/.android/release.keystore --location=global --keyring=android-gradle --key=default
rm -v ./credentials/*.enc
volumes:
- name: data
path: /persistent_volume
- name: gcr.io/cloud-builders/docker
id: 'firebase-distribution'
waitFor: ['kms-decode']
env:
- 'BUILD_NUMBER=$BUILD_ID'
volumes:
- name: data
path: /persistent_volume
args: [
'run',
'--rm', 'eu.gcr.io/$PROJECT_ID/cloudbuild',
'-v', 'data:/workspace',
'/bin/sh', '-c', 'cd /workspace && ./gradlew mobile:assembleRelease mobile:appDistributionUploadRelease'
]
timeout: 1200s
- Creating a Serverless Mobile Delivery Pipeline
- Simplify your CI processes with GitHub and Google Cloud Build
- Marketplace Google Cloud Build for GitHub integration.
- Google Cloud Build (official).