You probably use Github to manage your Android project. We all know that manually releasing can be very tedious and boring. From running some tests, generating the APK or AAB, signing it, entering the AppGallery Console, filling in information, uploading it, etc.

With this series of guides we will automate the release process in the simplest way, doing some things at a low level to understand them using the Publising API to publish our APK or AAB to AppGallery. But first we will take all the precautions to manage some secret files such as the keystore to sign the application and the agconnect-services.json file. Of course we will do it for a project that uses the Huawei Mobile Services and the considerations that must be taken.

GitHub Actions

GitHub Actions allows us to automate our development workflow and of course create our CI/CD for our Android project.

Our first step will be to have our Android project on GitHub, then in the repository go to the Actions section.

Github has several quick start templates for different processes in different technologies, to have a base code we can select the Android CI template.

This will generate a YAML file where we will write everything we want to do when a modification is made in the master branch either from a commit or a pull request approval.

This template can already be executed except for a modification to allow gradlew execution. This is necessary to avoid execute permission problems.

chmod +x gradlew

Our base YAML code would be the following:

name: Android CI   
on:   
 push:   
   branches: [ master ]   
 pull_request:   
   branches: [ master ]   
jobs:   
 build:   
   runs-on: ubuntu-latest   
   steps:   
   - uses: actions/checkout@v2   
   - name: set up JDK 1.8   
     uses: actions/setup-java@v1   
     with:   
       java-version: 1.8   
    - name: Build with Gradle   
     run: |   
        chmod +x gradlew   
        ./gradlew build   

Any change made in the YAML file will allow us to execute the workflow, it can also be executed manually and obviously see the entire process log.

Securing sensitive data and files

If we want our workflow to generate an APK or AAB signed and ready to upload to AppGallery we need some files, but those files have to be safe and they should not be versioned in the repository. They should even be placed in .gitignore to avoid uploading them by mistake.

Most developers in most cases recommend to be careful with the following files in an Android project that uses Huawei Mobile Services.

  • The keystore file
  • The keystore password and keystore alias password values
  • The agconnect-services.json file

We need this information but it cannot be visible in the YAML file because it is visible to those who have access to the repository. GitHub has a special space for this sensitive information, Secrets.

The problem here is that only key-values can be uploaded and not files.

To upload our keystore file (it is necessary for the application signature) we will obtain the string representation of it, the same with the agconnect-services.json configuration file.

We will do this by command line with gpg.

gpg -c --armor mykeystore.jks

This will ask for a passphrase that we must enter and will generate a mykeystore.jks.asc file. That file contains a Base64 string and that value is the one that we can save in Secrets.

We must do this for each file that we consider sensible and that is necessary in our workflow. We must also save the passphrase of each one of them because we will use it to generate the file again from the base64 value when our CI/CD is executed.

We will get something like this: (considering we put different passphrases, it could be just one)

Note that we have a keystore.properties file where we have the values of the passwords and alias of the keystore.

Well, the idea is to use these values in the workflow to generate the signed APK.

We are going to create a new step in our YAML and do the reverse that we did. First we generate the .asc file, then with the passphrase generate the final file in its corresponding directory.

- name: Prepare Secret Files   
  run: |   
     echo "${{ secrets.KEYSTORE }}" > alvareztech.jks.asc   
     gpg -d --passphrase "${{ secrets.KEYSTORE_PASSPHRASE }}" --batch alvareztech.jks.asc > app/alvareztech.jks   
     echo "${{ secrets.KEYSTORE_PROPERTIES }}" > keystore.properties.asc   
     gpg -d --passphrase "${{ secrets.KEYSTORE_PROPERTIES_PASSPHRASE }}" --batch keystore.properties.asc > keystore.properties   
     echo "${{ secrets.AGCONNECT_SERVICES }}" > agconnect-services.json.asc    
     gpg -d --passphrase "${{ secrets.AGCONNECT_SERVICES_PASSPHRASE }}" --batch agconnect-services.json.asc > app/agconnect-services.json   

Generating the signed application

Generating the APK is easy with a gradle command after having all the necessary files and having the signature configuration in the same build.gradle of the app module, as follows.

signingConfigs {
    release {
        storeFile file(keystoreProperties['storeFile'])
        storePassword keystoreProperties['storePassword']
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        v2SigningEnabled true
    }
}

So generating the signed APK is only:

./gradlew assembleRelease

Get signed APK file

For now we will download the result file of the workflow, the idea is to upload this file to AppGallery through the Publising API provided by AppGallery Connect, but now we will settle by downloading this artifact.

- name: Publish APK   
  uses: actions/upload-artifact@v2   
  with:   
    name: app-release   
    path: app/build/outputs/apk/release/

What we have is a workflow that generates a signed APK for change that we make in the master branch automatically. We can download the artifact, for now Github only allows download in ZIP format.

Conclusion

We have everything ready to start using Publish API, we already take considerations for sensitive files in a project with Huawei Mobile Services. The goal is to continue putting our CI/CD together a bit on a low level to understand the whole process.

The complete file so far is:

name: Android CI    
on:    
 push:    
   branches: [ master ]    
 pull_request:    
   branches: [ master ]    
jobs:    
 build:    
   runs-on: ubuntu-latest    
   steps:    
   - uses: actions/checkout@v2    
   - name: set up JDK 1.8    
     uses: actions/setup-java@v1    
     with:    
       java-version: 1.8    
   - name: Prepare Secret Files    
     run: |    
        echo "${{ secrets.KEYSTORE }}" > alvareztech.jks.asc    
        gpg -d --passphrase "${{ secrets.KEYSTORE_PASSPHRASE }}" --batch alvareztech.jks.asc > app/alvareztech.jks    
        echo "${{ secrets.KEYSTORE_PROPERTIES }}" > keystore.properties.asc    
        gpg -d --passphrase "${{ secrets.KEYSTORE_PROPERTIES_PASSPHRASE }}" --batch keystore.properties.asc > keystore.properties    
        echo "${{ secrets.AGCONNECT_SERVICES }}" > agconnect-services.json.asc     
        gpg -d --passphrase "${{ secrets.AGCONNECT_SERVICES_PASSPHRASE }}" --batch agconnect-services.json.asc > app/agconnect-services.json    
    - name: Build with Gradle    
     run: |    
        chmod +x gradlew    
        ./gradlew assembleRelease    
    - name: Publish APK    
     uses: actions/upload-artifact@v2    
     with:    
       name: app-release    
       path: app/build/outputs/apk/release/    

Implemented in a real project can be seen in:

alvareztech/facts-android
A utility application to obtain the device and OS information. - alvareztech/facts-android

Publising API

https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agcapi-publish_api_overview


You can also see this tutorial in the HUAWEI Developer Forum:

HUAWEI Developer Forum - HUAWEI Developer