CI/CD is a development practice that delivers software to the end user with speed and reliability. This is important for any kind of app be it web, mobile, console or desktop. In this post we will discuss how we can setup CI/CD for an Android Application using Github Actions.
Before we jump on to the implementation let us discuss a little about CI/CD first.
What is Continuous Integration?
Continuous Integration says that developers in the team should merge their code changes to a central branch as often as possible. These changes are validated using an automated integration process which generates a build and runs the automated tests against it. This helps in figuring out any integration issues early in the development cycle instead of waiting for the release day to merge all the changes.
What is Continuous Delivery?
Continuous Delivery says that every code change which has passed through the integration process can be deployed to test/production environment in an automated fashion or with least human intervention.
CI/CD for Android App
Setting up CI/CD for Android application is simple with tools like Github Actions. Github Action provides you with all the features and tools necessary to build and test an Android app.
Pipeline Architecure
Now let us design our CI/CD flow so that we are clear what we want to achieve. For any Android project I would recommend the following steps:
- Android Lint Check
- Unit Tests
- Instrumentation Tests
- Static Code Analysis
- Packaging
- Functional Tests
- Deployment
There can be more checks like checking the libraries for any vulenerabilities or adding a check to make sure that secrets are not checked into the code. Your team would be the best judge to finalize the CI/CD steps.
The whole process starts when a developer pushes the code to the centeral Github repository. The moment code is pushed, the CI/CD process will kick in. First it will run a lint check to make sure we are following the Android’s guidelines for coding. After that we will run unit and intrumentation tests. Then we will run a static code analysis using Sonarqube to catch any code smells introduced in the code base.
After we are done with initial checks we will create the APK file and provide it to the Functional Tests. Once the Functional Tests pass, we will Deploy the APK to a place where its available to QAs for testing.
Here is a detailed video that explains the architecture in more depth.
Now let us start building above checks using Github Actions.
Setup Github Actions for your repository
To add Github Actions to your repository you need to create a yaml file your_repo/.github/workflows/ci.yml
The first thing that you need to define in the ci.yml
is the events on which you want to trigger your workflow. In below configuration we are running the build whenever there is a push on main
branch or when someone creates a pull request against main
branch.
All the code that you see in this post is available on github.
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
Android Lint Check
Now that our basic configuration is in place, we will add Lint check as our first job. Let us understand what the following configuration does.
Step 1: runs-on: ubuntu-latest
tells to run the job on latest ubuntu machine.
Step 2: actions/checkout@v2
action checks out the codebase on the machine
Step 3: Once we have the codebase on the machine, run ./gradlew lintDebug
Step 4: Publish the lint report as a github artifact
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: Run lint
run: ./gradlew lintDebug
- name: Upload html test report
uses: actions/upload-artifact@v2
with:
name: lint.html
path: app/build/reports/lint-results-debug.html
Here is a detailed video which shows the whole process of adding lint check in depth.
Android Unit Tests
Our second job would be to run the unit tests. This job will run after the lint
job and that is why you see needs: [lint]
in the below config. We have already understood how to mention the machine and check out the codebase, let us just talk about the step 3 and 4.
Step 3: Run ./gradlew test
will run the unit tests
Step 4: Publish the test report folder as a github artifact
We need to publish the entire folder because we need all the files in app/build/reports/tests/testDebugUnitTest/
folder to navigate through the html report.
unit-test:
needs: [lint]
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: Run tests
run: ./gradlew test
- name: Upload test report
uses: actions/upload-artifact@v2
with:
name: unit_test_report
path: app/build/reports/tests/testDebugUnitTest/
Below is the detailed video which shows adding and running the unit tests in action.
Android Instrumentation Tests
Our 3rd job would run Android instrumentation tests. To ensure that it runs after unit tests
we have added needs: [unit-test]
.
You would notice that we are running this job on mac-latest
machine. That is because the modern Intel Atom (x86 and x86_64) emulators require hardware acceleration from the host to run fast.
The macOS VM provided by GitHub Actions has HAXM installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android tests directly on the VM.
Most of the steps we have already discussed in above configurations. The only thing we are doing extra is to start the android emulator using reactivecircus/android-emulator-runner@v2
and running the instrumentation tests using ./gradlew connectedCheck
.
instrumentation-test:
needs: [unit-test]
runs-on: macos-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: Run espresso tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
script: ./gradlew connectedCheck
- name: Upload test report
uses: actions/upload-artifact@v2
with:
name: instrumentation_test_report
path: app/build/reports/androidTests/connected/
You can find all of it in action in below video.
Static Code Analysis
As part of our next job we will run static code analysis using Sonarqube. For this you need to setup your sonarqube server or use SonarCloud. In this case I have used SonarCloud to publish the analysis results.
The minimum version required for sonar scanner is Java 11 and that is why you see a step to setup Java 11 jdk on the machine. Lastly we run the ./gradlew app:sonarqube
command to run the sonar scanner. You can see that we are also providing the SONAR_TOKEN
which is required to connect with SonarCloud. Also we need to provide the GITHUB_TOKEN
token in the environment variable to publish the code from Github to SonarCloud.
static-code-analysis:
needs: [instrumentation-test]
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: SonarCloud Scan
run: ./gradlew app:sonarqube -Dsonar.login=${{ secrets.SONAR_TOKEN }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
You can generate the Sonar token using SonarCloud portal. You can watch below video to understand how to setup sonarqube for your project and how the reports will look like on SonarCloud.
Generating APK file
Package job will run after the static code analysis. Here we will create apk file using our gradle command and upload it as github artifcat.
package:
needs: [static-code-analysis]
name: Generate APK
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build debug APK
run: ./gradlew assembleDebug
- name: Upload APK
uses: actions/upload-artifact@v2
with:
name: expense-manager.apk
path: app/build/outputs/apk/debug/app-debug.apk
You can see all about generating apk in below video.
Deploy to App Center
Finally we will deploy the apk file to app center so that it’s available for testing. We will use App Center to deploy our app.
First we need to download the apk from the package
job. Then we will upload the apk to app center. Do checkout wzieba/AppCenter-Github-Action@v1
for all the available options for configuring the upload task.
The APP_CENTER_TOKEN
can be generated from the App Center portal. Once you generate the token, add it to the secrets of your github repository.
Once your app is uploaded to App Center, app center will automatically send out an email notification to all the members of AlphaTesters
group as mentioned in the below configuration.
deploy:
needs: [package]
name: Distribut For Alpha Testing
runs-on: ubuntu-latest
steps:
- name: Download latest artifact
uses: actions/download-artifact@v2
with:
name: expense-manager.apk
- name: Upload to app center
uses: wzieba/AppCenter-Github-Action@v1
with:
appName: ajitsing/Expense-Manager
token: ${{ secrets.APP_CENTER_TOKEN }}
group: AlphaTesters
file: expense-manager.apk
notifyTesters: true
debug: true
Functional tests are not covered as part of this post because there are a lot of frameworks available to write functional tests like appium, calabash etc. And you can find a lot of posts covering this specific topic. Still if you want me to cover this topic as well, do let me know in the comments below.
That’s all folks. Thank you!