Continuous delivery for Android apps

Helder Pinhal
Helder Pinhal
Feb 4 2022
Posted in Engineering & Technology

Automate the distribution of your Android apps.

Continuous delivery for Android apps

GitHub Actions has been around for a few years, and while there are several alternatives like Circle CI, Travis CI, etc., it certainly has some advantages.

If you spend some time on GitHub, you've certainly noticed the Actions tab sitting right there. GitHub Actions is deeply integrated into the GitHub ecosystem, which is, if nothing else, convenient. No more do we need to create an account in some other service and integrate the two tools. On the other hand, not everyone will enjoy having a single point of failure for their source control and CI tool, specially when considering that GitHub has been having some downtimes.

You can run your workflows in Linux, Mac and Windows. The runners have loads of tools included by default (you can find the complete list here). That being said, the Android tools and licenses we need to build our app are already included and ready to be used.

Overview of (some) pros & cons

Pros
  • Deeply integrated tools
  • Prepared for Android
  • Considerably faster
Cons
  • Young ecosystem
  • Single point of failure

Getting started

There are three major concepts in GitHub Actions that are important to be aware of.

1. Workflow

Each workflow has its own YAML file under the .github/workflows folder. A single workflow defines one or more jobs that can run sequentially or in parallel.

2. Trigger

A trigger is what defines when a workflow runs. You can run a workflow in reaction to a pull request, a push, a tag or even a manual trigger. The complete guide on how to trigger a workflow can be found here.

3. Job

You can have several jobs in a single workflow and each will define a series of steps necessary to achieve a given task.

Let's get started with creating a workflow to test, build and distribute our app via Firebase App Distribution. The first thing we need is to create the workflow file and define a trigger.

name: Distribute app
on:
  push:
    branches:
      - main      # Only trigger when pushing to the 'main' branch.

Running the tests

Before building the app we should run the tests to make sure everything is OK and eligible for distribution. In the following sample we're also making sure we have the correct Java version. The cache: gradle property indicates the action to persist the Gradle binaries, so it's not downloaded every time it runs.

jobs:
  test:
    name: Run tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup JDK
        uses: actions/setup-java@v2
        with:
          distribution: adopt
          java-version: 11
          cache: gradle
      - name: Run unit tests
        run: ./gradlew test --stacktrace --no-daemon

Building the app

By default, all jobs in a workflow will run in parallel unless we configure otherwise. The build job should only run after the test job and to do so, we can use the needs property which lets you provide a list of jobs that need to run successfully, before the given job can run.

It's also a good practice to keep the build binaries for future reference, and in this case, we'll need them for the distribute_to_firebase job.

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    needs:
      - test
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup JDK
        uses: actions/setup-java@v2
        with:
          distribution: adopt
          java-version: 11
          cache: gradle
      - name: Build the app
        run: ./gradlew assembleRelease --stacktrace --no-daemon
      - name: Upload binaries
        uses: actions/upload-artifact@v2
        with:
          name: app-release
          path: app/build/outputs/apk/release/app-release.apk

Distributing to Firebase

Before we can upload anything to Firebase, we need to authenticate the runner. The easiest way to do so is to create a refresh token and store that in a GitHub Actions secret.

Run the following command in your terminal, copy the generated token and create a firebase_token secret.

firebase login:ci

Lastly, create an additional firebase_app_id secret to indicate which Firebase app to use when uploading. This id can be found in your project settings by selecting the intended app.

jobs:
  distribute_to_firebase:
    name: Distribute to Firebase
    runs-on: ubuntu-latest
    needs:
      - build
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Download binaries
        uses: actions/download-artifact@v2
        with:
          name: app-release
      - name: Upload to Firebase
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{ secrets.firebase_app_id }}
          token: ${{ secrets.firebase_token }}
          groups: your-app-distribution-groups-here
          file: app-release.apk

Conclusion

As you can see, with just a few lines of YAML we can set up a CI/CD system with the help of GitHub Actions. If you're a GitHub citizen, you're probably keeping tab on your GH notifications already, and you should start receiving a few more regarding the workflow executions.

As always, we hope you liked this article and if you have anything to add, we are available via our Support Channel.

Keep up-to-date with the latest news