How to set up APK release CI/CD on Github

Intro

This article documents the process for integration of the android client release process to the CI for automation i.e CI/CD on Github. The repository set up requires admin rights to set up the credentials for the Github Actions CI pipeline.

This set up supports tagging releases on Github as pre-releases if they include alpha or beta keyword in the tag e.g. v2.3.5-alpha

Contents

  • Gradle Configuration

  • Github Actions CI

  • Credentials configuration

  • Publishing via Tag

  • Signed Release for APK releases

Gradle configuration

Your applications build configuration should have the various properties configured to sign the release. One can use the alternative configuration with the credentials stored in a keystore.properties file.

The following gradle configuration combines both system environment variables and keystore properties file approach where the latter is only used if the environment variables are empty

apply from: '../properties.gradle' signingConfigs { release { v1SigningEnabled false v2SigningEnabled true keyAlias System.getenv("KEYSTORE_ALIAS")?: project.KEYSTORE_ALIAS keyPassword System.getenv("KEY_PASSWORD") ?: project.KEY_PASSWORD storePassword System.getenv("KEYSTORE_PASSWORD") ?: project.KEYSTORE_PASSWORD storeFile file(System.getProperty("user.home") + "/<project>.keystore.jks") }


It is recommended to have a separate properties.gradle file to process the key store properties then import in the above build.gradle file using apply from: '../properties.gradle' as shown above.

ext.props = new Properties() //KEYSTORE CREDENTIALS def keystoreAuthArray = ["KEYSTORE_ALIAS", "KEY_PASSWORD", "KEYSTORE_PASSWORD"] if (rootProject.file("keystore.properties").exists()) { props.load(rootProject.file("keystore.properties").newDataInputStream()) keystoreAuthArray.each { arrayProp -> project.ext.set(arrayProp, props.getProperty(arrayProp, "sample")) }} else { println("keystore.properties does not exist. The following values are required " + keystoreAuthArray.join(", ")) keystoreAuthArray.each { arrayProp -> project.ext.set(arrayProp, props.getProperty(arrayProp, "sample_" + arrayProp)) }}

Sample content of keystore.properties file

KEYSTORE_PASSWORD=xxxxxx KEYSTORE_ALIAS=xxxxxx KEY_PASSWORD=xxxxxx

NB: When using this approach to store credentials please remember to add a `keystore.properties` entry to the `.gitignore` file to prevent versioning on git

Github Actions CI

You can create a new Github action workflow file and name it release.yml with the following sample configuration used for building, signing and publishing the OpenSRP ZEIR application as a Github release.

NOTE: Remember to update the content with the module names corresponding to your project.

Credentials generation and configuration

  • Android requires that all release APKs be digitally signed with a certificate before they are installed on a device or updated. This requires that you generated a Keystore. To do this on Android Studio, you can follow the instructions here.

  • Alternatively if you have the keytool utility on your workstation you can generate one using the command `keytool -genkey -v -keystore my_keystore.jks -alias <my_alias_name> -keyalg RSA -keysize 4096 -validity 1000`

  • When generating the Keystore above you will be prompted to enter the KEYSTORE_ALIAS, KEYSTORE_PASSWORD, KEY_PASSWORD. Please note, if your platform doesn't prompt you for a second password when generating the Keystore (e.g. of type PKCS12) then the KEYSTORE_PASSWORD and KEY_PASSWORD values will be the same.

  • You need to then store the above credentials in your repository of choice. The values need to correspond to the exact naming on your Github actions workflow config file. For more on setting up repository secrets see this link.

  • You need to store the base64 encoded versions of the content of these files:

    • my_keystore.jks keystore file

    • local.properties file. (If relevant to your project)

    • keystore.properties file. (If using Example 2 approach above under Gradle Configuration)

  • The above base64 content needs to be stored on Github under the corresponding variable names. This name should correspond to what is on the Github Actions workflow config file e.g.

    • my_keystore.jks - KEYSTORE_FILE

    • local.properties - LOCAL_PROPERTIES

    • keystore.properties - KEYSTORE_PROPERTIES (required if using Example 2 approach)

    • google-services.json - FIREBASE_CONFIG (Do not encode these into Base64)

  • You can convert to Base64 using the following command that uses the openssl utility openssl base64 < my_keystore.jks | tr -d '\n' | tee my_keystore_base64_encoded.txt

    NB: You need admin rights on the repo to access Settings menu

Publishing via TAG

As part of integrating Continuous Delivery(CD) into the development lifecycle, CI has been set up to trigger release APK generation. The configuration requires the tag to have a prefix in the glob pattern format:

v[0-9]+.[0-9]+.[0-9]+[0-9a-zA-Z.-]+

Thus the following are all valid tags that will trigger the generation of a release APK

  • v1.4.0

  • v1.4.0-alpha

  • v1.4.0-zeir-beta

  • v1.4.0-anc-rc1


Note: e.g. when creating a tag for the zeir version 1.4.0, use the command:

git tag -a v1.4.0-zeir -s && git push origin v1.4.0-zeir

When you run the command, you will be prompted to add a message. The message should be of the format:

Template

Sample

Template

Sample

TITLE
- Release note 1
- Release note 2

BETA RELEASE
- Adds Login by PIN functionality
- Fixes sync bug causing crash on install

NOTE: For convention, the TITLE should be Capitalized. The release notes should show to what was updated.


Also please see Semantic versioning

Accessing your releases

Once the above command is executed and the tag is pushed, Github CI triggers an action to build the signed release APK and upload it as part of the tag’s assets.

One can access the published APK under Github’s releases url e.g. for ZEIR

https://github.com/opensrp/opensrp-client-path-zeir