This is a guide on how to configure a GitHub workflow for building, signing and publishing an Android app that uses Bevy (there is a separate guide for iOS).
The workflow uses the tool xbuild from a customized fork, which includes a couple fixes and hacks needed to get app bundles. Some of these changes are explained and motivated in a separate post. If you want to see the workflow in use, you can take a look at bevy_game_template
You can install xbuild from my fork with cargo install --git https://github.com/NiklasEi/xbuild
. Check your environment with x doctor
and install missing dependencies.
To run your project with xbuild, add the following as manifest.yaml
:
android:
gradle: true
icon: "icon.png"
manifest:
package: "com.example.app"
version_code: 1
application:
label: "Bevy game"
Make sure to correct the path to your app icon. Also, update the package identifier and the app label.
Connect your Android device to your machine and note down the device id from x devices
. Now run x run --device <device ID>
. Gradle should build the project and then start your app on the device. If this works, you can also try building a bundle with x build --release --platform android --store play
.
The following goes into a yaml
file in your GitHub workflows directory (for example .github/workflows/release-android-google-play.yaml
).
name: release-android-google-play
on:
workflow_dispatch:
inputs:
version:
description: 'GitHub Release'
required: true
type: string
play_release:
description: 'Release name from google play console'
required: true
type: string
env:
# used for uploading the app to a GitHub release
APP_NAME: bevy_game
BUNDLE_PATH: "target/x/release/android/mobile.aab"
PACKAGE_NAME: "com.example.app"
# release track; you can promote a build to "higher" tracks in the play console or publish to a different track directly
# see track at https://github.com/r0adkll/upload-google-play#inputs for more options
TRACK: internal
MOBILE_DIRECTORY: mobile
permissions:
contents: write
jobs:
bundle-sign-release:
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- name: Install Dependencies
run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev lld llvm
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: Add Android targets
run: rustup target add aarch64-linux-android armv7-linux-androideabi
- name: Install cargo-binstall
run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
- name: Install xbuild
run: cargo binstall --git https://github.com/NiklasEi/xbuild --bin-dir x xbuild -y
- name: Build app bundle
run: |
cd ${{ env.MOBILE_DIRECTORY }}
x doctor
x build --release --platform android --store play
- name: sign app bundle
run: |
KEYSTORE_PATH=${{ runner.temp }}/upload-keystore.jks
echo -n "${{ secrets.PLAYSTORE_KEYSTORE }}" | base64 --decode > $KEYSTORE_PATH
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore $KEYSTORE_PATH -storepass "${{ secrets.PLAYSTORE_KEYSTORE_PASSWORD }}" ${{ env.BUNDLE_PATH }} upload
- name: Upload self-signed bundle to GitHub
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.BUNDLE_PATH }}
asset_name: ${{ env.APP_NAME }}_${{ inputs.version }}_android.aab
release_name: ${{ inputs.version }}
tag: ${{ inputs.version }}
overwrite: true
- name: prepare Google play store secrets
run: |
SERVICE_ACCOUNT=${{ runner.temp }}/service-account.json
echo -n "${{ secrets.PLAYSTORE_SERVICE_ACCOUNT }}" | base64 --decode > $SERVICE_ACCOUNT
- name: upload bundle to Google play store
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJson: ${{ runner.temp }}/service-account.json
packageName: ${{ env.PACKAGE_NAME }}
releaseName: ${{ inputs.play_release }}
releaseFiles: ${{ env.BUNDLE_PATH }}
track: ${{ env.TRACK }}
Change the env
section according to your project. The bundle name is going to be your crate name with an aab
file ending. If the crate that is built as a library for Android is at the root of your project, remove the MOBILE_DIRECTORY
variable and its usage. Otherwise, adapt the value.
The workflow requires multiple secrets to be configured in GitHub. You need to have a Google Play Developer account which comes with a one-time 25$ registration fee.
Before configuring the required secrets, let's quickly go through the workflow steps:
Produced app bundles will contain libraries for the ABIs arm64-v8a
and armeabi-v7a
, which cover more than 90% of the devices currently supported by Android (see Notes on mobile development with Bevy #2 for more details).
Simple strings like passwords can directly go into a GitHub secret. Files will be encoded first using base64 (e.g. openssl base64 -in ~/upload-keystore.jks
).
To configure a secret go to your repository settings in GitHub. Navigate to "Security" -> "Secrets and variables", select "Actions" then click "New repository secret".
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
json
type and press "Create"Before running the workflow for the first time, got to Play Store Connect and create a release in "Internal testing". Remember the name (e.g. "v0.1.0") since you will have to pass it to the workflow. Create a bundle on your machine following the workflow steps 7 and 8, then upload it manually to the release. Otherwise, the Google Play API will return the error "Package not found".
Now you can head over to the "Actions" tab in your repository. Find the workflow in the list on the left, select it and click "Run workflow" in the top right. Put a name for a GitHub release in the first input and the release name from Google Play Console in the second and press "Run workflow".
The app build number is taken from the manifest.yaml
and needs to be bumped for every build. Otherwise, the upload is not accepted by Google Play Console.
Workflow runs are free for public repositories on GitHub. If your project is private, it will use build minutes from your allowance (2000 minutes per month on a free account).
Thank you for reading! If you have any feedback, questions, or comments, you can find me at @nikl_me@mastodon.online or on the Bevy Discord server (@nikl).