GitLab CICD to Firebase App Distribution with APK

Jerry Cho
5 min readDec 6, 2023

--

In today’s fast-paced software development world, efficient and reliable continuous integration and continuous deployment (CI/CD) processes are essential. GitLab and Firebase are two powerful platforms that can be seamlessly integrated to streamline the deployment of mobile applications. This article will guide you through the process of integrating GitLab CI/CD with Firebase App Distribution, allowing you to automate the distribution of your Android to testers and stakeholders.

Part 1: Setting up Firebase Ap Distribution

Part 2: Prepare release keystore

storePassword={your password}
keyPassword={your password}
keyAlias={your key}

Part 3: Got Firebase token via firebase login:ci

  • Ref from Part 5 at above link
  • You will got following information:
  • FIREBASE_TOKEN, like 1//xxxxxxxxxxxxxxxxxxxxx

Part 4: Setup your gitlab project

Part 6: Update your project

  • Add file into your project (PS. also gitgnore them :) )
  • {projectFolder}/app/google-services.json
  • {projectFolder}/release.keystores & release.properties
  • {projectFolder}/release-notes.txt ( release note to test at firebase)
  • Update your /app/build.gradle.kts
import java.util.Properties
import org.gradle.api.JavaVersion


plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.gms.google-services")
}

android {
namespace = "com.jerry.gitlab_cicd"
compileSdk = 34

defaultConfig {
applicationId = "com.jerry.gitlab_cicd"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}

signingConfigs {
create("release") {

storeFile = file("${rootDir}/release.keystore")

val releaseKeystorePropertiesFile = readProperties(file("${rootDir}/release.properties"))
storePassword = releaseKeystorePropertiesFile["storePassword"] as String
keyAlias = releaseKeystorePropertiesFile["keyAlias"] as String
keyPassword = releaseKeystorePropertiesFile["keyPassword"] as String
}

getByName("debug") {
storeFile = file("${rootDir}/debug.keystore")
}

}

//should be under signingConfigs!!
buildTypes {

release {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}


}

fun readProperties(propertiesFile: File) = Properties().apply {
propertiesFile.inputStream().use { fis ->
load(fis)
}
}

dependencies {

implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.8.1")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")

//firebase
implementation(platform("com.google.firebase:firebase-bom:32.6.0"))
implementation("com.google.firebase:firebase-analytics")
}
  • Test build on your local via ./gradlew assembleRelease
  • You will see app-release.apk in {your project}\app\build\outputs\apk\release\}

Part 7: Setup your CI/CD variable at

Mapping with following key with STRING format

Part 8: Prepare .gitlab-ci.yml into {yourproject} folder

# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Android.gitlab-ci.yml

# Read more about this script on this blog post https://about.gitlab.com/2018/10/24/setting-up-gitlab-ci-for-android-projects/, by Jason Lenny
# If you are interested in using Android with FastLane for publishing take a look at the Android-Fastlane template.

image: eclipse-temurin:17-jdk-jammy

variables:

# ANDROID_COMPILE_SDK is the version of Android you're compiling with.
# It should match compileSdkVersion.
ANDROID_COMPILE_SDK: "33"

# ANDROID_BUILD_TOOLS is the version of the Android build tools you are using.
# It should match buildToolsVersion.
ANDROID_BUILD_TOOLS: "33.0.2"

# It's what version of the command line tools we're going to download from the official site.
# Official Site-> https://developer.android.com/studio/index.html
# There, look down below at the cli tools only, sdk tools package is of format:
# commandlinetools-os_type-ANDROID_SDK_TOOLS_latest.zip
# when the script was last modified for latest compileSdkVersion, it was which is written down below
ANDROID_SDK_TOOLS: "9477386"

# Packages installation before running script
before_script:
- apt-get --quiet update --yes
- apt-get --quiet install --yes wget unzip

# Setup path as android_home for moving/exporting the downloaded sdk into it
- export ANDROID_HOME="${PWD}/android-sdk-root"
# Create a new directory at specified location
- install -d $ANDROID_HOME
# Here we are installing androidSDK tools from official source,
# (the key thing here is the url from where you are downloading these sdk tool for command line, so please do note this url pattern there and here as well)
# after that unzipping those tools and
# then running a series of SDK manager commands to install necessary android SDK packages that'll allow the app to build
- 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

# Nothing fancy here, just checking sdkManager version
- sdkmanager --version

# 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}"

# Not necessary, but just for surity
- chmod +x ./gradlew


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

stages:
- build
- deploy


# Build release
assembleRelease:
stage: build
only:
- main #only work on main branch
script:
- echo ${GOOGLE_SERVICE_JSON} | base64 -d > app/google-services.json
- echo ${RELEASE_PROPERTIES} | base64 -d > release.properties
- echo ${RELEASE_KEYSTORE} | base64 -d > release.keystore
# - ./gradlew bundleRelease
- ./gradlew assembleRelease
artifacts:
expire_in: 7 days
paths:
# - app/build/outputs/bundle/**/*.aab
- app/build/outputs/apk/**/*.apk

# Deploy release
deployFirebase:
stage: deploy
image: node:latest
before_script:
- export GRADLE_USER_HOME=$(pwd)/.gradle
- export JAVA_HOME="/usr/bin/java"
- apt-get update -y && apt-get install wget -y
dependencies:
- assembleRelease
only:
- main #only work on main branch
script:
- npm install -g firebase-tools
# not work for aab because https://firebase.google.com/docs/app-distribution/android/distribute-console?hl=en&authuser=0&_gl=1*1bbeg83*_ga*OTU3MDA5MzEzLjE3MDE2MDY5Mjk.*_ga_CW55HF8NVT*MTcwMTg2NjE1Ni4xMi4xLjE3MDE4Njg1NjEuNTUuMC4w
# - if [ -f "app/build/outputs/bundle/release/app-release.aab" ]; then firebase appdistribution:distribute app/build/outputs/bundle/release/app-release.aab --app $FIREBASE_APP_ID --release-notes-file release-notes.txt --groups "testers" --token "$FIREBASE_TOKEN"; fi
- if [ -f "app/build/outputs/apk/release/app-release.apk" ]; then firebase appdistribution:distribute app/build/outputs/apk/release/app-release.apk --app $FIREBASE_APP_ID --release-notes-file release-notes.txt --groups "testers" --token "$FIREBASE_TOKEN"; fi

Part 9: Let try with push something

👏 Congratulation! You will see a new apk was at firebase like 👏

--

--