GitLab (CI+ CD) Create Android Signed build + Upload build on firebase distribution.

Rushabh Shah
4 min readSep 30, 2020

--

When we heard CI-CD, there is some reluctance in ourselves until we fully know about it!

CI- Continuous Integration, CD- Continuous Delivery.

Step1- .gitlab-ci.yml

Open GitLab and create a file in the root folder. We will write our CI-CD code in this file.

FYI: GitLab CI/CD pipelines are configured using a YAML file called .gitlab-ci.yml within each project.

The .gitlab-ci.yml the file defines the structure and order of the pipelines and determines:

  • What to execute using GitLab Runner.
  • What decisions to make when specific conditions are encountered. For example, when a process succeeds or fails.

Step-2 Caching

Caching the Gradle folder between builds may reduce building time. When we add cache to our builds, each cached job will download and extract the cache at the beginning and upload the cache at the end. Add this in .gitlab-ci.ymlat the top of the file.

before_script:
- export GRADLE_USER_HOME=$(pwd)/.gradle
- chmod +x ./gradlew

cache:
key: ${CI_PROJECT_ID}
paths:
- .gradle/

Step-3 Stages

Defines a job stage for our GitLab CI pipeline. It will define the order of the job. Jobs in the same stage will run in parallel. Add this after caching in the .gitlab-ci.yml file:

stages:
- build
- deploy

Step-4 Stage build

assembleDebug:
image: jangrewe/gitlab-ci-android
stage: build
only:
- master
script:
- echo $KEYSTORE_FILE | base64 -d > my.keystore
- ./gradlew assembleRelease
-Pandroid.injected.signing.store.file=$(pwd)/my.keystore
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD
-Pandroid.injected.signing.key.alias=$KEY_ALIAS
-Pandroid.injected.signing.key.password=$KEY_PASSWORD
- cp app/build/outputs/apk/appname.apk appname.apk

artifacts:
expire_in: 7 days
paths:
- appname.apk

Let me explain!

assembleDebug:

This represents Job that will be done in the build stage. We can provide any name which we want to provide here.

image: jangrewe/gitlab-ci-android

CI Job will run inside a Docker Image. Downloading the image, with everything installed, is a bit faster than running the SDK install process every time.

only:
- master

It specifies that the stage will be applied only when there is a push on a specific branch. Here, I applied to the master branch.

script:
- echo $KEYSTORE_FILE | base64 -d > my.keystore
- ./gradlew assembleRelease
-Pandroid.injected.signing.store.file=$(pwd)/my.keystore
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD
-Pandroid.injected.signing.key.alias=$KEY_ALIAS
-Pandroid.injected.signing.key.password=$KEY_PASSWORD
- cp app/build/outputs/apk/appname.apk appname.apk

This is the stage where we build and sign our application. We want a release build with an app that is signed with a release key store. We will use GitLab CI/CD variables here. We will store our secret keys and passwords inside the variables and they will be accessible only for our job runners. It’s easy to store the Keystore passwords and alias, but what are we going to do with the Keystore file? We will store it as a base64 string.

base64 -w 0 ~/.android/debug.keystore

Now that we have our base64 string, let’s put it inside CI/CD variables. In GitLab project, go to Settings -> CI / CD -> Environment variables (expand). Add your keys and passwords. Make sure that your variable names match the same in the script.

Add Variable
  • KEY_ALIAS
  • KEY_PASSWORD
  • KEYSTORE_PASSWORD

Protected variables will be used only in protected branches.

Now that we have the keys, our release job will decode the base64 back to the Keystore file and will run gradlew assembleRelease with the keys.

cp app/build/outputs/apk/appname.apk appname.apk

^^ This is your generated release APK path. Change it as per your build or product flavor requirements.

artifacts:
expire_in: 7 days
paths:
- appname.apk

The artifacts are passed to the next job (upload) and are also available for download from GitLab UI.

Here your Build Stage is completed.VOILA!! Now let us move to the deploy stage.

A supporter is worth 1000 followers!!

❤❤ Hey!! Enjoying reading my post, please show some LOVE! ❤❤

Firebase Distribution Deployment:

Here we will use,
image: node: latest

deploy_production:
image: node:latest
stage: deploy
only:
- master
script:
- npm install -g firebase-tools
- firebase appdistribution:distribute app.apk --app your_firebase_app_id --release-notes-file release-notes.txt --groups "test-group" --token "$FIREBASE_CI_TOKEN"

We need the firebase app id and firebase CI token here. Firebase app id can be found in the project setting of your firebase console. Firebase CI Token can be generated using the below guide.
https://firebase.google.com/docs/cli#cli-ci-systems

Add this token as a variable in GitLab as FIREBASE_CI_TOKEN.

Once code is merged in the master branch, the release build will generate and will upload directly on firebase app distribution!!

Yeah, it looks like magic!

.gitlab-ci.yml (complete file)

before_script:
- export GRADLE_USER_HOME=$(pwd)/.gradle
- chmod +x ./gradlew

cache:
key: ${CI_PROJECT_ID}
paths:
- .gradle/
stages:
- build
- deploy

assembleDebug:
image: jangrewe/gitlab-ci-android
stage: build
only:
- master
script:
- echo $KEYSTORE_FILE | base64 -d > my.keystore
- ./gradlew assembleRelease
-Pandroid.injected.signing.store.file=$(pwd)/my.keystore
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD
-Pandroid.injected.signing.key.alias=$KEY_ALIAS
-Pandroid.injected.signing.key.password=$KEY_PASSWORD
- cp app/build/outputs/apk/appname.apk appname.apk

artifacts:
expire_in: 7 days
paths:
- appname.apk

deploy_production:
image: node:latest
stage: deploy
only:
- master
script:
- npm install -g firebase-tools
- firebase appdistribution:distribute app.apk --app your_firebase_app_id --release-notes-file release-notes.txt --groups "test-group" --token "$FIREBASE_CI_TOKEN"

Useful Links:

GitLab CI-CD Configuration: https://docs.gitlab.com/ee/ci/yaml/
Firebase App Distribution CLI: https://firebase.google.com/docs/app-distribution/android/distribute-cli
Gradle build flavors: https://stackoverflow.com/questions/21307444/gradle-build-only-a-flavour

If you like SMASH the CLAP 👏 Button and DISAPPEAR it from Medium.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Rushabh Shah
Rushabh Shah

Written by Rushabh Shah

Android & Flutter Developer with zeal to new learning everyday

Responses (1)

Write a response