Social LiveDocumentationPLATFORM DOCS

Android release build & Drive upload

One script bumps the build number, builds a signed Android release, and uploads it to Google Drive for testers.

Last updated Thu Jul 16 2026 00:00:00 GMT+0000 (Coordinated Universal Time)

TL;DR -scripts/release-android.sh runs the entire Android release loop from a developer machine: it bumps the build number, builds a signed app, checks the file, and uploads it to a shared Google Drive folder for testers.

Who this is for -DevOps & Operations and Mobile: the day-to-day command and flags. QA: how to grab the latest tester build from Drive. Engineering: the one-time signing/rclone setup and the step-by-step of what the script does.

Getting an Android build into testers' hands has several fiddly steps — incrementing a version number, compiling a signed release, making sure the file is really the fresh one, and sharing it. scripts/release-android.sh does all of that in one command so nobody has to remember the sequence.

"Signed" means the app is stamped with a cryptographic key that proves it came from us; Android and Google Play refuse unsigned release builds. "rclone" is a command-line tool for copying files to cloud storage -here, Google Drive.

Quick reference

The common ways to run it. With no flags it bumps the build number, builds an APK, and uploads to Drive.

scripts/release-android.sh                 # bump +N, build APK, upload to Drive
scripts/release-android.sh --aab           # build an app bundle (Play upload) instead
scripts/release-android.sh --no-bump       # keep the current build number
scripts/release-android.sh --build-number 42   # set an explicit build number
scripts/release-android.sh --no-upload    # build only, skip the Drive upload

These environment variables tune where and how it uploads:

Env varDefaultMeaning
SL_DRIVE_REMOTEgdriverclone remote name to upload through
SL_DRIVE_FOLDERsocial liveDrive folder path (created on first upload if missing)
SL_API_BASE_URL(app default)optional API_BASE_URL dart-define -point the build at a non-default backend (e.g. the test stack)
SL_SIGNALING_URL(app default)optional SIGNALING_URL dart-define, pairs with SL_API_BASE_URL

One-time setup

You only do this section once per machine. It sets up the signing key and the Google Drive connection the script relies on.

1. Upload keystore (release signing)

The keystore is the signing key file. Without it the build can't be signed for release, so the script refuses to run rather than produce a debug-signed app.

The release build is signed with the upload keystore configured in android/key.properties (git-ignored). Without it the Gradle config falls back to debug signing, so the script refuses to run.

# Generate the keystore once (keep the .jks and passwords in the password manager):
keytool -genkey -v -keystore ~/social-live-upload.jks \
  -keyalg RSA -keysize 2048 -validity 10000 -alias upload

# Then create android/key.properties from the template:
cp android/key.properties.example android/key.properties
# ...and fill in storeFile / storePassword / keyPassword / keyAlias.

Never commit key.properties or the .jks file.

2. rclone → Google Drive auth

This connects rclone to your Google Drive so the script can upload. It opens a browser once to sign you in.

brew install rclone
rclone config

In the interactive config: n (new remote) → name it gdrive (or set SL_DRIVE_REMOTE to whatever you choose) → storage type drive → accept the defaults, leave client id/secret blank → scope drive (full access, needed to create the upload folder and share links) → no service account → auto-config y opens a browser for the Google sign-in. Verify with:

rclone lsd gdrive:

The token is stored in ~/.config/rclone/rclone.conf -per-machine, never in the repo.

What the script does, step by step

For the curious or the debugging: here is exactly what happens on each run, so nothing is a black box.

  1. Preflight -fails fast if flutter is missing, android/key.properties is absent (debug-signing guard), or (unless --no-upload) rclone or the configured remote is missing.
  2. Bump -reads version: X.Y.Z+N from pubspec.yaml and rewrites it with N+1 (or the value from --build-number). The +N build number is shared with iOS: it must be unique per store upload on both platforms, so a bump here also "uses up" the number for the next iOS upload -that is intended, the two platforms stay on one counter. X.Y.Z is never touched; change it by hand when starting a new store version.
  3. Build -flutter build apk --release (default) or flutter build appbundle --release with --aab. Outputs:
    • APK: build/app/outputs/flutter-apk/app-release.apk
    • AAB: build/app/outputs/bundle/release/app-release.aab
  4. Verify -asserts the artifact exists and was written after the build started (a stale file from an earlier run fails the check), then copies it to build/social_live-vX.Y.Z-N.apk|aab so every upload is uniquely named.
  5. Upload -rclone copy to $SL_DRIVE_REMOTE:$SL_DRIVE_FOLDER, then lists the folder back to prove the file landed. rclone itself verifies size/checksum during copy.
  6. Share link -best-effort rclone link; prints an anyone-with-the-link URL for handing to testers. Non-fatal if the remote does not permit link creation.

Choosing APK vs AAB

Two output formats. Use an APK for direct install to testers; use an AAB only when you're feeding Google Play.

  • APK (default): directly installable -right choice for Drive distribution to testers or sideloading onto devices.
  • AAB (--aab): what Google Play requires for store uploads. Not installable directly; use it when archiving the exact bundle sent to Play.

Troubleshooting

Common failures and their fixes.

  • android/key.properties missing -do the keystore setup above; the template is android/key.properties.example.
  • rclone remote 'gdrive:' not configured -run rclone config; if you named the remote differently, export SL_DRIVE_REMOTE=<name>.
  • rclone auth errors after a long idle period -the OAuth token can be revoked; run rclone config reconnect gdrive: to re-authorize.
  • upload verification failed -the copy did not land (network drop or Drive quota). Re-run with --no-bump so the build number is not consumed twice: scripts/release-android.sh --no-bump.
  • Build fails on signing -check the values in android/key.properties and that storeFile points at an existing .jks; the Gradle wiring is in android/app/build.gradle.kts (signingConfigs).
  • Play rejects the build number -someone uploaded that +N already; run again (the default bump picks the next number) or set --build-number explicitly.