5.9 KiB
Background — what already exists in this repo
The Android AAB build-and-upload pipeline is already fully automated. The user does not need to drop a file into Play Console at all — the same machinery that publishes to internal can publish to the closed-testing track.
ci/main.go:952—PublishAndroid()builds a release AAB, stamps the versionCode (time.Now().Unix()), re-signs with the upload key, and uploads to Play Store.ci/main.go:899—UploadToPlayStore()invokesscripts/deploy_playstore.pyinside a container.scripts/deploy_playstore.py:14— pinned toTRACK = "internal". This is the only thing limiting the existing pipeline to internal testing.Taskfile.yml:230—task publish-androidwraps the Dagger call..forgejo/workflows/deploy.yml—deploy-playstorejob runs every hour (0 * * * *) and callstask publish-androidwhenever android-relevant files changed since the last successful deploy.Taskfile.yml:214—task build-android-bundlebuilds an unsigned AAB locally tobuild/app/outputs/bundle/release/app-release.aab. The signed AAB built byPublishAndroidlives only inside Dagger and is not currently exported to disk on the runner.
Recommended path (A): publish to the closed-testing track from CI — no manual upload
This piggybacks on what's already running every hour.
-
In Play Console, decide which track maps to "closed testing":
- The built-in
alphatrack is treated as closed testing by the API. - Or create a custom closed-testing track (e.g.
closed-testers) under Testing → Closed testing → Create track. Configure the testers list there. Note the track ID exactly as it appears in the URL/console.
- The built-in
-
Edit
scripts/deploy_playstore.pyso the upload goes to both internal and the closed track in the same Play edit (one commit, atomic). Concretely:- Replace
TRACK = "internal"(line 14) withTRACKS = ["internal", "alpha"](or["internal", "<custom-track-id>"]). - Replace the single
track_resp = session.put(... /tracks/{TRACK} ...)block (lines 97-101) with a loop that PUTs the sameversionCodes: [version_code]payload to each track inTRACKSbefore the commit at line 104. - Internal stays as the smoke-test target; the closed track is what gets the testers.
- Replace
-
Update the test fixture in
scripts/test_deploy_playstore.py(and any test forverify_playstore_deploy.py) to expect the new track list. Runpython3 scripts/test_deploy_playstore.pylocally. -
Trigger a deploy: push the change to
mainand either wait for the next hourly run orworkflow_dispatch.forgejo/workflows/deploy.ymlfrom the Forgejo UI. Note thatdeploy.yml:114already includesscripts/deploy_playstore\.pyin the change-detection regex, so any normal hourly tick after the merge will pick it up. -
Result: closed-testing release appears in Play Console with the AAB pre-attached. Reviewers only need to edit the release notes (already generated by
task generate-changelog— seeTaskfile.yml:232) and click "Roll out". No "Drop app bundles here" step.
Caveat — first time a track is used, Google requires that internal testing has been previously published; that is already true here, so no blocker.
Alternative path (B): one-shot manual drop — export the signed AAB as a CI artifact
Use this if you want to do this single closed-testing release by hand and decide on full automation later.
-
Modify
ci/main.goto expose the signed AAB as a file return: add a thin wrapper exporting the result ofSignAndroidBundle(StampAndroidVersionCode(BuildAndroidRelease(commitHash), versionCode), ...)as*dagger.File(something likeSignedAndroidBundle(...) *dagger.File). The existingPublishAndroiddiscards the signed bundle after upload, so a separate entry point is cleaner than reshaping it. -
Add a
Taskfile.ymltargetbuild-android-bundle-signedthat calls the new Dagger function with-o build/app/outputs/bundle/release/app-release.aab(mirroringbuild-android-bundleatTaskfile.yml:218). -
Add to
.forgejo/workflows/deploy.ymldeploy-playstorejob (aftertask publish-android) — or to a new dedicated job — these two steps:- name: Export signed AAB locally env: { DAGGER_NO_NAG: "1" } run: task build-android-bundle-signed - name: Upload AAB artifact uses: actions/upload-artifact@v4 with: name: app-release-aab path: build/app/outputs/bundle/release/app-release.aab if-no-files-found: error -
Trigger the workflow (
workflow_dispatch), then downloadapp-release-aabfrom the run's artifacts page on Forgejo. Drop it into Play Console.
Risk to flag: the version code stamped via the artifact path must be strictly greater than every version code already on internal. Since the same Unix-timestamp scheme is used everywhere this naturally holds, but if the artifact run and a regular publish run race, the closed release may fail with "Version code N has already been used." Easy fix: only run one or the other.
Alternative path (C): build locally — fastest one-off
export ANDROID_KEYSTORE_BASE64=... # same secret used by CI
bash scripts/build_android_bundle_local.sh
AAB lands at build/app/outputs/bundle/release/app-release.aab. Note this script uses fvm flutter build appbundle directly and does not match the Dagger pipeline's stamp/sign step exactly — the keystore path is wired via ANDROID_KEYSTORE_PATH and the signing config in android/app/build.gradle.kts reads from there. Verify the bundle is correctly signed (jarsigner -verify -verbose -certs) before uploading.
Recommendation
Go with path A. The diff is small (~10 lines in deploy_playstore.py + test update), reuses the build-and-sign pipeline that has already been proven in deploy.yml for months, and removes the "Drop app bundles here" step entirely instead of just satisfying it once.