Continuous Delivery of Android Apps with CircleCI 1: Tutorial

Jacques Smuts
Dec 21, 2018 · 4 min read

You have a growing Android Project and you need to implement more DevOps tools. You know this, but how easy is it to deploy to Google Play every time you push a change? Is it even worth the effort? If you have a CircleCI (or Travis or similar) setup, it’s actually quite easy. Unless you already have an internal distribution system in place, Google Play is a good choice since it handles auto-updates easily. Figuring out all the steps to deploy to Google Play can be tedious though, so I’ve compiled a list of steps you can follow.

Image for post
Image for post
Continuous Delivery adds more steps, but ensures a more stable product

Some assumptions:

Step 1: Signing your release build

First, we’ll generate a Base64 encoded keystore file as described here . Go to terminal, navigate to your keystore and run the following command:

$ cat path/to/yourprivatekey.jks | base64

This outputs some text that you save as an Environment Variable in your CircleCI Project Settings. Save that output under `ENCODED_KEYSTORE`

Also save your Keystore’s Alias and Passwords as normal text if you want.

Image for post
Image for post
Your Project Settings. DEPLOYMENT_PRIVATE_KEY will be relevant later.

Then, in your config.yml file, before the ‘download dependencies’ step, add the following lines

- run: echo $ENCODED_KEYSTORE | base64 --decode >> ${HOME}/keystore.jks
- run: echo ‘export KEYSTORE=${HOME}/keystore.jks’ >> $BASH_ENV

This decodes the keystore and makes it available to the build environment.

Finally, we set up the signing of the app in gradle. Add this to your app-level build.gradle file:

android {
signingConfigs {

release {
storeFile file(System.getenv("KEYSTORE") ?: "keystore.jks")
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEYSTORE_PASSWORD")

buildTypes {
release {
signingConfig signingConfigs.release

Now your app should be signed by your CI environment. Please be aware that if your CI logs are available to the public (such as in an open source project) then this provides a point of attack for obtaining your keystore.jks file.

Step 2: Deploying your release build

Image for post
Image for post
The real hero

This step is a bit more involved. We’ll be following the instructions given at the Gradle Play Publisher Repo, so we need to set up a Google Service Account. This service account allows for API calls to be made to your Google Play/Cloud Project, such as uploading APKs.

First, go to Play Console API Access, click Create Service Account and follow those instructions. Be sure to generate and download a private key and store it in a safe space.

Next, upload the Base64 Encoded private key to the CI Project Environment Variables as before. Type the following into your terminal.

$ cat path/to/your_private_key.json | base64

save the output as ‘DEPLOYMENT_PRIVATE_KEY’ in your environment variables and add the following lines to your config.yml file:

- run: echo $DEPLOYMENT_PRIVATE_KEY | base64 --decode >> ${HOME}/deployment_private_key.json
- run: echo ‘export PRIVATE_KEY=${HOME}/deployment_private_key.json’ >> $BASH_ENV

As before, this makes the private key available to the build environment. Next, we set up Gradle-Play-Publisher: In your app-level gradle file, replace the “apply plugin: ‘’” line with the plugin {} DSL so that your gradle file looks like this:

plugins {
id ''
id '' version '2.0.0'
android {
// Configuration for deployment
play {
track = 'internal' // or alpha or beta...
serviceAccountCredentials = file(System.getenv("PRIVATE_KEY") ?: "private_key.json")
defaultToAppBundles = true // Use App Bundle instead of APK
resolutionStrategy = "ignore" //If the deployment fails due to an API error, don't fail the CI Build

And finally, we tell the CI Environment to deploy to Google Play. Add the publish task as the last step in your config.yml file.

- run: 
name: Upload to Google Play
command: ./gradlew publishRelease

If your setup is working correctly, CircleCI will publish a build to Google Play every time it runs. You don’t necessarily want that, so you need to do branch management, version code management and/or set up different workflows to make sure the deployment happens when and how you want it. Do you want Continuous Delivery or full-on Continuous Deployment? That will be covered in part 2 of this post.

For a PR that contains all of the above code, see this link.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store