Automate Android Deploys To Play Store

In this post, we will outline and share the automated shipping process we use to build the Ever Android app. We’ll outline how we use Circle CI to automatically increment version, build the app, ship to the Play Store, and more.

Shipping Android apps could not be any easier.

  1. Build your production flavor with the proper key (the key is created once, so not adding it to this list).
  2. Upload to Play Store.
  3. Become rich!!!

However, things start getting complicated when you want to throw in CI, post release notes somewhere, tag releases in GitHub, etc. Next thing you know, you’re spending 30 minutes shipping the greatest app in the world to the Play Store. Now… even that is not that bad. If you ship once a month or rarely, 30 minutes is not a thing.

But, if you’re writing apps for a living, you probably ship pretty regularly. At Ever we ship weekly on Thursdays. You should also care about things like CI, checking builds on real phones (pre-L, L, M+), tagging releases, etc. You’ll probably quickly outgrow this:


Ever Android shipping steps

  1. We merge beta (our dev branch) into master.

Done, that’s it. This is what happens when we do:

  1. Unit and Integration tests are run.
  2. Version number is incremented.
  3. Various flavors are built.
  4. Said flavors are uploaded to Fabric Beta so they are available to the team.
  5. Play Store release notes are copied to a temp file.
  6. APK is uploaded to the Play Store.
  7. We post to a Slack channel to let the team know what was shipped with the release notes using a Slack webhook
  8. We commit our changes to master.
  9. We tag the release in GitHub.

Here is our circle.yml file. Ignore most of it. We’ll do another post soon on our testing infrastructure. However, take a look at the deployment section in the bottom. We’ll now go into detail on these steps.

Ever Android circle.yml
- ./gradlew incrementVersion

This is a gradle task that we use to increment the version number. It looks like this:

This increments our version code (in format of MAJOR.MINOR.PATCH)from something like 3.5.1 to 3.5.2. The patch version is incremented until it reaches 15, at which point the minor version will get incremented. The minor version will get incremented until it reaches 10. We persist this number in the app/gradle.properties file.

Of course, sometimes, we would want to skip some minor and patch versions and go directly to a new major release (i.e. 1.4.1 to 2.0.0). We can easily change the version number in the app/gradle.properties file to 1.10.15, which will get incremented to 2.0.0 in the next release.

- ./gradlew assembleStagingRelease assembleBetaRelease assembleProductionRelease

At Ever, we have 3 app flavors. Staging points to our dev environment. Beta points to our prod environment, but it has a different package, so we can run Prod and Beta on the same phone.

- ./gradlew crashlyticsUploadDistributionStagingRelease crashlyticsUploadDistributionBetaRelease crashlyticsUploadDistributionProductionRelease

These are the gradle plugin commands for Fabric Beta. They upload our app to be distributed to testers.

- ./gradlew :mobile:publishApkProductionRelease

This is an open source gradle command that uploads your app to the Play Store. Using it, you can control which channel to update your app into, and what percent rollout to publish it with.

- curl -X POST — data-urlencode “payload={\”text\”:\”New Android Release v$VERSION_NUMBER in the Play Store at $ROLLOUT_PERC%.\n\n $(cat fabric_beta_release_notes.txt)\”}” <Slack Webhook>

This command posts a new message to our #releases Slack channel so that the rest of the company knows when things are shipped, using a slack webhook. We post it with the release notes we send to Fabric Beta. These are in fabric_beta_release_notes.txt.

- git commit -am "Bump to $VERSION_NUMBER [skip ci]"
- git push origin master

We now commit the new version number. Notice the [skip ci] at the end. This is to prevent this commit kicking off another CI build. Otherwise, you will get into an infinite loop. Another way around this is to change your Circle CI settings to only build branches from PRs.

- echo “export API_JSON=\”{\”tag_name\”:\”v$VERSION_NUMBER\”,\”target_commitish\”:\”master\”,\”name\”:\”v$VERSION_NUMBER\”,\”body\”:\”Release of version v$VERSION_NUMBER\”,\”draft\”:false,\”prerelease\”:false}\”” >> ~/.bashrc
- curl — data “$GIT_HUB_RELEASE_JSON” https://api.github.com/repos/everalbum/android/releases?access_token=<some token>

Lastly, we tag a draft in GitHub. This way we have a record of what was shipped and the release notes.

I hope this will inspire you to automate more processes. As is customary, I feel obliged to mention that Ever is always hiring! Send me a message if interested: ralph@ever.com.