Easy Android CI/CD with Gitlab CI and Firebase

Easy Android CI/CD with Gitlab CI and Firebase

Gitlab is a web-based DevOps lifecycle tool that provides a complete CI/CD workflow. It includes git repository management, issue tracking, code reviews, an integrated CI server, and more. This article will teach you how to use Gitlab to set up a complete CI/CD pipeline for an Android app. The pipeline will build your android application and then upload your application to Firebase App Distribution, a safe and trusted way to share your app with testers outside of the play store.

This tutorial assumes you have an existing android application with its git repository on GitLab. If not, you can set that up and follow along.

Gitlab CI/CD basics

Gitlab CI/CD is an integrated CI/CD environment that can be set up for both internal and external GitLab projects. The instructions for the CI/CD pipeline are contained in a YAML file .gitlab-ci.yml at the root level of your project. The file defines the CI's steps, including any setup/teardown instructions and artifacts to store. Below are some common keywords:

image: this defines what docker image to use for executing the CI steps. This declaration can be for the whole pipeline or individual stages.

stages:  is used to define your pipeline’s stages, for example: build, test and deploy.

stage: is added to a CI step to identify it as part of a particular execution stage.

before_script: defines any instructions run before the CI step is executed.

script:  contains all the commands executed for a specific CI stage.

after_script: includes any teardown instructions to be executed after the main script has run.

artifacts: these are any files you wish to keep after the CI job completes running, for example, files or reports. Artifacts can also specify how long they should be stored.

cache: this keyword specifies any files to cache during the running of the CI job. The files will be persisted between runs and make subsequent jobs fasters as they would need to re-download/ generate the same files.

only: this keyword defines which branches can run a particular ci step. For instance, you may only want to run the deployment step on the main branch.

dependencies: this keyword defines any dependencies to run before running the current step. The current step will only run after the dependent steps are successfully run.

Connect our project to firebase

To start, you must create a firebase project and connect that with your android application. The easiest way to do this is by using the firebase assistant within Android Studio. You can find it by going to the top navigation panel, selecting Tools, and then Firebase. This will show you the different Firebase services. Please,  scroll down until you see App Distribution and select it. It should give you an interface like the one below, where you can set up firebase:

Step 1 will link your android application to a firebase project, and step 2 will add the necessary requirements to your project’s build.gradle file. After step 2, update the dependencies to the latest version, as the firebase assistant sometimes provides outdated versions.

Once done, you can run the following command in the project’s root directory.

./gradlew appDistributionLogin

The output of this is a Firebase authentication token. You will use this to authenticate our CI server with Firebase. Copy the token and add it to your Gitlab project as a CI/CD variable. CI/CD variables allow us to inject variables into our pipelines and are helpful for storing IDs, API keys and tokens, signing keys, as well as other variables that might change. CI/CD variables also help with key security, as the project owner/administrator can add these without sharing them with team members who should not have access. You can also easily set different variables for your different environments, e.g, testing, staging, and production.

To add our variables, open your GitLab project, click on Settings and then CI/CD, scroll down, and you will see a Variables section. It should look like this:

After adding your Firebase Token, also add the name of your app and your firebase Application ID. You can find the application id by going to your Firebase console, opening your project, navigating to settings, scrolling down to your android application, and copying the value. Be sure to add the variables using the variable names specified in the image above, as these names will be referenced in our CI/CD file.

To complete our firebase setup, you need to create a group of testers. Firebase allows us to specify testers by mentioning a predefined group or an email list. For this tutorial, we will use a group. From your firebase console, navigate to the Release and Monitor tab, select App Distribution, and create your group.

Building the YAML file

From you can now start defining your CI/CD pipeline. To do this, create a file in your root project directory, name it .gitlab-ci.yaml, and add the following lines.

Setup

image: jangrewe/gitlab-ci-android
before_script:
- export GRADLE_USER_HOME=$(pwd)/.gradle
- chmod +x ./gradlew
cache:
key: ${CI_PROJECT_ID}
paths:
- .gradle/
stages:
- build
- deploy

The above lines tell Gitlab to use the jangrewe/gitlab-ci-android Docker image for the project. This is a popular docker image that comes with java, android, and Gradle components pre-setup. The lines above also add a before script and tell Gitlab to cache our docker image for future runs. Lastly, it defines our two project stages: build and deploy.

Build Stage

assembleDebug:
stage: build
script:
- ./gradlew assembleDebug
artifacts:
expire_in: 7 days
paths:
- app/build/outputs/apk/debug/app-debug.apk

The build stage of our CI/CD pipeline has one step named assembleDebug. The step’s script tells Gradle to build a debug version of our application. The output of this, an apk file, is stored as an artifact that expires in 7 days. You can also create a production build of the application if need be, but for this tutorial, we will create only a debug version.

Deploy Stage

deployFirebase:
stage: deploy
image: node:latest
dependencies:
- assembleDebug
only:
- main
script:
- npm install -g firebase-tools
- firebase appdistribution:distribute app/build/outputs/apk/debug/app-debug.apk --app $FIREBASE_APP_ID --release-notes-file release-notes.txt --groups "internal-testers"

The final stage of our pipeline uses a node docker image. Using this image, it installs firebase-tools, a node library that provides a CLI for interfaces with firebase. Next, it uses the library to run a distribute command specifying the apk file to distribute, the release notes and the group of testers to distribute to.  Make sure to add a file titled release-notes.txt with any release notes for testers. Also, replace “internal-testers” with the name of the group you created in the firebase console.

It is important to note, the deployFirebase step, specifies assembleDebug as a dependency. This means it will only run if the assembleDebug step completes successfully.

To run the pipeline, simply commit your changes and push your main branch to GitLab. This will trigger your CI/CD pipeline. You can check its status by navigating to your project and clicking the CI/CD tab. If there are any errors, you can review the complete YAML file for any differences. After your pipeline completes, testers should receive the following email:

Conclusion

In this guide, you learned how to configure a Gitlab CI to build and distribute an android application using Firebase App Distribution. This can help you remove manual steps, weed out errors, and speed up the testing of your apps. While this tutorial was specifically for native android apps, it can easily be adapted for hybrid app frameworks like Flutter and React Native.