Continuous Delivery of Android with CircleCI 2: Branch to Track

In Part 1, I explained how to get your CircleCI setup to automatically publish to Google Play. In this post I discuss using Gradle and Git Branching to ensure that the CI/CD system delivers the latest, most stable version to the correct target.

Delivery Cycle

First, you need to determine your QA/delivery cycle. For the sake of argument, I’m assuming you have a developer/QA team to test internally, you have a stakeholder to give final approval on releases, and you have users. You probably also have sprints or semi-determined release dates.

Developer, QA, Stakeholder and Users

Version Conflicts

If you followed part 1, you’ll notice that the builds don’t always update on Google Play. The reason for that is because Google Play rejects APKs unless the versionCode is higher than the previous upload. What you want to do is to automatically increment the versionCode somehow. There are good ways to do that using Gradle and commit numbers, but if you’re already running a CI the easiest way is to simply use the CI’s build number. In your app-level build.gradle file, make the following adjustment:

CIRCLE_BUILD_NUM is a build number automatically determined by CircleCI (or replace with your specific CI), and 123 is an arbitrary number to make sure your versionCode is the minimum number you want. The versionName does not matter to Google Play.

Be aware that with auto-incrementing builds, every single commit/PR that builds on the CI will end up deploying to Google Play on the same track. So to fix that we need to make our deployment track dependant on our branch.

Git Branches and you

You most likely already have a Git Branching strategy in place. If not, choose one. For our purposes, the differences in the branching strategies are minor. The important part is that your whole team understands and follows the same branching strategy, so that you can design the CI/CD around that.

GitFlow: A popular choice, though some would argue not the best for CI

Once you’ve decided on your branching strategy, set up your app’s build.gradle file to deploy to a track based on the branch, like so:

This means that:

  • Master Branch deploys to Beta, then it’s in the stakeholder’s hands.
  • Release Branch deploys to closed Alpha track. Then it’s in QA’s hands.
  • All other branches deploy to internal track. This is for internal testing.

This setup also means that no one except the stakeholder requires access to the Google Play Console (in order to promote the Beta app to Production). It’s always good practice to minimise the number of people with administrative access; luckily CI/CD makes this easy.

You may notice at this point that you will have a lot of overlapping branches overwriting each-other on the internal track. So effectively, the internal track is useless for now. That will be fixed in part 3 of this series.

For reference, here is a Pull Request which contains all of the changes I described above.

Flavors

If you have two different Gradle Flavors pointing to different endpoints, the setup we implemented would be useful, since the internal+Alpha tracks could point to the develop/staging endpoint and Beta could point to the live endpoint.

But if you have different flavors, how do you choose which one to build? And similarly, how do you make sure that only the right branches are deployed, but all the minor commits are at least compiled? For that, you need to customise your config.yml file and set up workflows based on your branches. To find out how to do that, you can read Part 3 of this series, wherein I provide a working example of a repo which builds different flavors and deploys only accepted builds to the correct track.

Continuous Delivery, or Continuous Deployment?

The difference between Continuous Deployment and Delivery is fairly small, mostly coming down to the level of human intervention vs automation. Many Android app developers don’t ever use continuous deployment, opting instead for continuous delivery due to the interlinked nature of iOS/Android/Backend development of any one product.

Depending on your own setup, the simplest solution may be to deploy to live only by promoting an app’s track on the Google Play Console. If you don’t want to require anyone to sign into Google Play Console, you can deploy to live from the Master Branch or run the “promoteReleaseArtifact” gradle task and add a human intervention step to Circle CI fairly easily.

Be careful of using Continuous Deployment in Android; it’s possible to be too continuous and put your product’s stability at risk. CI/CD should be seen not as a way to deliver the product as fast as possible, but as a way to deliver the most up to date product as reliably as possible.

Part 3 and final in this series, here.