Code Signing for Android via Github Actions
(TL;DR: By using fastlane supply and Github Action we can compile and publish an Android app automatically)
(Part three of my series on code-signing / distributing apps, check Part 4 on Building Unity Game using Github Action)
Create Github Repository
Create a Github Repository for your project. Feel free to use this repository as a template since it already contains all the workflows files.
Installing fastlane
We need to install fastlane locally on our machine, first makes sure you have ruby installed (personally I prefer via WSL2).
Also you need to makes sure Bundler is installed:
gem install bundler
Now create a file called Gemfile
in the root:
source "https://rubygems.org"
gem "fastlane"
gem 'fastlane-plugin-github_action', git: "https://github.com/starburst997/fastlane-plugin-github_action"
Notice that we’re using my fork of joshdholtz/fastlane-plugin-github_action, this is because the published gem is out of date.
Now run:
bundle install
Create a Google Play Console Account
Sign up for a Google Play Console account, you’ll need to pay a one-time fee of $25 and verify your identity.
You can follow the instructions here but it’s pretty straight-forward.
Create a Google Service Account
To programmatically access the Google Play Console, you will need a dedicated Google Service account with API access.
The fastlane documentation highlight the steps but here’s a summary:
-
Create a Google Cloud Project (or use an existing one). Give it a name and click create (more info).
-
Enable the Google Play Developer API by clicking the Enable API button for your newly created project.
-
Open Service Accounts on Google Cloud and select your project.
-
Create a new Service Account by clicking the create button.
-
Give it a name and ID, copy the resulting Email address (we will need it later). Click on Done, NOT
Create and Continue. -
Click on the vertical three-dot icon (under Actions) of your newly created account, and select Manage keys.
-
Click on Add key and select Create new key.
-
Make sure to select JSON, click Create and save the file on your computer.
Invite Service Account to Google Play Console
-
Open the Google Play Console and select Users and Permissions. Click Invite new users.
-
Paste the email address of the Service Account you saved for later (
xxxx@yyyy.iam.gserviceaccount.com
). -
Choose the permissions: Admin (all permissions). Click on Invite User.
Test your JSON file
We can now test that the connection is valid using your JSON file by running this command:
bundle exec fastlane run validate_play_store_json_key json_key:/path/to/your/downloaded/file.json
Look for Successfully established connection to Google Play Store
.
Generate a Personal Access Token (PAT)
We also need to generate a Personal Access Token for Github.
This will enables us to increment a build number after each build.
-
Visit your settings page and click on Generate new token and select Generate new token (classic).
-
We need all the repo scope enabled. Set no expiration. Click Generate token and save the value.
Save secrets
Save this secret in the github repository for your project:
GH_PAT
: The value of your newly generated token
Generate an upload key and keystore
You can generate your key using Android Studio, but here’s how you can do it on the command line using keytool (available in your JDK’s bin folder):
keytool -genkey -v -keystore .keystore -keyalg RSA -keysize 2048 -validity 10000
You’ll be prompt to add some information, keep notes of the password (use the same for both) and the alias.
For ease of use afterward, we’ll save the keystore as base64 and save it as a secret in our repository.
base64 .keystore
I’ve also created a Github Action that you can run in this repository called: Generate .keystore (.yml). You need to set the secrets: ANDROID_KEYSTORE_PASS
and ANDROID_KEYALIAS_PASS
first since there is no way to input password string in Github Action yet. Makes sure you added the GH_PAT
secret as well.
The action will add the ANDROID_KEYALIAS_NAME
and ANDROID_KEYSTORE_BASE64
secrets once done.
Either the PKCS12 or JKS variant will works.
Save secrets
We can now add all the necessary secrets to our repository:
GOOGLE_PLAY_KEY_FILE
: The JSON file content from the Service Account.ANDROID_KEYALIAS_NAME
: The alias used for the .keystore.ANDROID_KEYSTORE_PASS
: The password used for the .keystore.ANDROID_KEYALIAS_PASS
: The password used for the .keystore (same as above).ANDROID_KEYSTORE_BASE64
: The base64 value of the .keystore file.ANDROID_PACKAGE_NAME
: The package name of your app.
(Save those variables inside a Password Manager for re-use in future projects, except for the package name, they won’t change)
Build .aab
Before you can upload your app via fastlane, you need to manually submit your app once (see fastlane/fastlane#14686).
The android folder contains a simple “Hello World” project.
- Build your app using the Github Action (Build Android). Go to the Actions tab and click Run Workflow.
- Download the artifact once the action is done.
Create app in Google Play Console
-
Open the Google Play Console and click Create app.
-
Fill out the form and click Create app.
-
In the Test and release section, select testing and then Internal testing.Click Create new release.
-
Drag and drop your .aab, enter release name and notes. Click Save and publish
Create Fastfiles
fastlane/Appfile
for_platform :android do
package_name(ENV["ANDROID_PACKAGE_NAME"])
json_key_file(ENV["GOOGLE_PLAY_KEY_FILE_PATH"])
end
fastlane/Fastfile
org, repo = (ENV["GITHUB_REPOSITORY"]||"").split("/")
match_org, match_repo = (ENV["MATCH_REPOSITORY"]||"").split("/")
platform :android do
desc "Upload a new Android version to the production Google Play Store"
lane :production do
upload_to_play_store(track: 'production', release_status: 'completed', aab: "#{ENV['ANDROID_BUILD_FILE_PATH']}")
end
desc "Upload a new Android internal version to Google Play"
lane :internal do
upload_to_play_store(track: 'internal', release_status: 'completed', aab: "#{ENV['ANDROID_BUILD_FILE_PATH']}")
end
end
Create workflows
By using workflow_call
we can simplify the workflow file by referencing an external one, but feel free to copy the original instead to fit your pipeline better.
Save those inside the .github/workflows
directory of your repository.
android.yml (original)
name: Build Android
on:
workflow_dispatch:
workflow_call:
jobs:
build:
uses: starburst997/android-code-sign/.github/workflows/android.yml@v1
secrets: inherit
with:
path: 'android'
module: 'app'
version: '1.0'
publish_android.yml (original)
name: Publish Android
on:
workflow_dispatch:
jobs:
build:
uses: ./.github/workflows/android.yml
secrets: inherit
publish:
uses: starburst997/android-code-sign/.github/workflows/publish_android.yml@v1
needs: [build]
secrets: inherit
with:
lane: 'internal'
Notice that we need to specify the project’s path, module and version.
Publish an update
Now you can call the Publish Android action and you should see a new build appears in your internal test lane.
Makes sure your app has been reviewed by filling all the necessary steps in Google Play Console, otherwise you’ll get an error: Only releases with status draft may be created on draft app.
The workflow will also automatically increment the build number and save it as a variable in the repository.
I’ve also included a release workflow as an example.
Code-signing / distributing app series:
- Part 1: Code Signing for Windows as an Individual Developer
- Part 2: Code Signing for Apple without a mac
- Part 3: Code Signing for Android via Github Actions
- Part 4: Build and publish your Unity Game using Github Actions