Sign an Android release
Create an upload key, add it to a release pipeline, and build a signed App Bundle.
On this page you create a second pipeline that builds a release App Bundle
(.aab) and signs it with your upload key. Google Play accepts only App
Bundles signed with that key, so this is the build you deliver to testers on
the next page.
What you need
- The project from Build your first Android app, with one successful build.
- A JDK for
keytool. Android Studio includes one. - Your app’s application ID, such as
com.example.app, fromandroid/app/build.gradleorandroid/app/build.gradle.kts.
1. Create an upload key
If the app is already on Google Play, skip this step and use your existing upload keystore.
Otherwise, create one with the JDK that comes with Android Studio. A fresh
Mac has no Java on its PATH, so call the tools by their full path:
JAVA_BIN="/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin"
"$JAVA_BIN/keytool" -genkeypair -v -keystore upload-keystore.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias uploadkeytool asks for a store password, your name and organization, and a key
password. Write down both passwords and the alias upload.
Keep a backup
Store the keystore and its passwords in a password manager or another secure place outside Oore. If you lose the upload key, you must ask Google to reset it before you can publish updates.
2. Create the release pipeline
-
Open the project and its Pipelines tab, then select Add pipeline.
-
Under Start from, select Release Android. The pipeline is named
Release Android, and Enable release signing is already turned on under Signing > Android. -
Under Signing > Android, select the keystore file for Release keystore (.jks). Enter Release key alias, Release store password, and Release key password.
-
Under Build > Commands, open Per-platform arguments and overrides. In Android command override, enter:
flutter build appbundle --release --build-number=$OORE_VERSION_CODEOore’s default Android command builds an APK. This command builds the App Bundle that Google Play needs instead.
$OORE_VERSION_CODEgives every build a higher version code than the last, which Google Play requires. -
Select Create pipeline.
Oore uploads the keystore, then opens Run build with the new pipeline selected.
3. Run the release build
Check the Branch, then select Run build.
Your repository’s commands never see the keystore or its passwords. After
Flutter builds the bundle, the runner signs it with your upload key in a
separate step named android-sign, outside the repository checkout. Any
signature the Gradle build added is replaced.
Check the result
The build ends as Succeeded, and its log shows an android-sign step. Under
Artifacts, download app-release.aab, then check its signature:
JAVA_BIN="/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin"
"$JAVA_BIN/jarsigner" -verify app-release.aab
"$JAVA_BIN/keytool" -printcert -jarfile app-release.aabjarsigner prints jar verified. It also warns that the certificate is
self-signed. Upload keys are self-signed, so you can ignore that warning.
keytool prints an Owner line with the name you entered in step 1.
Troubleshooting
Oore rejects the signing profile when you save
The keystore file, alias, and passwords must all belong to the same keystore.
Check the alias with "$JAVA_BIN/keytool" -list -keystore upload-keystore.jks.
The build fails with “artifact patterns matched no files”
The command did not produce a bundle under build/app/outputs/bundle/release.
Check that the override is exactly the command above, without a
--flavor. The template also keeps an APK pattern next to the .aab
pattern. That is fine: a build fails only when every pattern matches
nothing. A flavored app writes its bundle to a folder named after the flavor
instead. See Flavored apps for the
artifact pattern to use.
Gradle fails because it cannot find a local key.properties
The repository’s Gradle file loads signing details that exist only on
developer machines. Make it skip local signing when CI is true, as in
Configure Gradle for Android signing. Oore sets
CI=true for repository commands.