Automating the publication of Android applications in Google Play and Huawei AppGallery — from A to Z

Automating the publication of Android applications in Google Play and Huawei AppGallery — from A to Z


This article has been translated from its original publication at https://habr.com/ru/articles/732550/

In this guide, we will explain how to automate the release process of Android applications on Google Play and Huawei AppGallery (currently excluding RuStore). You will forget how to do it manually forever and be able to spend your time on something more useful, saving hundreds of hours per year.

Table of Contents

  • Gradle Project Structure 
  • Android App Bundles (AAB) vs Android Packages (APK) 
  • Version Number Generation 
  • Obtaining Access Keys from Google Play 
  • Uploading Builds to Google Play 
  • Managing Metadata on Google Play 
  • Obtaining Access Keys from Huawei AppGallery 
  • Uploading Builds and Release Notes to Huawei AppGallery 
  • Adding CI/CD 
  • Conclusion

Gradle Project Structure 

Let's start by organizing the Android project itself. In the project's build.gradle, we use a fairly standard matrix of flavors x buildTypes: 

android {
  // <REDACTED>

  flavorDimensions 'default'

  productFlavors {
    google {
      dimension "default"
      versionName = android.defaultConfig.versionName + '-Google'
    }

    huawei {
      dimension "default"
      versionName = android.defaultConfig.versionName + '-Huawei'
    }
  }

  // <REDACTED>

  buildTypes {
    debug {
      applicationIdSuffix '.debug'
      versionNameSuffix '-debug'
      signingConfig signingConfigs.debug
      // <REDACTED>
    }
    beta {
      applicationIdSuffix '.beta'
      versionNameSuffix '-beta'
      signingConfig signingConfigs.release
      // <REDACTED>
    }
    release {
      signingConfig signingConfigs.release
      minifyEnabled true
      shrinkResources true
      // <REDACTED>
    }
}

This matrix provides combinations like GoogleDebug, GoogleBeta, GoogleRelease, HuaweiDebug, and so on. Customization based on stores is not mandatory, but it's useful for managing special features such as in-app. Customize the matrix according to your needs.

Useful links:

Android App Bundles (AAB) vs Android Packages (APK) 

Since the inception of Android, all applications have been distributed as .apk files, which are essentially ZIP archives containing compiled code and resources for different architectures, localizations, and API levels. You compile your code and resources, package them into an archive, and sign it with your signature. Then you can distribute it through Google Play, RuStore, or simply upload it to a website.

The situation changed in 2021. Google decided it would be good to send only the resources that correspond to the application's API level and architecture to clients on Google Play. After all, why send armeabi-v7a libraries for arm64-v8a devices? Why would devices with API level = 31 need compatibility files for API level = 21? Why include localizations in 30 languages when there's only one in the system? Overall, the idea is good, but there are nuances.

To make all this work, Google introduced a new intermediate format called Android App Bundles (.aab). Similar to .apk files, you compile and sign .aab files with your own key. However, .aab files cannot be directly installed on devices. Google Play creates an .apk file with the necessary resources from the .aab file for each specific device and sends it to the device. Instead of downloading a 50 MB .apk file, you download a targeted 20 MB .apk file specifically built for your device. Profit!

Important! When using Android App Bundles, the final build and APK signing will be handled by Google (and now Huawei as well). Each app store uses its own keys for signing. Google has its keys, Huawei has its keys. Android operates in such a way that installing a new version of an application signed with a different key is not possible without uninstalling the previous version along with all its data. If your users install a bundle application from Google Play, they won't be able to update it through Huawei.

The classic approach with APKs does not have this problem. However, starting from August 2021, you no longer have a choice, and all new applications must be uploaded in the .aab format, leaving the signing process to Google Play. Now you can forget about split APKs, APK expansion files, and other workarounds. Users won't have to download unnecessary megabytes over fast EDGE^W5G internet. More free space on the device! And ultimately, there's almost no possibility to switch from Google Play to alternative stores for such applications. Win-win-win.

Useful links:

Android App Bundles

Version Number Generation 

The first and one of the most important tasks to accomplish for implementing continuous deployment, whether for an online service or package software, is to ensure a deterministic version number generation algorithm. The requirement is simple: there should be a unique version number for each git commit in the release branch. Stop manually bumping versions (1.0.2 -> 1.0.3 -> 1.0.4, and so on) every time. You need a system where the version number (at least its latest .patch-level component) is generated automatically.

There are two good approaches that allow you to solve this issue once and for all.

Approach 1: Using git describe

One characteristic of Android (and Google Play and AppGallery, respectively) is that downgrading application versions is not possible. The version number should always increase. However, the git history of the release branch also always grows if you're not messing around with history rewrites and force-pushes.

It makes sense to use the number of git commits in the branch for numbering. For example, you can periodically create an annotated tag like v$major.$minor (e.g., v1.0, v2.0), while the third component (patch) is generated as the number of commits from the previous tag.

To implement this, it's most convenient to use the command git describe --long:

This approach provides a valid semver v1.0.0, v1.0.1, v1.0.2, and so on, where the last digit of the version (patch) is assigned automatically, while the major.minor is controlled by you. Each git commit will have a unique version number. Finding a git commit based on a tag and the number of commits from it is also straightforward. Simple and practical.

Approach 2. Using Dates 

A more radical approach is to use the date of the git commit as the version. For example, 2023.04.23-2. But what about semver, you may ask? Well, why do you need it in the world of Android applications when you have continuous app updates from Google Play and no way to roll back or provide multiple versions? You can argue about this in the comments if you wish.

In git, there are two typeds of dates:

  • Author Date: What the author specified and what you can see in git log and git show. It can be as far in the past as you like, especially if you merge some year-old pull request.
  • Commit Date: The date when the commit was added to the branch. It usually corresponds to the actual time when the merge happened in the branch. Time always moves forward, and Commit Date is no exception unless you intentionally experiment with your clock settings.

We will use the Commit Date, which can be obtained using the %cd formatting in git log:

2023-04-23 10:37:01 +0000

2023-04-23 09:02:18 +0000

2023-04-19 14:43:21 +0000

2023-04-19 14:43:21 +0000

2023-04-19 01:39:03 +0000

<REDACTED>

One date is not enough unless you want to limit yourself to one commit per day. As an additional number, we will consider the number of commits since the beginning of the day. We will get something like 2023.04.23-1, 2023.04.23-3, 2023.04.23-4, and so on. You can also take the hour multiplied by 60 plus minutes or simply the hour (up to you) as the last digit. But the option with the number of commits since the beginning of the day seemed to be a practical choice for us.

Don't forget that ultimately all of this needs to be inserted into the versionCode, which is limited to the range 0-2_100_000_000 (slightly less than 31 bits). Version Code is what Android and stores use for versioning, while Version Name is simply a text string where you can put anything you want to show the user.

To generate the version, we will use the following code in Gradle (pay attention to the comments):

def getVersion() {
  def time = Integer.parseInt(run(['git', 'log', '-1', '--format=%ct']).trim())
  def cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ENGLISH)
  cal.setTimeInMillis((long) time * 1000)
  def year = cal.get(Calendar.YEAR)
  def month = cal.get(Calendar.MONTH) + 1
  def day = cal.get(Calendar.DAY_OF_MONTH)
  def date_of_last_commit = String.format("%04d-%02d-%02d", year, month, day)
  def build = Integer.parseInt(run(['git', 'rev-list', '--count', '--after="' + date_of_last_commit + 'T00:00:00Z"', 'HEAD']).trim())

  // Use the last git commit date to generate the version code:
  // RR_yy_MM_dd_CC
  // - RR - reserved to identify special markets, max value is 21.
  // - yy - year
  // - MM - month
  // - dd - day
  // - CC - the number of commits from the current day
  // 21_00_00_00_00 is the the greatest value Google Play allows for versionCode.
  // See https://developer.android.com/studio/publish/versioning for details.
  def versionCode = (year - 2000) * 1_00_00_00 + month * 1_00_00 + day * 1_00 + build

  // Use the current date to generate the version name:
  // 2021.04.11-12-Google (-Google added by flavor)
  def versionName = String.format("%04d.%02d.%02d-%d", year, month, day, build)

  return new Tuple2(versionCode, versionName)
 }

Next, set the versionCode and versionName from the result of calling getVersion(): 

You can ensure that each Gradle run will generate a unique version number for each git commit. This approach solves the versioning issue once and for all, so you don't have to worry about it.

Obtaining Access Keys from Google Play 

To automate uploading to Google Play, you need to first obtain access keys for the API. It seems that Google Play has tightly integrated with IAM Google Cloud, and you need to create a new Service Account with the necessary permissions and obtain a .json file with credentials. But let's go step by step.

Step 1: Linking Google Play Developer Account to Google Cloud Project

It seems that new Google Play Developer Accounts need to be linked to Google Cloud in a somewhat semi-magical way, especially if you have used Firebase.

Go to Google Play Console -> Setup -> API Access. If you already have a Linked Project, then you're good to go and can move on to the next step. If not, check in the Google Cloud Console to see what projects you already have and create a new one if necessary. Firebase implicitly creates a Google Cloud project, and you can use the same one.


Step 2: Creating a Google Service Account 

A Service Account is an entity for "robots" that has credentials and can be assigned roles and access rights. It is recommended to use a robot Service Account rather than your own human Google Account.

Click on "View in Google Cloud Platform" or simply go to the Google Cloud Console and select the desired project from the list. Go to the "Service Accounts" section. Click on "Create Service Account".

Enter a name for the Service Account, for example, "Google Play Automatic Upload". Enter an ID (email) for the Service Account, for example, "google-play-uploaded". Click "CREATE AND CONTINUE". 

Add the "Viewer" role; without it, the Service Account cannot be added to the Google Play Console. Click "DONE", skipping the third step ("Grant users access to this service account (optional)"). 

Step 3: Obtaining the Access Key Click on the newly created Service Account. 

Go to the "Keys" tab. Click the "ADD KEY" button -> "Create new" -> JSON.

After clicking "CREATE," a JSON file with the Service Account's private key will be downloaded: 

{
  "type": "service_account",
  "project_id": "<YOUR GCLOUD PROJECT>",
  "private_key_id": "<REDACTED>",
  "private_key": "-----BEGIN PRIVATE KEY-----\n<REDACTED>\n-----END PRIVATE KEY-----\n",
  "client_email": "google-play-uploaded@<YOUR GCLOUD PROJECT>.iam.gserviceaccount.com",
  "client_id": "<SOME BIG NUMBER>",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-play%40<YOUR GCLOUD PROJECT>.iam.gserviceaccount.com"

Keep this key in a secure and confidential location!

Step 4: Adding the Google Cloud Service Account to the Google Play Console 

The final step is to grant upload permissions for the new Service Account. It seems that this part is not fully integrated with Google Cloud yet, and the permissions need to be granted in the Google Play Console.

Go to the Google Play Console, navigate to the "Users and permissions" section. Click on the menu with three dots, then select "Invite new users."

Enter google-play-uploaded@<YOUR GCLOUD PROJECT>.iam.gserviceaccount.com 

The following permissions need to be granted:

  • View app information and download bulk reports (read only): 

Users with this permission can view app information, including any associated Play Games services projects, without access to financial data. They can also download bulk reports and view any new apps added to the Play Console in the future.

  • Create, edit, and delete draft apps: 

This permission allows users to create, edit, and delete draft apps but does not grant the ability to publish apps on Google Play.

  • Release to production, exclude devices, and use Play app signing: 

Users with this permission can create, edit, and roll out releases to production, unpublish and republish apps, exclude devices in the device catalog, and utilize app signing by Google Play to sign APKs. They can publish apps to users on Google Play.

  • Release apps to testing tracks:

This permission allows users to upload draft apps, create, edit, and roll out releases to testing tracks, unpublish and republish apps published to a testing track, upload and modify .obb files, edit release notes for apps that are not active in production, and upload app bundles for internal sharing. It does not grant the ability to publish apps to production on Google Play.

Done. By following the instructions in this section, you should have obtained a .json file with credentials for accessing Google Play, enabling you to upload applications.

Useful links:

Google Play Developers API

Uploading Builds to Google Play

We will use the Triple-T Gradle Play Publisher plugin for Gradle to upload to Google Play. An alternative is the well-known Fastlane, which we also use but only for iOS. Triple-T Play Publisher is subjectively more convenient, at least due to its integration with Gradle and the absence of the need to deal with a bunch of Ruby dependencies.

Adding the Library Add the following Gradle plugin to your build.gradle: com.github.triplet.gradle:play-publisher:

The version in the article will quickly become outdated. Please always use the latest available version.

Adding the plugin invocation 

Add the plugin invocation com.github.triplet.play to your build.gradle immediately after the android {} section: 

Plugin Configuration 

The plugin is configured through the global play {} configuration section, as well as individually for each flavor:

  • track: Specifies the track to upload to. Of course, we want to upload directly to production!
  • userFraction: Enables staged rollout to 10% to avoid being too aggressive.
  • defaultToAppBundles: Enables Android App Bundles (AAB) instead of Android Packages (APK).
  • releaseStatus.set(ReleaseStatus.DRAFT): Creates a draft version but doesn't submit it for review immediately. It can be changed to ReleaseStatus.IN_PROGRESS after testing.

Are you currently doing a staged rollout? Please share your experience in the comments.

Testing the upload to Google Play 

We launch the bundleGoogleRelease task to build a release version of the aab application for subsequent uploading.

gradle bundleGoogleRelease

We check to see if something has actually been built. 

ls build/outputs/bundle/googleRelease
OrganicMaps-23043001-google-release.aab

We launch the publishGoogleReleaseBundle task to upload the compiled aab to Google Play. 

gradle publishGoogleReleaseBundle
> Task :publishGoogleReleaseBundle
Starting App Bundle upload: OrganicMaps-23043001-google-release.aab
Uploading App Bundle: 10% complete
Uploading App Bundle: 19% complete
Uploading App Bundle: 29% complete
Uploading App Bundle: 39% complete
Uploading App Bundle: 49% complete
Uploading App Bundle: 58% complete
Uploading App Bundle: 68% complete
Uploading App Bundle: 78% complete
Uploading App Bundle: 88% complete
Uploading App Bundle: 97% complete
App Bundle upload complete
Updating [completed, inProgress] release (app.organicmaps:[23030505, 23040207]) in track 'production'

In general, the uploading part is ready, and you can see the draft release in the Google Play Console and submit it for review. 

If everything meets the expectations, then you can change 

releaseStatus.set (ReleaseStatus.DRAFT) to 

releaseStatus.set(ReleaseStatus.IN_PROGRESS) to avoid having to go to the Google Play Console to press the button again. The button should be somewhere in your CI/CD so that you don't have to use it there.

Useful links:

Managing Google Play metadata

Next step is to learn how to update metadata (app description, screenshots, etc.) in Google Play. Once you've automated this, it will be possible to update metadata in your Git repository instead of Google Play. The plugin will handle the updates in Google Play.

Downloading metadata from Google Play 

First, you need to download the existing metadata from Google Play to your repository. Run the bootstrapGoogleReleaseListing task to do this.

gradlew bootstrapGoogleReleaseListing

After shuffling the disk, Gradle will download all current metadata to the repository.

> Configure project :
Building with Google Mobile Services
Building without Google Firebase Services
Version: 2023.04.23-2
VersionCode: 23042302
Building for [armeabi-v7a, arm64-v8a, x86_64] archs.
Create separate apks: false

> Task :bootstrapGoogleReleaseListing
Downloading app details
Downloading listings
Downloading in-app products
Downloading release notes
Downloading en-US listing
Downloading tr-TR listing
<REDACTED>

Downloading en-US listing graphics for type 'featureGraphic'
Downloading en-US listing graphics for type 'icon'
Downloading en-US listing graphics for type 'phoneScreenshots'
Downloading en-US listing graphics for type 'tenInchScreenshots'
Downloading en-US listing graphics for type 'sevenInchScreenshots'
Downloading tr-TR listing graphics for type 'sevenInchScreenshots'
Downloading tr-TR listing graphics for type 'phoneScreenshots'
<-<----<
BUILD SUCCESSFUL in 44s
1 actionable task: 1 executed

New files will appear in src/<FLAVOR>/play:

contact-email.txt
contact-website.txt
default-language.txt
listings/
  en-US
    full-description.txt
    graphics/
      icon/
        1.png
      phone-screenshots/
        1.jpg
        2.jpg
        3.jpg
        4.jpg
    release-notes.txt
    short-description.txt
    title.txt
    video-url.txt
release-notes/
  en-US/
    default.txt

Editing data

After downloading the data from Google Play, add it to Git and then use Git as the primary source, avoiding manual actions in the Google Play Console. This provides many benefits, starting with clear versioning and the ability to add translation tools. For example, we use Weblate to translate Google Play descriptions into other languages. The Google Play data will also be reused later for the Huawei AppGallery.

Uploading metadata to Google Play

To upload updated metadata back to Google Play, there is a task called publishGoogleReleaseListing (where GoogleRelease is your flavor):

./gradlew publishGoogleReleaseListing

<REDACTED>

> Configure project :
Building with Google Mobile Services
Building without Google Firebase Services
Version: 2023.05.01-1
VersionCode: 23050101
Building for [armeabi-v7a, arm64-v8a, x86_64] archs.
Create separate apks: false

> Task :publishGoogleReleaseListing
Uploading app details
Uploading ar listing
<REDACTED>
Uploading zh-TW listing

Important. Updating metadata in Google Play is not tied to releasing releases. You can update descriptions and app screenshots at any time, regardless of the current state of the app build review. Review of metadata changes is done separately! 

We use this approach to update data in all 59 languages ​​supported by the Google Play Console at the time of writing.

Useful links:

Getting Access Keys from Huawei AppGallery

To upload something to the Huawei AppGallery, we first need to get access keys to Huawei AppGallery Connect. The Huawei AppGallery Connect interface, let's say, is sometimes quite confusing. But you need to figure it out once so that you don't have to go there anymore.

Go to AppGallery Connect and click Users and permissions. Go to API key > Connect API and click Create:

You can enter any name. Specify the following roles:

  • Development
  • Operations
  • Customer service (will be selected automatically)

"After this, you will be able to download the .json access key to Huawei AppGallery Connect:

{
        "type":"team_client_id",
        "developer_id":"<BIG NUMBER>",
        "client_id":"<ANOTHER BIG NUMBER>",
        "client_secret":"<REDACATED>",
        "configuration_version":"3.0"
}

Done. This file will be needed in the next step to automate the upload.

Useful links:

Uploading builds and release notes to Huawei AppGallery

To automate uploading to Huawei AppGallery, we will use the Gradle plugin ru.cian:huawei-publish-gradle-plugin, developed by Aleksandr Mirko from Omsk. Let's thank Aleksandr for such a wonderful alternative plugin for uploading to Huawei AppGallery, without which we would have suffered.

Adding the plugin to Gradle

Add the Gradle-plugin ru.cian:huawei-publish-gradle-plugin to build.gradle:

buildscript {
  repositories {
    google()
    mavenCentral()
  }

  // <REDACTED>

  dependencies {
    classpath 'com.android.tools.build:gradle:7.4.2'

    // <REDACTED>

    classpath("ru.cian:huawei-publish-gradle-plugin:1.3.6")
  }
}

Enabling the plugin

Enable the ru.cian.huawei-publish-gradle-plugin plugin in build.gradle after the android {} section:

apply plugin: 'ru.cian.huawei-publish-gradle-plugin'

Plugin configuration

The plugin has a separate section for configuring huaweiPublish {}. Add a new section with settings to the end of build.gradle:

huaweiPublish {
  instances {
    huaweiRelease {
      credentialsPath = "$rootDir/huawei-appgallery.json"
      buildFormat = 'aab'
      deployType = 'draft' // confirm manually
      releaseNotes = []
      def localeOverride = [
          'am' : 'am-ET',
          'gu': 'gu_IN',
          'iw-IL': 'he_IL',
          'kn-IN': 'kn_IN',
          'ml-IN': 'ml_IN',
          'mn-MN': 'mn_MN',
          'mr-IN': 'mr_IN',
          'ta-IN': 'ta_IN',
          'te-IN': 'te_IN',
      ]
      def files = fileTree(dir: "$projectDir/src/google/play/listings",
          include: '**/release-notes.txt')
      files.each { File file ->
        def path = file.getPath()
        def locale = file.parentFile.getName()
        locale = localeOverride.get(locale, locale)
        releaseNotes.add(new ru.cian.huawei.publish.ReleaseNote(locale, path))
      }
    }
  }
}
  • credentialsPath - sets the path to the file with keys from AppGallery Connect
  • deployType = 'draft' - tell the plugin to create a release draft, but not to send it for review immediately. After testing and debugging, it can be changed to publish.
  • buildFormat = 'aab' - just like in Google Play, we use Android App Bundles (AAB) for Huawei AppGallery instead of Android Packages (APK).
  • localeOverride - here and below, magic is done to reuse the release notes from the plugin for Google Play. There are some minor inconveniences due to the fact that a small group of locale codes for Huawei AppGallery is written with _ (underscore) instead of - (dash) like Google Play. The rest match.

For more information on configuration parameters, see the plugin README on GitHub.

Testing the upload

Run the bundleHuaweiRelease task to build a release version of the aab application for subsequent upload:

gradle bundleHuaweiRelease

Make sure that we have actually built something:

ls build/outputs/bundle/huaweiRelease
OrganicMaps-23043001-huawei-release.aab

Run the publishHuaweiAppGalleryHuaweiRelease task to upload the assembled aab to Huawei AppGallery Connect:"

gradle publishHuaweiAppGalleryHuaweiRelease

Attention: the publish task for some reason does not have an explicit dependency on the bundle, so do not forget to run the bundle task every time. You can run both commands at once in one gradle run:

gradle bundleHuaweiRelease publishHuaweiAppGalleryHuaweiRelease

Pay attention to what gradle says during the upload process:

> Task :publishHuaweiAppGalleryHuaweiRelease
Huawei AppGallery Publishing API: Prepare input config
Huawei AppGallery Publishing API: Found build file: `OrganicMaps-23043001-huawei-release.aab`
Huawei AppGallery Publishing API: Get Access Token
Huawei AppGallery Publishing API: Get App ID
Huawei AppGallery Publishing API: Get Upload Url
Huawei AppGallery Publishing API: Upload build file '/home/runner/work/organicmaps/organicmaps/android/build/outputs/bundle/huaweiRelease/OrganicMaps-23040207-huawei-release.aab'
Huawei AppGallery Publishing API: Upload release notes: 1/59, lang=et
Huawei AppGallery Publishing API: Upload release notes: 2/59, lang=kk

[REDACTED]

Huawei AppGallery Publishing API: Upload release notes: 59/59, lang=ar
Huawei AppGallery Publishing API: Update App File Info
Huawei AppGallery Publishing API: Upload build file draft without submit on users - Successfully Done!

BUILD SUCCESSFUL in 1m 36s
1 actionable task: 1 executed

In case of any problems with credentials in the huawei-appgallery.json file, the plugin will fail at the "Get Access Token" stage.

There may also be situations where the number of characters in the release notes exceeds the limits, which results in creating a draft without fully updating the release notes. In this case, it is better to fix the problem, manually delete the draft release in AppGallery Connect, and try again. In this example, we upload release notes for almost all available localizations, although in most cases the text is simply the same as in English.

Done. After this step, uploading to Huawei AppGallery is also possible through Gradle.

Useful links:

Adding CI/CD

The last but not the least step is to add all of the above to your CI/CD system. There are no special difficulties, as all automation is already done in gradle and you just need to run the corresponding tasks.

We use GitHub Actions, so we'll briefly explain them.

First, you will need to add files with keys google-play.json and huawei-appgallery.json to GitHub Actions Secrets. You will also need signing keys, which can be saved in base64. Go to Settings -> Secrets and variables -> Actions and add two new variables for each file:

Then create a new workflow file .github/workflows/android-release.yaml:

name: Android Release
on:
  workflow_dispatch:  # Manual trigger

Add one task with a matrix of several options (google, huawei, web):

jobs:
  android-release:
    name: Android Release
    runs-on: ubuntu-latest
    environment: production
    needs: tag
    strategy:
      fail-fast: false
      matrix:
        include:
          - flavor: google
          - flavor: huawei

Download the sources:

 steps:
      - name: Checkout sources
        uses: actions/checkout@v3
        with:
          fetch-depth: 200 # enough to get all commits for the current day

In case of shared GitHub Runners, all dependencies such as Android SDK are already installed, but you can install something else necessary for your project:

 - name: Install build tools and dependencies
        shell: bash
        run: |
          sudo apt-get update -y
          sudo apt-get install -y ninja-build

Create the google-play.json and huawei-appgallery.json files from GitHub Secrets:

- name: Get credenials
        shell: bash
        run: |
          echo "${{ secrets.GOOGLE_PLAY_JSON }}" > google-play.json
          echo "${{ secrets.HUAWEI_APPGALLERY_JSON }}" > huawei-appgallery.json

Similarly, you will need to add signing keys for applications and other keys. Instead of working with individual secrets, you can create a separate repository and apply it on top of the main one at the time of application building.

Add tasks for building and uploading to Google Play and Huawei AppGallery using the instructions described earlier in this article:

- name: Compile and upload to Google Play
        shell: bash
        working-directory: android
        run: |
          gradle bundleGoogleRelease publishGoogleReleaseBundle
        if: ${{ matrix.flavor == 'google' }}

      - name: Compile and upload to Huawei AppGallery
        shell: bash
        working-directory: android
        run: |
          gradle bundleHuaweiRelease
          gradle publishHuaweiAppGalleryHuaweiRelease
        if: ${{ matrix.flavor == 'huawei' }}

In general, it's done. When you run it, it will look something like this:

You can also add a task to update the metadata of the Google Play Store. You can add a task to the same workflow file or create a separate file, as in Google Play this operation.

 - name: Update Google Play Metadata
        shell: bash
        run: ./gradlew publishGoogleReleaseListing
        working-directory: android
        timeout-minutes: 5

Useful links

Conclusion

Issuing new releases in stores is a routine operation that needs to be done periodically - every week, every month, year after year. And at every step, something will be forgotten or done with errors. Release after release. You will spend your time every time.

Before adding the automation described in the article, we carefully documented the existing process. It turned out to be more than 30 pages of text with screenshots... Configured CI/CD has been working for two years already and allows saving hundreds of hours per year, which can be spent on more useful things.


Report Page