Loading... Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?
Deploy your app to Google Play with Codemagic CLI tools and GitHub Actions

Deploy your app to Google Play with Codemagic CLI tools and GitHub Actions

Jun 30, 2025

This article is written by Masaki Sato

We previously explored how Codemagic CLI tools can streamline iOS app deployment to App Store Connect with GitHub Actions. The Codemagic team has continuously improved these tools, extending their capabilities to Android releases on Google Play Console.

What are Codemagic CLI tools?

Codemagic CLI tools are free, open-source utilities that power Codemagic’s CI/CD platform internally. These tools help streamline mobile development workflows including app building, code signing, and store deployment.

Their standout feature is cross-platform compatibility β€” you can use the same toolset for both iOS and Android projects. Unlike Codemagic’s cloud-based CI/CD service, these CLI tools provide the flexibility to run builds locally on your development machine or integrate with any CI/CD platform like GitHub Actions. Built with user-friendliness in mind, these tools offer a gentle learning curve while providing powerful capabilities for experienced developers.

In this article, I’ll show you how to deploy Flutter apps to Google Play Console using these CLI tools with GitHub Actions.

By implementing the content of this article, you’ll be able to streamline your app distribution process and save significant time and resources.

Requirements

To follow the steps in this article, you will need the following:

Registering a Google Play Developer Account

Releasing on the Google Play Store requires a paid account registration.

Registering the App on Google Play Console

The app must already be created on the Google Play Console.

Preparation

First, let’s perform the necessary preparations to create the Codemagic workflow.

Checking the Keystore Configuration

Open the Flutter project you are using in your IDE.

Check the /android/key.properties file, which contains the jks file and the storePassword, keyPassword, and keyAlias for uploading to Google Play.

Example:

storePassword=XXXXXXXX
keyPassword=XXXXXXXX
keyAlias=upload
storeFile=upload-keystore.jks

If the jks file has not been created yet, create it with the following command:

keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA \
        -keysize 2048 -validity 10000 -alias upload

For details, please refer to the official documentation.

Keep the created file and the passwords used during the creation process handy.

Checking the build.gradle Configuration

Open the app-level build.gradle or build.gradle.kts file.

Make sure it is set up to reference key.properties as follows:

build.gradle

// ...

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    // ...

    signingConfigs {
        release {
            keyAlias = keystoreProperties['keyAlias']
            keyPassword = keystoreProperties['keyPassword']
            storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword = keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig = signingConfigs.release
        }
    }
// ...
}

build.gradle.kts

import java.util.Properties
import java.io.FileInputStream

plugins {
   ...
}

val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}

android {
    // ...

    signingConfigs {
        create("release") {
            keyAlias = keystoreProperties["keyAlias"] as String
            keyPassword = keystoreProperties["keyPassword"] as String
            storeFile = keystoreProperties["storeFile"]?.let { file(it) }
            storePassword = keystoreProperties["storePassword"] as String
        }
    }
    buildTypes {
        release {
            signingConfig = signingConfigs.getByName("release")
        }
    }
...
}
  • For details, please refer to the official documentation.

  • The key.properties file itself will be automatically generated by Codemagic during execution, so it does not need to be committed to the repository (do not include it in version control as it contains sensitive information).

Setting up Google Play API Access

Set up the necessary API access to publish the app to Google Play via Codemagic.

  1. Open the project for the target app in the Google Cloud Console (create a new project from here if necessary).

  2. Enable the Google Play Android Developer API.

  3. From the Credentials menu page, create a new service account by clicking “CREATE CREDENTIALS”.

  4. In the first step, enter the account name and description.

  5. In the second step, select the role Service Accounts > Service Account User.

  6. The third step is unnecessary, so click Done to complete.

  7. Open the edit screen from the Actions column of the created service account.

  8. From the Keys tab, click Create new key.

  9. Select JSON for the key type and create a new key.

  10. A private key for this service account will be downloaded. Keep it in a safe place as it will be needed for Codemagic settings later.

  11. Once this is done, go to the Google Play Console.

  12. Click Invite new user on the Users and permissions page.

  13. Enter the email address of the service account created earlier.

  14. Select the relevant app from Add app.

  15. Grant release section permissions and click Apply.

  16. Invite the service account by clicking Invite user.

Setting up the environment

This section explains how to set up repository variables and secrets that will be needed later in the pipeline.

To use Codemagic CLI tools with GitHub Actions, you need to set up variables and secrets in your repository. Variables are text values that can be referenced within workflows, while secrets are encrypted and allow you to securely manage sensitive information (such as passwords and certificates).

You can add new values from Settings > Secrets and Variables in your GitHub repository:

Let’s input the values prepared during the preparation phase.

Setting up Variables

Set up the following variables:

  • ANDROID_KEY_ALIAS: The value of keyAlias in key.properties (e.g., upload).
  • ANDROID_KEYSTORE_FILENAME: The value of storeFile in key.properties (e.g., upload-keystore.jks).
  • GOOGLE_PLAY_PACKAGE_NAME: The package name of the app registered in Google Play (e.g., com.example.app).

Setting up Secrets

Set up the following secrets:

  • ANDROID_KEYSTORE: The JKS upload key. If it’s stored in android/app/upload-keystore.jks as in the preparation example, you can copy the base64 encoded value to the clipboard by running the following command from the project root:
openssl base64 < android/app/upload-keystore.jks | pbcopy
  • ANDROID_KEYSTORE_PASSWORD: The value of storePassword in key.properties.

  • ANDROID_KEY_PASSWORD: The value of keyPassword in key.properties.

  • GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS: The JSON key for the service account. This is the one you saved in step 10 of the “Setting up Google Play API Access” preparation section. Paste the contents of the file.

Setting up the GitHub Actions pipeline

In this section, you’ll learn how to use GitHub Actions to build your Flutter app and upload it to Google Play using Codemagic CLI tools.

YAML files should be saved in your project’s .github/workflows/ directory with the .yaml extension.

GitHub Actions comes with Python pre-installed. Codemagic CLI tools require Python 3.8 or higher, but you can also explicitly specify a version using setup-python.

First, let’s start with a simple example that outputs Hello world! to get familiar with GitHub Actions:

name: Hello World Workflow
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Echo Hello World
        run: echo "Hello, World!"

This workflow is triggered by any push to the repository and runs on the latest Ubuntu environment (ubuntu-latest).

To make it work according to our requirements, we need to make several modifications. First, for testing purposes, we’ll set the workflow to trigger manually (workflow_dispatch). We also need to configure the pipeline to use the repository variables and secrets we set up earlier to integrate with Google Play.

Follow these steps to add and modify the YAML:

  1. Code checkout

Use the actions/checkout@v4 action to checkout your repository code to the runner.

- uses: actions/checkout@v4
  1. Set up Flutter

Use subosito/flutter-action. Here we use the stable channel, but you can select a specific version or other channels.

- name: Set up Flutter
  uses: subosito/flutter-action@v2
  with:
    channel: stable
  1. Install Codemagic CLI tools

Add a job to install the Codemagic CLI tools.

- name: Install Codemagic CLI tools
  run: pip install codemagic-cli-tools
  1. Set up environment variables Configure the environment variables for use in the pipeline that we registered in the previous section. Reference variables with the vars prefix and secrets with the secrets prefix.

    build:
      runs-on: ubuntu-latest
      env:
        # Google Play authentication - Service account JSON key
        GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS }}
    
        # Android signing information
        ANDROID_KEYSTORE_FILENAME: ${{ vars.ANDROID_KEYSTORE_FILENAME }}
        ANDROID_KEY_ALIAS: ${{ vars.ANDROID_KEY_ALIAS }}
        ANDROID_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }}
        ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
        ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
    
        # Google Play app configuration
        GOOGLE_PLAY_PACKAGE_NAME: ${{ vars.GOOGLE_PLAY_PACKAGE_NAME }}
    
  2. Set up Android keystore

Decode the upload key used for signing the Android app and store it in the specified location.

- name: Set up Android keystore
  run: |
    echo "$ANDROID_KEYSTORE" | base64 --decode > android/app/$ANDROID_KEYSTORE_FILENAME    
  1. Create key.properties file

Create the key.properties file needed to sign the app bundle in the Flutter build process.

- name: Create key.properties
  run: |
    cat > android/key.properties << EOF
    storePassword=$ANDROID_KEYSTORE_PASSWORD
    keyPassword=$ANDROID_KEY_PASSWORD
    keyAlias=$ANDROID_KEY_ALIAS
    storeFile=$ANDROID_KEYSTORE_FILENAME
    EOF    

This file contains Android signing configuration information and is used by the Flutter build process to sign the App Bundle.

  1. Build Android App Bundle

Get the latest build number from Google Play and build the app bundle with a build number that’s incremented by 1.

- name: Build Android App Bundle
  run: |
    FLUTTER_BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$GOOGLE_PLAY_PACKAGE_NAME") + 1))
    flutter build appbundle --release --build-number=$FLUTTER_BUILD_NUMBER    

This step retrieves the latest build number from all Google Play tracks and increments it by 1 to build a new app bundle. This ensures consistent versioning management without relying on pseudo build numbers.

  1. Publish to Google Play

Publish the built app bundle to Google Play’s internal testing track as a draft.

- name: Publish to Google Play
  run: |
    google-play bundles publish \
    --bundle "build/app/outputs/bundle/release/app-release.aab" \
    --track internal \
    --draft    

Change the track (internal, alpha, beta, production) and publishing options (with or without --draft) as needed.

This completes the workflow for automatically publishing your app to Google Play using GitHub Actions and Codemagic CLI tools.

When you run this workflow, the version will be automatically incremented for each build and uploaded to the Google Play Console.

Code Sample

Here’s the complete code sample example:

name: Publish to Google Play
on: 
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
       # Google Play authentication - Service account JSON key
        GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS }}
        
        # Android signing information
        ANDROID_KEYSTORE_FILENAME: ${{ vars.ANDROID_KEYSTORE_FILENAME }}
        ANDROID_KEY_ALIAS: ${{ vars.ANDROID_KEY_ALIAS }}
        ANDROID_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }}
        ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
        ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
        
        # Google Play app configuration
        GOOGLE_PLAY_PACKAGE_NAME: ${{ vars.GOOGLE_PLAY_PACKAGE_NAME }}
        
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
            channel: stable
      
      - name: Install Codemagic CLI tools
        run: pip install codemagic-cli-tools
      
      - name: Set up Android keystore
        run: |
          echo "$ANDROID_KEYSTORE" | base64 --decode > android/app/$ANDROID_KEYSTORE_FILENAME          

      - name: Create key.properties
        run: |
          cat > android/key.properties << EOF
          storePassword=$ANDROID_KEYSTORE_PASSWORD
          keyPassword=$ANDROID_KEY_PASSWORD
          keyAlias=$ANDROID_KEY_ALIAS
          storeFile=$ANDROID_KEYSTORE_FILENAME
          EOF
                    
      - name: Build Android App Bundle
        run: |
          FLUTTER_BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$GOOGLE_PLAY_PACKAGE_NAME") + 1))
          flutter build appbundle --release --build-number=$FLUTTER_BUILD_NUMBER
                    
      - name: Publish to Google Play
        run: |
          google-play bundles publish \
          --bundle "build/app/outputs/bundle/release/app-release.aab" \
          --track internal \
          --draft          

Conclusion

While Codemagic CI/CD provides a modern environment optimized for mobile development, code signing, and simple YAML configuration, utilizing CLI tools allows you to access additional features in pipelines both within and outside of Codemagic. Whether in a local environment or on a CI/CD pipeline, these tools enable flexible and efficient mobile app building and publishing.

In this article, we introduced the steps for publishing Android builds to Google Play from GitHub Actions pipelines using Codemagic CLI tools, and explained their ease of use and flexibility. Please give it a try!

Additionally, by setting up a similar workflow for App Store Connect alongside the Google Play, you can further streamline your development process.

Check out the following article for more detailsπŸ‘‡
Deploy your app to App Store Connect with Codemagic CLI tools and GitHub Actions

Let’s continue to enjoy developing comfortably with Codemagic!

Learn more

Latest articles

Show more posts