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 aliasKEYSTORE_KEY_PASSWORD
this is our upload key passwordKEYSTORE_STORE_PASSWORD
this is our store passwordPLAY_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
andpackage-name
in theConfigure Keystore
step.
That's all, everytime you push to the development branch a new draft release will be automatically created in the play console.