Android GitHub Actions Setup
Tags: android, github, githubactions, cicd
Published: 2020-07-08 13:02:27

Here at [Coletiv](https://coletiv.com/), when starting a new project, we always aim to deliver it following the highest standards of quality. Like so, [continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) (CI) and [continuous deployment](https://en.wikipedia.org/wiki/Continuous_deployment) (CD) are some of the first things we focus on before starting to code. That way we are sure that our code is always checked, tested, and meets our quality standards before being deployed.
<br/>
For Android specifically, we always used services like [TravisCI](https://coletiv.com/blog/android-build-times-travis-ci/) or [CircleCI](https://circleci.com/) for this task, but since we are heavy users of [GitHub](http://github.com/) we couldn’t wait to experiment with [GitHub Actions](https://github.com/features/actions) and check if it would be a better option for our projects. And guess what? It’s amazing!
<br/>
Here is how we set up GitHub Actions on our Android projects.

## Workflow Overview

We always have two types of [workflows](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions), one for testing and code checking and one for deploying. The first always runs on pull requests and the last only runs when the code is merged to master or develop.

> Please note that we usually create multiple [build variants](https://developer.android.com/studio/build/build-variants) for different environments. In this example, we’re running the commands for the staging build variant. You should change all the commands to your correct variant name or remove the staging word from them if you don’t need to use variants.

### Testing Workflow

The purpose of this workflow is to run some [lint checks](<https://en.wikipedia.org/wiki/Lint_(software)>) and to run our tests. Here is the list of jobs that it performs:

- Decode our Google services config file (if you’re not using Google services or Firebase you can remove this job).

- Run Kotlin lint check.

- Run Android lint check.

- Run unit tests.

- Run instrumented tests.

```yaml
name: Testing Workflow

# Step 1: Choose the branch or branches you want to run this workflow
on:
  pull_request:
    branches:
      - develop

jobs:
  testing:
    name: Lint Check and Testing

    runs-on: ubuntu-latest

    steps:
      - name: Clone Repo
        uses: actions/checkout@v1

      - name: Set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      # Step 2: Decode Google services configuration file
      - name: Decode google-services.json
        env:
          FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }}
        run: echo $FIREBASE_CONFIG > app/google-services.json

      # Step 3: Check the code with ktlint, you can remove this job if you don't use ktlint
      - name: Run Kotlin Linter
        run: ./gradlew ktlintStagingDebugCheck

      # Step 3: Check the code with Android linter
      - name: Run Android Linter
        run: ./gradlew lintStagingDebug

      # Step 4: Yun your unit tests
      - name: Run Unit Tests
        run: ./gradlew testStagingDebugUnitTest

      # Step 4: Assemble debug apk to send to firebase test lab
      - name: Assemble Debug APK
        run: ./gradlew assembleStagingDebug

      # Step 4: Assemble debug test apk to send to firebase test lab
      - name: Assemble Debug Test APK
        run: ./gradlew assembleStagingDebugAndroidTest

      # Step 4: Run instrumented tests on firebase test lab
      - name: Run tests on Firebase Test Lab
        uses: asadmansr/Firebase-Test-Lab-Action@v1.0
        with:
          arg-spec: '.github/test-lab-config-staging.yml:android-pixel-4'
        env:
          SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
```

Let’s go step by step to explain everything.

1. Tell GitHub when do you want to run this workflow. In this case, I’m saying that I want it to run on pull requests to the develop branch. You can tell it to run on any branch you like, just replace/add the name.

2. Skipping a bit further, since we’re using the Google services, you need to go to the settings to add a new [secret](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets). Just create a new secret paste the content of your Google services JSON file there and choose a name. Our name for this secret is FIREBASE_CONFIG.

3. We want to perform some checks to the code. For this, we’re using Ktlint and the built-in [Android linter](https://developer.android.com/studio/write/lint). To configure Ktlint check [this](https://github.com/pinterest/ktlint) and [this](https://github.com/JLLeitschuh/ktlint-gradle). If you don’t need to run Kotlin checks delete the Kotlin linter job.

4. Finally, we are creating jobs to run our tests. Running unit tests is pretty straightforward but running instrumented tests is really a pain because you need an emulator or real device to run them. You can go two ways here. Create a job to create and start an emulator on the CI machine (check [this](https://github.com/marketplace/actions/android-emulator)) or run them on [Firebase Test Lab](https://firebase.google.com/docs/test-lab/android/overview). We’ve chosen the last one and we’re using the awesome [Firebase Test Lab Action](https://github.com/marketplace/actions/firebase-test-lab-action). For more info on how to do this follow the [guide](https://github.com/asadmansr/Firebase-Test-Lab-Action/blob/master/docs/SIMPLE_USAGE.md).

```yaml
android-pixel-4:
  type: instrumentation
  # Specify the path to the debug and test apks generated early
  app: app/build/outputs/apk/staging/debug/app-staging-debug.apk
  test: app/build/outputs/apk/androidTest/staging/debug/app-staging-debug-androidTest.apk
  device:
    - model: flame
      version: 29
      locale: 'en'
      orientation: portrait
```

### Deploy Workflow

Assuming that you have the develop and master [branches protected](https://help.github.com/en/github/administering-a-repository/configuring-protected-branches) and that you ran the testing workflow before doing a merge to these main branches, this workflow is very simple. It only needs to publish our app.

```yaml
name: Deploy Workflow

# Step 1: Choose the branch or branches you want to run this workflow
on:
  push:
    branches:
      - develop

jobs:
  deploy:
    name: Deploy

    runs-on: ubuntu-latest

    steps:
      - name: Clone Repo
        uses: actions/checkout@v1

      - name: Set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      # Step 2: Decode Google services configuration file
      - name: Decode google-services.json
        env:
          FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }}
        run: echo $FIREBASE_CONFIG > app/google-services.json

      # Step 3: Decode the service account json key, needed on the next job
      - name: Decode service account key
        env:
          SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
        run: echo $SERVICE_ACCOUNT > app/service-account-key.json

      # Step 4: Publish the APK to the Play Store using the release keystore
      - name: Publish APK
        env:
          RELEASE_KEYSTORE_PASSWORD: ${{ secrests.RELEASE_KEYSTORE_PASSWORD }}
          RELEASE_KEY_ALIAS: ${{ secrests.RELEASE_KEY_ALIAS }}
          RELEASE_KEY_PASSWORD: ${{ secrests.RELEASE_KEY_PASSWORD }}
        run: ./gradlew publishStagingRelease
```

Let’s again go step by step to explain everything.

1. Tell GitHub when to run this workflow. I’m saying that I want it to run on pushes to the develop branch.

2. Like on the testing workflow we need the Google services JSON file so we’re decoding it before running any Gradle task.

3. We also need to create a Google service account to be able to have publish access in the next job. Check [this](https://github.com/Triple-T/gradle-play-publisher#service-account) for more info. After creating the account you can download the JSON key and add it to the secrets like you’ve done before with the Google services JSON.

4. Lastly, we are publishing the app using the [Gradle Play Publisher](https://github.com/Triple-T/gradle-play-publisher) plugin. You’ll need to add some configurations into your Android project to have this task available. For that please follow the instructions [here](https://github.com/Triple-T/gradle-play-publisher#table-of-contents). You also must add the release Keystore passwords and alias to the GitHub secrets as those are needed on this job and are sensitive data. Note that we usually have the [Keystore file on our private repos](https://stackoverflow.com/a/56116822/6937784), if you don’t want to have it there or you’re working on a public repo, you can also add it to your secrets and then decode it when running the workflow.
<br/>

## Final Thoughts

I really recommend that you give GitHub Actions a try in your next Android project. It’s easy to create and manage workflows as the syntax is very simple and understandable, the integration with other GitHub services is nice and having your CI builds in the same place of your repository is really a plus. Also, the community is amazing and the number of actions in the [GitHub marketplace](https://github.com/marketplace?type=actions) grows by the day.
<br/>
## Thank you for reading!

Thank you so much for reading, it means a lot to us! Also **don’t forget to follow [Coletiv](/) on [Twitter](https://twitter.com/coletivstudio) and [LinkedIn](https://www.linkedin.com/company/coletiv/)** as we keep posting more and more interesting articles on multiple technologies.

In case you don’t know, Coletiv is a software development studio from Porto specialised in Elixir, Web, and App (iOS & Android) development. But we do all kinds of stuff. We take care of UX/UI design, software development, and even security for you.

So, **[let’s craft something together?](https://coletiv.com/contact)**