Files
sharedinbox/ISSUE-535.md

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:952PublishAndroid() builds a release AAB, stamps the versionCode (time.Now().Unix()), re-signs with the upload key, and uploads to Play Store.
  • ci/main.go:899UploadToPlayStore() invokes scripts/deploy_playstore.py inside a container.
  • scripts/deploy_playstore.py:14 — pinned to TRACK = "internal". This is the only thing limiting the existing pipeline to internal testing.
  • Taskfile.yml:230task publish-android wraps the Dagger call.
  • .forgejo/workflows/deploy.ymldeploy-playstore job runs every hour (0 * * * *) and calls task publish-android whenever android-relevant files changed since the last successful deploy.
  • Taskfile.yml:214task build-android-bundle builds an unsigned AAB locally to build/app/outputs/bundle/release/app-release.aab. The signed AAB built by PublishAndroid lives only inside Dagger and is not currently exported to disk on the runner.

This piggybacks on what's already running every hour.

  1. In Play Console, decide which track maps to "closed testing":

    • The built-in alpha track 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.
  2. Edit scripts/deploy_playstore.py so the upload goes to both internal and the closed track in the same Play edit (one commit, atomic). Concretely:

    • Replace TRACK = "internal" (line 14) with TRACKS = ["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 same versionCodes: [version_code] payload to each track in TRACKS before the commit at line 104.
    • Internal stays as the smoke-test target; the closed track is what gets the testers.
  3. Update the test fixture in scripts/test_deploy_playstore.py (and any test for verify_playstore_deploy.py) to expect the new track list. Run python3 scripts/test_deploy_playstore.py locally.

  4. Trigger a deploy: push the change to main and either wait for the next hourly run or workflow_dispatch .forgejo/workflows/deploy.yml from the Forgejo UI. Note that deploy.yml:114 already includes scripts/deploy_playstore\.py in the change-detection regex, so any normal hourly tick after the merge will pick it up.

  5. 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 — see Taskfile.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.

  1. Modify ci/main.go to expose the signed AAB as a file return: add a thin wrapper exporting the result of SignAndroidBundle(StampAndroidVersionCode(BuildAndroidRelease(commitHash), versionCode), ...) as *dagger.File (something like SignedAndroidBundle(...) *dagger.File). The existing PublishAndroid discards the signed bundle after upload, so a separate entry point is cleaner than reshaping it.

  2. Add a Taskfile.yml target build-android-bundle-signed that calls the new Dagger function with -o build/app/outputs/bundle/release/app-release.aab (mirroring build-android-bundle at Taskfile.yml:218).

  3. Add to .forgejo/workflows/deploy.yml deploy-playstore job (after task 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
    
  4. Trigger the workflow (workflow_dispatch), then download app-release-aab from 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.