How to setup Android CI/CD in MNC Repo

Posted February 4, 2022 by Ramdan Nurul ‐ 4 min read

It is undeniable, the build process of an application often spends a lot of time in developing an android application, especially if the scope of existing features is quite large. CI/CD can automate the process even to the distribution of APK(s) to testers. Continuous Integration (CI) is one of the most common development practices used in software development by enforcing automation in building. Continuous delivery (CD) is the step after Continuous Integration, used to deploy or distribute the applications.

GitLab has a simple and free CI/CD system that can automate the build, test, and deploy processes that can be applied to our apps. In this article, I will explain how to setup Android CI/CD configuration in GitLab repository, with the final goal is deploying the APK(s) to Firebase app distribution.

Signing Config

Prepare the keystore that was previously created, create a file key.properties to save the credentials data of the keystore. Don’t forget to add the file to .gitignore.

storePassword=<your_store_password>  
keyPassword=<your_key_password>  
keyAlias=<your_key_alias>
storeFile=<your_kestore_location_file>

Then change the signing config in the gradle app by pointing to the key.properties file just created.

def keystoreProperties = new Properties()  
def keystorePropertiesFile = rootProject.file('key.properties')  
if (keystorePropertiesFile.exists()) {  
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))  
}

Add the following script in the android section :

signingConfigs {  
  file(rootProject.file('key.properties')).with { propFile ->  
       if (propFile.canRead()) {  
            config {  
                keyAlias keystoreProperties['keyAlias']  
                keyPassword keystoreProperties['keyPassword']  
                storeFile file(keystoreProperties['storeFile'])  
                storePassword keystoreProperties['storePassword']  
            }  
        }  
        else {  
            print('not signed')  
        }  
    }  
}

Then, add the following script to the defaultConfig section :

file(rootProject.file('key.properties')).with { propFile ->  
  if (propFile.canRead()) {  
        signingConfig signingConfigs.config  
    }  
}

For more details how to signing config, you can visit: https://developer.android.com/studio/publish/app-signing.

Store as Variable

To protect sensitive data and information, we can store it as variable in Gitlab CI/CD settings. For files like keystore, key.properties dan google-services.json, we can convert them to base64 encoding first before store to variabel.

Settings > CI/CD > Variables

For more details how to get firebase ci token, you can visit: https://firebase.google.com/docs/cli.

Gitlab CI/CD Pipeline

GitLab CI/CD pipelines are configured using a YAML file named .gitlab-ci.yml in the root of the project folder. The configuration file consists of several tags :

  • Image, to define a Docker Image that will be used for execution in CI/CD Jobs.
  • Variable, is to set a value to a variable used during script execution.
  • Before Script, used to initialize setup or define the command that should be run before main script execution.
  • Cache, to specify a list of files and directories to cache between jobs.
  • Stages, is to defines the order of job stage for pipeline.
  • Stage, used to specify the stages of work in progress, the pipeline will execute all stages with the same name first.
  • Only, is to applied only when there is a push on a specific branch.
  • Script, is to execute the main script where we build or sign the application, with the credentials previously stored in a variable.
  • Artifacts, used to store or upload the file (APK) and passed to the next job and are also available for download from repo.

We can add CI/CD configuration to any branch we want. In my case, I create a new branch with the named app-distribute to run the pipeline.

Build Stage

Here is the workflow build script for the debug :

 assembleDebug:
  stage: build
  only:
    - app-distribute
  script:
    - ./gradlew assembleDebug.

Meanwhile, here is the workflow build script for the production release :

assembleProductionRelease:
 stage: build
 only:
 - app-distribute
 script:
 - echo ${GOOGLE_SERVICE_JSON} | base64 -d > app/google-services.json
 - echo ${KEY_STORE_PROP} | base64 -d > key.properties
 - echo ${KEYSTORE_FILE} | base64 -d > app/keystore.jks
 - ./gradlew assembleProductionRelease
 artifacts:
 expire_in: 7 days
 paths:
 - app/build/outputs/apk/production/**/*.apk

It’s free to change depending on your needs.

Sometimes the Docker Image OpenJDK script from Gitlab CI has problems because the license of the Android SDK changes causing the build to fail. An alternative is to use jangrewe’s prebuild Docker Image

Deploy Stage

You need to create a tester group first in the firebase console, then add the update notes file (release-notes.txt) in the root of the folder structure.

deployFirebaseProduction:
 image: node:latest
 stage: deploy
 before_script:
 - export GRADLE_USER_HOME=$(pwd)/.gradle
 - export JAVA_HOME="/usr/bin/java"
 - apt-get update -y && apt-get install wget -y
 dependencies:
 - assembleProductionRelease
 only:
 - app-distribute
 script:
 - npm install -g firebase-tools
 - if [ -f "app/build/outputs/apk/production/release/app-release.apk" ]; then firebase appdistribution:distribute app/build/outputs/apk/production/release/app-release.apk --app $FIREBASE_APP_ID --release-notes-file release-notes.txt --groups "gitlab-group" --token "$FIREBASE_CI_TOKEN"; fi

Implementation

This is the sample script of how I implement Gitlab CI/CD in one of the android projects in the MNC Repo.

Full script of yaml can be found here.

Conclusion

To make it easier for a mobile developer from build APK repeatedly, I think CI/CD can be applied, especially in developing android mobile applications in the MNC Repo. Some other better alternative tools such as Codemagic might also good, but they need more cost. So far, Gitlab CI/CD on the MNC Repo is better. However, the MNC Repo has a limit on the size of the uploaded APK(s).