CI
pipeline for Android
Apps on top of Github Actions
Introduction
The purpose of this article is to fulfill an internal need for reliable documentation on how to properly set up CI & CD pipelines for our Android apps. For our Android development team, having a robust and reliable CI/CD pipeline is crucial to streamline the development process, catch issues early, and deploy updates swiftly, yet the internet currently lacks proper documentation on how to build your pipelines. This article aims to provide comprehensive, step-by-step guidance on setting up effective CI and CD pipelines for Android apps.
CI - Continuous Integration Pipeline
The first pipeline that one might consider integrating within their development setup is the CI. It should build and test your mobile Android app whenever a new Pull Request is opened and ready to be reviewed. I am not going to explain why it’s important to base your work on PRs or why building / testing your app is important before reaching the main
(ex master ) branch. But I am going to explain each and every step of the pipeline so you (the developer) understand the reasoning behind it.
Header
Defines the name of the pipeline alongside when it should run. In our case, the pipeline will run each time there is a new pull request opened against the main
branch. The pipeline will run the code on the branch (ex: task/some-branch-name
) that you intend to merge into main
.
name: Build & Test
on:
pull_request:
branches: [ "main" ]
Envelope
Defines the so-called jobs
alongside each runner
and the steps
to be run within that job
. In our case, we will be running the so-called build
job
on ubuntu-latest
provided by Github, there are multiple the runners already available but you can also define yours using this [steps] (https://docs.github.com/en/enterprise-cloud@latest/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners)
jobs:
build:
runs-on: ubuntu-latest
steps:
# here it comes the fun part - all the content below should be added here
Step 1 - Checkout
This is an action that checks out your repository onto the runner, allowing you to run scripts or other actions against your code (such as build and test tools). You should use the checkout action any time your workflow will use the repository’s code.
# Checkout
- uses: actions/checkout@v3
Step 2 - Setup JDK
This action just sets up your Java environment, but if you’re an Android Developer/ DevOps you know all that already. A few important aspects that might be important for you are the following: distribution
in our case zulu
, java-version
in our case 17
and cache
which should be gradle
.
# Setup JDK
- name: Set Up JDK
uses: actions/setup-java@v3
with:
distribution: 'zulu' # See 'Supported distributions' for available options
java-version: '17'
cache: 'gradle'
Step 3 - Permissions
Just provide right permissions to Gradle
# Provide right permissions to gradle
- name: Change wrapper permissions
run: chmod +x ./gradlew
Step 4 - Build Debug
You can execute all the build tasks available for your Android project using gradlew command line tool. It’s available as a batch file for Windows (gradlew.bat) and a shell script for Linux and Mac (gradlew.sh), and it’s accessible from the root of each project you create with Android Studio.
- name: Build Gradle
run: ./gradlew assembleDebug
Optional Step 5 - Optional Run Tests
Run your unit tests, using gradle
command line tools. Only use this step if you have any unit tests written, otherwise skip this step.
- name: Run Tests
run: ./gradlew assembleDebug
Optional Step 6 - Slack Notification - Extract Branch
Use this step in order to extract the current branch name in order to be used in the next step when sending a Slack notification regarding your build status.
# Extract Branch Name
- name: Extract branch name
if: always()
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch
Optional Step 7 - Slack Notification - Push Notification
Use this step in order to send a Slack notification regarding your build status as successful 🚀 or fail 💥. Please note that you need to set up an environment secret called SLACK_WEBHOOK_URL
within GitHub, containing an webhook from Slack.
- name: Slack Notification
if: always()
id: slack
uses: slackapi/slack-github-action@v1.23.0
with:
# This data can be any valid JSON from a previous step in the GitHub Action
payload: |
{
"build_status": "${{ job.status }}",
"work_done": "CI from - branch ${{ steps.extract_branch.outputs.branch }}",
"github_repository": "${{ github.event.repository.name }}",
"github_commit_url": "${{ github.event.head_commit.url }}"
}
env:
SLACK_WEBHOOK_URL: ${{secrets.SLACK_WEBHOOK_URL}}
Whole Action
Below you will get the feeling of how the whole action could look like. We’ve combined all the steps above in a ready to use CI
action that can be reused for all your Android
apps.
name: Build & Test
env:
# The name of the main module repository
main_project_module: app
# The name of the Play Store
playstore_name: Axalta
on:
pull_request:
branches: [ "main" ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Setup JDK
- name: Set Up JDK
uses: actions/setup-java@v3
with:
distribution: 'zulu' # See 'Supported distributions' for available options
java-version: '17'
cache: 'gradle'
# Provide right permissions to gradle
- name: Change wrapper permissions
run: chmod +x ./gradlew
# Build App
- name: Build Gradle
run: ./gradlew assembleDebug
# Run Tests
- name: Run Tests
run: ./gradlew assembleDebug
# Extract Branch Name
- name: Extract branch name
if: always()
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch
# Push slack notification
- name: Slack Notification
if: always()
id: slack
uses: slackapi/slack-github-action@v1.23.0
with:
# This data can be any valid JSON from a previous step in the GitHub Action
payload: |
{
"build_status": "${{ job.status }}",
"work_done": "CI from - branch ${{ steps.extract_branch.outputs.branch }}",
"github_repository": "${{ github.event.repository.name }}",
"github_commit_url": "${{ github.event.head_commit.url }}"
}
env:
SLACK_WEBHOOK_URL: ${{secrets.SLACK_WEBHOOK_URL}}
Written by @DanielMandea