Automate publishing your Android application with Bitbucket Pipelines and Gradle

This blog is part of 12 days of CI/CD, a celebration of all things CI/CD. Click here for more content and stay up to date by following us on Twitter!


This post was written by Alexander Zhukov, a Senior Software Engineer at SoftServe.


Setting up CI/CD for your Android app lets you automate much of your development process, resulting in higher velocity development with less bugs and defects, and new features getting into the hands of your customers faster.

Bitbucket Pipelines can be used to automatically build and test your Android application, and this guide will show you how to do this through a workflow that includes internal, alpha, beta, and production processes, using a simple demo application (source code available here).

Prerequisites

Before publishing your Android application using Pipelines, you need to do a couple of things:

Bitbucket Pipelines should also be enabled in your repository. Simply navigate to your repository and click on Settings, and under Pipelines click on Settings again, selecting the toggle to enable Pipelines.

Configuring your Pipeline

The pipeline configuration will consist of several steps:

  1. Setup of the build environment.
  2. Building the debug application (debug version) for testing.
  3. Building the application for production.
  4. Running tests.
  5. Publishing to the Internal track.
  6. Promoting Internal to Alpha.
  7. Promoting Alpha to Beta.
  8. Promoting Beta to Production.

The last three steps are triggered manually in order to provide more control over the release process.

Pipelines variables

During the pipeline execution we will use a couple of environment variables that you'll need to setup. Here is a list of variables:

VariableDescription
GOOGLE_API_KEY_JSONThe API key for your google service account with permissions to publish to Google Play.
SIGNING_JKS_FILEBase64 encoded contents of the Java KeyStore file containing your code signing certificate.
SIGNING_KEYSTORE_PASSWORDPassword to your java key store with your signing key
SIGNING_KEY_ALIASThe alias name of the key in your key store
SIGNING_KEY_PASSWORDPassword to the signing key

You'll learn where to get the values for these variables later. You can configure your variables by navigating to Repository settings > Pipelines > Settings > Repository variables.

Building and testing the application

The workflow in this guide uses two parallel steps to build a debug and production an Android Application Package (APK). Add the following snippet to your bitbucket-pipelines.yml file

Tip: Use caching to speed up subsequent execution. In the example below we cache the gradle dependencies.

pipelines:
  branches:
    master:
      - parallel:
        - step:
            name: Build
            image: bitbucketpipelines/android-ci-image
            caches:
              - gradle
            script:
              - echo "$SIGNING_JKS_FILE" | base64 -d > android-signing-keystore.jks
              - ./gradlew assembleRelease
            artifacts:
              - app/build/outputs/**
  
        - step:
            name: Build Debug
            caches:
              - gradle
            image: bitbucketpipelines/android-ci-image
            script:
              - echo "$SIGNING_JKS_FILE" | base64 -d > android-signing-keystore.jks
              - ./gradlew assembleDebug
            artifacts:
            - app/build/outputs/**

Note that we're using a SIGNING_JKS_FILE variable. The value is a base64 encoded content of your Java KeyStore file containing your code signing certificate. You can learn more about Android app signing here.

Tip: Use secured variables for sensitive data like passwords and tokens.

To encode your file you can run the following command and copy the output: base64 -w 0 keystore-file.jks. To generate a self signed code signing certificate, you can use the java keytool:

keytool -genkey -v -keystore keystore-file.jks -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

Notice that in the yaml snippet above we specify which artifacts we would like to create during the step execution. In our case we want to keep our APK files for future testing and publishing in subsequent steps.

Running our unit tests

To run your unit tests using gradle add another step to your bitbucket-pipelines.yml:

      - step:
          name: Test
          image: bitbucketpipelines/android-ci-image
          script:
            - ./gradlew test

Publishing

Note: You should upload the first version of the APK manually via Google Play dashboard. This will connect your Google Play application with the package ID from your build.gradle configuration

Now we’ll publish our release APK to the internal testing track. This is the last step that is triggered automatically, and all other steps will need to be triggered manually to promote the package to the rest of the tracks. Tracks are a way to organize and manage your test and production releases in Google Play. You can read about open, closed and internal tests here.

Tip: Deployments in Bitbucket allow you to view and track deployment results. In the example below, we use the "Internal" deployment environment. Read more about Deployments in our official docs.

      - step:
          name: Publish Internal
          image: bitbucketpipelines/android-ci-image
          deployment: Internal
          script:
            - echo $GOOGLE_API_KEY_JSON > google_play_api_key.json
            - echo "$SIGNING_JKS_FILE" | base64 -d > android-signing-keystore.jks
            - ./gradlew publishReleaseApk

The publishReleaseApk task is available when you enable the Gradle Play Publisher plugin.

You'll need to configure the GOOGLE_API_KEY_JSON variable to use for this and remaining steps. The value of this variable should be the content of the Google API Key json file for the service account that has permissions to publish and promote APKs. Below is a simple instruction how to create a service account and generate an API key json file:

  1. Open the Google Play Console
  2. Click the Settings menu entry, followed by API access
  3. Click the CREATE SERVICE ACCOUNT button
  4. Follow the Google Developers Console link in the dialog, which opens a new tab/window:
    1. Click the CREATE SERVICE ACCOUNT button at the top of the Google Developers Console
    2. Provide a Service account name
    3. Click Select a role and choose Service Accounts > Service Account User
    4. Check the Furnish a new private key checkbox
    5. Make sure JSON is selected as the Key type
    6. Click SAVE to close the dialog
    7. Make a note of the file name of the JSON file downloaded to your computer
  5. Back on the Google Play Console, click DONE to close the dialog
  6. Click on Grant Access for the newly added service account
  7. Choose Release Manager (or alternatively Project Lead) from the Role dropdown. (Note that choosing Release Manager grants access to the production track and all other tracks. Choosing Project Lead grants access to update all tracks except the production track.)
  8. Click ADD USER to close the dialog

Promoting using Deployments

The last three steps are manually triggered steps to promote to the rest of the Google Play tracks, including Alpha, Beta and Production. You can also configure multiple options, such as rollout fractions.

      - step:
          name: Promote Internal to Alpha
          image: bitbucketpipelines/android-ci-image
          deployment: Alpha
          trigger: manual
          script:
            - echo $GOOGLE_API_KEY_JSON > google_play_api_key.json
            - ./gradlew promoteArtifact --track alpha

      - step:
          name: Promote Alpha to Beta
          trigger: manual
          image: bitbucketpipelines/android-ci-image
          deployment: Beta
          script:
            - echo $GOOGLE_API_KEY_JSON > google_play_api_key.json
            - ./gradlew promoteArtifact --track beta

      - step:
          name: Promote Beta to Production
          trigger: manual
          image: bitbucketpipelines/android-ci-image
          deployment: Production
          script:
            - echo $GOOGLE_API_KEY_JSON > google_play_api_key.json
            - ./gradlew promoteArtifact --track production

And with that you now have an easy workflow that lets you build, test, and deploy your Android app to the Google Play Store using Bitbucket Pipelines and Deployments!