Setting up CI/CD with Github Actions and Firebase App Distribution for Android Projects
Tags: android, firebase, github
Published: 2020-05-13 19:02:13

[Portuguese Version](https://dev.to/denisvieira05/configurando-ci-cd-com-github-actions-e-firebase-distribution-para-projetos-android-5b47) 🇧🇷

Continuous integration (CI) and continuous deployment (CD) are the first steps to start automating and speed up our development process. This way, we are sure that our code is always verified, tested and attend our quality standards before being deployed.

Currently, there are many tools that can help us automate all of this. One of the most popular are Jenkins, GitLab, Travis CI, CircleCI or Fastlane. But now, to our happiness, the darling of open source, Github, has entered the game with GitHub Actions.

On Github there is a marketplace that contains several Apps and Actions available to add great powers to our projects and the best thing is that they are easily coupled and uncoupled with simple configurations. These items work as extensions or plugins that automate several important tasks for our project and are also open-source and developed by several developers. For our setup we will use the actions, [Checkout] (https://github.com/marketplace/actions/checkout) to checkout our repository, [setup-java] (https://github.com/marketplace/actions/setup-java-jdk) to configure Java JDK and [Firebase App Distribution] (https://github.com/marketplace/actions/firebase-app-distribution) to finally send our APK to Firebase.

So, come on.

First, go to the "Actions" tab in the repository of the android project that you want to automate, then we will go to the "setup a workflow yourself" link to create our own configuration through the Github editor, remembering that after finalizing the file of all workflows you need to commit it, but if you want you can also do it manually in your personal code editor, for that we just need to create a folder at the root of your project with the name ".github" and inside it create another folder with name "workflows" and we’ll add all of our workflow files to it.

Based on the need to create a setup to apply a workflow based on the [Git Feature Branch] (https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow), we will basically create 2 workflows , the "Pull Requests CI" that will start when there is a new pull request for Master or release branch, checking if the tests pass, running build and generating an APK that will be stored in Github Artifacts for functional tests of a new feature or bug developed and the "Master Release CI" that will run when there is an update in the Master or branch of release, making the same checks but this time sending the release APK to Firebase App Distribution.

Note: Feel free to use any name for your workflows, Jobs and Steps in this post, the ones used are only suggestions based on my current need.

We will start then by creating Pull Requests CI, first we will configure in which case we will run this workflow, in this case in all pull requests created for the "master" or "release .." branchs.

```
name: Pull Requests CI

on:
  pull_request:
    branches:
      - 'master'
      - 'release*'
```
Now we will configure the Jobs that will be executed. Each Job can have several Steps, and it is in these Steps that we will configure what we need to reach each result.

So, assuming that if the tests don't pass, you don't need to run anything else. The first job that we will configure will be the test job. In this Job we basically need an Ubuntu environment and configure JDK 1.8 as prerequisites, so for this Job we have these two Steps defined by the name "set up JDK 1.8" and "Unit tests", and in "runs-on" we define the environment "Ubuntu-latest". Having our first Job getting this way:
```
# A workflow is composed of one or more Jobs that can be executed sequentially or in parallel
jobs:
  # This Workflow contains two Jobs called "test" and "build-and-deploy"
  test:
    name: Run Unit Tests
    # The type of runner the job will run on
    runs-on: ubuntu-latest
    # Steps represent the sequence of tasks using shell runners that will be executed as part of the Job
    steps:
    - uses: actions/checkout@v1
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Unit tests
      run: bash ./gradlew test --stacktrace
```

Now we will configure our main Job "build-and-generate-apk", in this Job we will configure the Steps "set up JDK 1.8", "Install NDK", "Build Debug" and "Upload APK on Github Artifact" where we will make available for a QA, PO or by yourself the APK for testing the Feature or Bug before being dipped. Having our Job looking like this:
```
  build-and-generate-apk:
    # The type of runner the job will run on
    runs-on: ubuntu-latest
    # Steps represent the sequence of tasks using shell runners that will be executed as part of the Job
    steps:
    - uses: actions/checkout@v1
    # Step to Configure the JDK
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8       
    # Step to install the NDK
    - name: Install NDK
      run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;20.0.5594570"
    # Step to Build the Project and generate the APK
    - name: Build Debug 
      run: ./gradlew assembleDebug
    # Step to save the APK as Artifacts
    - name: Upload APK on Build Artifacts
      uses: actions/upload-artifact@v1
      with:
        name: app
        path: app/build/outputs/apk/debug/app-debug.apk 
```
Done, now every time whenever create a Pull Request, the Pipeline starts with this Workflow.

![Pipeline starting] (https://miro.medium.com/max/1400/1*cHFmyFFpwWqUh9i3sU3D8w.gif)

And at the end of it we have a test apk generated and available to perform a functional test of a feature or bugfix.

![Apk Generated on Github Artifacts] (https://miro.medium.com/max/1400/1*S1LteudpMHmkA5zUGpgeVw.png)

Here is the link to the final file for this workflow:
- ["Pull Request CI" Workflow file link on Github] (https://github.com/denisvieira05/themoviesdb-android-app/blob/master/.github/workflows/pull-request-ci.yml)

Having ready our first workflow that will handle our first development process, the Pull Request. Now we will configure the "Master Release CI" that will start whenever an update occurs in our main branch, in this case "master" or some other branch with initials "release".
```
name: Master Release CI

on:
  push:
    branches:
      - 'master'
      - 'release*'
```
In this workflow in the configuration part of Jobs and Steps, only what differs would be to add the Step of "upload artifact to Firebase App Distribution", where we will upload the artifact generated in the build to Firebase Distribuition, and for that we will need to make some configurations with o Firebase, if you have not yet configured Firebase in your Android project follow the instructions in the [Official Documentation Link] (https://firebase.google.com/docs/android/setup?hl=en).

With your application configured, we will enter our project on the [firebase console] (https://console.firebase.google.com/u/0/) and access the "App Distribution" session, there we will add our Android app and it will now be available to send an apk and distribute it to the testers and groups we want. With that you can now upload your apks manually and distribute to groups easily, it is also possible to upload any apk locally via the FIREBASE CLI following the [Official Documentation instructions] (https://firebase.google.com/docs / app-distribution / android / distribute-cli).

![Firebase Distribution ](
https://www.androidpolice.com/wp-content/uploads/2019/09/image1.png)

To perform this procedure automatically via Github Actions and in a reliable pipeline, we need to generate some credentials before hand. The "FIREBASE APP ID" and "FIREBASE TOKEN".

The Firebase App ID is easily found in the tab of going to Project Overview -> Select Settings of the Android App already Configured -> And under "Your apps" we copy the hash of the App Id. Ex.:"1:1234567890:android: 0a1b2c3d4e5f67890 "

![App Settings Firebase](https://docs.monaca.io/images/tutorials/10.png)

To purchase the Firebase Token it is necessary to use the Firebase CLI to perform some local actions on your machine, if you have not yet configured it, see how to install it in the following [Official Documentation Link] (https://firebase.google.com/docs/cli#install_the_firebase_cli).

With the Firebase CLI installed, we will configure it to use our Firebase account using the command:
```
firebase login:ci
```

This command will open a new google authentication window in the browser (if it doesn't open automatically, there will be a link that you can see on the command line), after successfully authenticating with your account, you will see the message "✔ Success! Use this token to login on a CI server: "and just below you will see a hash that is our" Firebase Token "and that should be used in our github actions workflow file.

![Firebase Token](https://loiane.com/assets/images/2017/angular-travis-firebase-10.jpg)

As the Firebase App Id and Firebase Token are credentials it is important that we don't leave it exposed directly in the code, so we will configure it as "Secrets" of the project and thus leaving them dynamic and really obfuscated.
To configure the "Secrets" of a project go to the Settings part of your repository and there will be a "Secrets" tab. Then simply go to "Add a new secret" and add the two we need, FIREBASE_APP_ID and FIREBASE_TOKEN, with their respective values that we acquired in the previous steps.

![Github Secrets](https://www.edwardthomson.com/blog/images/githubactions/11-addingsecret.png)

Now with the credentials we need at hand and configured in the project, let's go back to our workflow configuration file. In it we will now add our only Job with the necessary steps to build and generate the apk, adding the new Step that will serve to send the Apk to the Firebase Distribuition automatically. So our Job is exactly as follows:
```
# A workflow is composed of one or more Jobs that can be executed sequentially or in parallel
jobs:
  # This Workflow contains a single Job called "build-and-deploy"
  build-and-deploy:
    # The type of runner the job will run on
    runs-on: ubuntu-latest
    # Steps represent the sequence of tasks using the shell runners that will be run on as part of the Job
    steps:
    - uses: actions/checkout@v1
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8       
    # Step to Install the NDK
    - name: Install NDK
      run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;20.0.5594570"
    # Step to Build the Project and generate the APK
    - name: build debug apk
      run: ./gradlew assembleDebug
    # Step to Submit the generated APK to Firebase App Distribution
    - name: upload artifact to Firebase App Distribution
      uses: wzieba/Firebase-Distribution-Github-Action@v1.2.1
      with:
        appId: ${{ secrets.FIREBASE_APP_ID }}
        token: ${{ secrets.FIREBASE_TOKEN }}
        groups: testers
        file: app/build/outputs/apk/debug/app-debug.apk

```
Here is the link to the final file for this workflow:
- [Master Release CI workflow file link on Github] (https://github.com/denisvieira05/themoviesdb-android-app/blob/master/.github/workflows/master-release-ci.yml)

And ready ! We now have what we need to deliver integrated, continuous and centralized delivery to all stakeholders just by following a simple development flow. Please comment your doubts, suggestions and share your experience.

Follow the link to an old open source project that I applied the settings in this article.
- https://github.com/denisvieira05/themoviesdb-android-app

Next step:
- Configure to generate release apk with the keystore

These workflows are still very simple, but I intend to create new posts by adding some other very important jobs or steps, such as linters, sonarqube, test reports and running instrumented tests with Firebase Test Lab.

Notes:
- Even sending the apk automatically to Firebase Distribuition through Actions, you still need to go to the Firebase console in the App Distribuition session and make the distribution to the group you want.
- By default, the release notes are filled with the description of the last commit, but it is possible to modify it before carrying out its distribution.

Referencies: 
- https://coletiv.com/blog/android-github-actions-setup/
- https://medium.com/@wkrzywiec/github-actions-for-android-first-approach-f616c24aa0f9