ArticlesAndroidCI Pipeline

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.

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