Publishing Flutter apps to Play Store using Github Actions and Fastlane

Publishing Flutter apps to Play Store using Github Actions and Fastlane

Recently I was trying to figure out how I can publish flutter apps to the Play Store using Github Actions and publish them I did. I started reading some documentation available at flutter's official site and followed the instructions to set up Fastlane locally on my machine. First, we will get the Fastlane working on your local machine.

Installing Fastlane

First, we will install the Fastlane tool on our local machine. Use homebrew to install it on a mac

brew install fastlane

on a Linux or Windows machine, we'll use ruby's gem package manager to install Fastlane.

gem install fastlane

Setting up app signing

Go to android/app/build.gradle file and define add the following lines:

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

// ^ Above this
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'

...

 defaultConfig {
    ...

    // Add this as well
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.release
        }

    }

...

You also need to create a new file under the android folder called key.properties, and add the following configurations:

storePassword=store-password
keyPassword=key-password
keyAlias=key-alias
storeFile=path-to-jks

make sure to change these with your Keystore configuration. Run the flutter build appbundle command to build a signed app bundle.

Creating the service account

To upload the app bundle on the play console we need a service account that will have the required permissions to perform actions on our behalf. Before doing this make sure you have already registered an app on the play console with the correct package name.

  • Head over to Play Console.
  • On the left sidebar under Setup, you'll see API Access.
  • Scroll Down until you see the Service Accounts section.
  • Click on Learn how to create service accounts.
  • On the popup that shows up click on the link that says Google Cloud Platform
  • Once you're in the google cloud console, click on Create service account
  • This will ask you a couple of questions, just fill those in and create a new service account.
  • Once a new service account is created click on the three dots for the context menu and then Manage Keys
  • Now you need to create a JSON key for this service account.
  • Once all that is done return back to the play console and click on the Done button in the Dialog Box.
  • You should see the service account listed on the play console. Click on the View Play Console Permissions.
  • Under app permission, you need to give admin access to this account for the app you want it to manage.

Setting up Fastlane for the project

Now that app signing is set up you need to configure Fastlane for the android project. Make sure you are in the android folder and run the following command:

fastlane init

Fastlane will ask you a couple of questions, like what's the package name for your app and path to the json secret (this is the file that we downloaded from the last step).

This will create a new folder called fastlane, which will contain two files Appfile and Fastfile. Make sure the package name and path to JSON secret are correct in the Appfile.

Open the Fastfile and replace it with the following lines:

# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

default_platform(:android)

platform :android do
  desc "Submit a new Google Play [Beta Testing]"
  lane :beta do
    upload_to_play_store(
      track: 'internal',
      release_status: 'draft',
      aab: '../build/app/outputs/bundle/release/app-release.aab'
    )

    # sh "your_script.sh"
    # You can also use other beta testing services here
  end

  desc "Deploy a new version to the Google Play [Production]"
  lane :deploy do
    upload_to_play_store(track: 'production', aab: '../build/app/outputs/bundle/release/app-release.aab')
  end
end

Now you've created two lanes one that will push the build to the internal testing beta track and another one deploy to production.

Run the fastlane supply init to fetch information from the play console about the app (like screenshots, app descriptions, etc).

You can test if your setup works by running:

fastlane beta

Setting up Github Action

Now that we know our configuration works locally, let's set up a GitHub action that will create a beta release every time we push our app into the development branch.

First, we need to add some repository secrets by going to Repository Settings > Secrets >> Actions and add the following variables:

  • PLAY_STORE_UPLOAD_KEY this is the base64 encoded Keystore file (.jks)
  • KEYSTORE_KEY_ALIAS this is the upload Keystore key alias
  • KEYSTORE_KEY_PASSWORD this is our upload key password
  • KEYSTORE_STORE_PASSWORD this is our store password
  • PLAY_STORE_CONFIG_JSON this is the google service account's config file

use the cat keystore.jks | base64 | pbcopy to copy the base64 version of jks file to the clipboard.

name: Deploy Beta build to Play Store

on:
  push:
    branches:
      - development

# Declare default permissions as read only.
permissions: read-all

jobs:
  fastlane-deploy:
    runs-on: ubuntu-20.04
    steps:
      # Set up Flutter.
      - name: Checkout App Repo
        uses: actions/checkout@v3
        with:
          ref: 'development'

      - name: Setup Java
        uses: actions/setup-java@v1
        with:
          java-version: '12.x'

      - name: Configure Keystore
        run: |
          echo "$PLAY_STORE_UPLOAD_KEY" | base64 --decode > /home/runner/work/repo-name/repo-name/upload-keystore.jks
          echo "storeFile=/home/runner/work/repo-name/repo-name/upload-keystore.jks" >> key.properties
          echo "keyAlias=$KEYSTORE_KEY_ALIAS" >> key.properties
          echo "storePassword=$KEYSTORE_STORE_PASSWORD" >> key.properties
          echo "keyPassword=$KEYSTORE_KEY_PASSWORD" >> key.properties
          echo "$KEYSTORE_KEY_ALIAS"
          echo "$PLAY_STORE_CONFIG_JSON" > /home/runner/work/repo-name/repo-name/service_key.json
          echo "json_key_file(\"/home/runner/work/repo-name/repo-name/service_key.json\")" > /home/runner/work/repo-name/repo-name/android/fastlane/Appfile
          echo "package_name(\"package-name\")" >> /home/runner/work/repo-name/repo-name/android/fastlane/Appfile
          cat /home/runner/work/repo-name/repo-name/android/fastlane/Appfile
        env:
          PLAY_STORE_UPLOAD_KEY: ${{ secrets.PLAY_STORE_UPLOAD_KEY }}
          KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }}
          KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
          KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
          PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }}
        working-directory: android

      - name: Install Flutter & Build App Bundle
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.x'
          channel: 'stable'
      - run: flutter --version && flutter pub get && flutter build appbundle --release

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '2.7.2'

      - name: Fastlane Action
        uses: maierj/fastlane-action@v2.2.1
        with:
          lane: "beta"
          subdirectory: "android"

make sure to replace the repo-name and package-name in the Configure Keystore step.

That's all, everytime you push to the development branch a new draft release will be automatically created in the play console.