Using Buildkite for CI/CD on Android

For the past few weeks I have been trying out Buildkite, specifically for Android. There are not many resources on the web on how to do this, so hopefully this will be of some use to some Android developers out there. I will be discussing the pros and cons of using Buildkite, and will show a sample build script for Android.

Why Buildkite?

First of all, why Buildkite? Why not some of the more popular solutions like Jenkins or CircleCI? First we take a look at what CI/CD is and how it is run:

Fundamentally, doing CI/CD is just having one or more machines execute a bunch of command-line commands automatically - there's no magic or anything to it. Normally a master - slave method is used to achieve this: The master server takes the commands you want to run and delegates each of them to different slave machines to execute (or executes them itself) for efficiency. Let's break down two of the popular types of services I have seen on the internet:

  1. Server - For some services like Jenkins, you need to set up both the master server and all the slaves by yourself. This means installing Jenkins on one machine (which will be the master), and all the necessary stuff like Git, Java, Android SDK, etc on the master and slaves. You will also be responsible for keeping the versions updated, security, device space and anything and everything that could go wrong on each of the devies (and believe me, some weird stuff happens from time to time)

  2. Cloud - On the other hand, services like Circle CI (cloud version) give you CI out of the box (or circle). All you have to do it register an account, point it to your Github repository, provide it a build script and it takes care of all the downloading, delegating, and executing for you. No more maintaining or worrying about any of your machines.

Builkite is somehow in the middle of these two. Essentially the master is in the cloud and the slaves (or "agents" in their terms) are provided by you. You register an account in Buildkite and they give you access to their dashboard wherein you can control your agents and projects.

With this type of infrastructure, you get full control over the machines that will be running the builds, therefore you can make it as powerful or as optimized as it can be. If you have a spare machine lying around, you can convert it to be your build slave. You also don't have to worry about the security and reliability of the master server (which is the most important component here) because Buildkite handles it for you.

Getting started

You can dive right in by registering here and creating an organization for yourself.

Registering your agents

First of all, you have to have a machine and/or machines that will act as your agents. Any machine will do as long as it can build Android projects. Buildkite provides a more in depth explanation as well as a guide to set up an agent here.

Creating your project

I won't get into details on how to setup Buildkite, as their website does an awesome job at guiding newcomers to do that. Besides, All types of projects are set up similarly from scratch. You can set up a new pipeline for your project here

CI commands

After setting up the project and pointing it to the correct repo in Github, we create a buildkite.yml file inside our project with the steps for our agent to run. More info on how to populate the file here. After pushing it to Github, a new build should be automatically triggered via the webhook (if you set it up). You can also run it by clicking the build button.

steps:
  - label: ":hammer: Building Project"
    command: "./gradlew clean assembleDevelopmentDebug"
    artifact_paths: "app/build/outputs/apk/development/debug/*.apk"

  - label: "Running unit tests"
    command:
      - './gradlew testDevelopmentDebugUnitTest'
      - 'zip -r app/build/reports/test-results.zip app/build/reports/tests'
    artifact_paths:
      - "app/build/reports/test-results.zip"
      - "app/build/test-results/testDevelopmentDebugUnitTest/*.xml"

Explanation:
1. Build step (lines 1-4):

* ./gradlew clean assembleDevelopmentDebug - builds the project (development flavor, debug variant)
* artifact_paths: "app/build/outputs/apk/development/debug/*.apk" - uploads the built apk to Buildkite's dashboard
2. Running unit test (lines 6 onwards)
* ./gradlew testDevelopmentDebug - runs unit tests
* zip -r app/build/reports/test-results.zip app/build/reports/tests - zips the results ran by the previous command into one file (for easier uploading)
* artifact_paths: ... - uploads the zipped test results, as well as the xml test reports, to the Buildkite dashboard.

note: the files uploaded by the artifact_paths step can be downloaded in the Buildkite dashboard by clicking the "Artifacts" tab of that specific step:

screen shot 2019-01-15 at 4 13 16 pm

Conclusion

The build script above barely scratches the surface of what Buildkite can do. Like other CI systems, you can also have it automatically upload to and release your .apks, do code quality checks, integrate with other services, and so much more.

Also note that choosing a CI/CD system still depends on your budget, company size, and other specific needs, so Buildkite may or may not be the right tool for you. I have given you the facts here, but it is still up to you to judge.