feat(playstore): also publish AAB to closed-testing (alpha) track #546

Merged
guettlibot merged 1 commits from refs/pull/546/head into main 2026-06-08 16:56:00 +00:00
4 changed files with 42 additions and 11 deletions
+1 -1
View File
@@ -544,7 +544,7 @@ tasks:
- sops exec-env secrets.enc.yaml 'bash scripts/build_android_bundle_local.sh' - sops exec-env secrets.enc.yaml 'bash scripts/build_android_bundle_local.sh'
deploy-android-bundle: deploy-android-bundle:
desc: Build release AAB and upload to Play Store internal track (local/fvm) desc: Build release AAB and upload to Play Store internal + closed-testing tracks (local/fvm)
deps: [build-android-bundle-local] deps: [build-android-bundle-local]
dotenv: [".env"] dotenv: [".env"]
cmds: cmds:
+1 -1
View File
@@ -896,7 +896,7 @@ func withGoCache(c *dagger.Container) *dagger.Container {
WithEnvVariable("GOMODCACHE", "/home/ci/go/pkg/mod") WithEnvVariable("GOMODCACHE", "/home/ci/go/pkg/mod")
} }
// UploadToPlayStore uploads a pre-built AAB to the Play Store internal track. // UploadToPlayStore uploads a pre-built AAB to the Play Store internal and closed-testing (alpha) tracks.
func (m *Ci) UploadToPlayStore( func (m *Ci) UploadToPlayStore(
ctx context.Context, ctx context.Context,
aab *dagger.File, aab *dagger.File,
+16 -9
View File
@@ -1,5 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Upload an Android App Bundle to the Google Play Store internal track.""" """Upload an Android App Bundle to the Google Play Store.
The bundle is published to every track in ``TRACKS`` within a single Play edit,
so internal testing and closed testing share the same version code. ``alpha``
is what the Play Console labels "Closed testing"; publishing there removes the
need to manually drag-and-drop the AAB into the closed-testing release form.
"""
import json import json
import os import os
@@ -11,7 +17,7 @@ from google.oauth2 import service_account
PACKAGE_NAME = "de.sharedinbox.mua" PACKAGE_NAME = "de.sharedinbox.mua"
AAB_PATH = "build/app/outputs/bundle/release/app-release.aab" AAB_PATH = "build/app/outputs/bundle/release/app-release.aab"
TRACK = "internal" TRACKS = ("internal", "alpha")
_BASE = "https://androidpublisher.googleapis.com/androidpublisher/v3/applications" _BASE = "https://androidpublisher.googleapis.com/androidpublisher/v3/applications"
_UPLOAD_BASE = "https://androidpublisher.googleapis.com/upload/androidpublisher/v3/applications" _UPLOAD_BASE = "https://androidpublisher.googleapis.com/upload/androidpublisher/v3/applications"
_MAX_UPLOAD_ATTEMPTS = 3 _MAX_UPLOAD_ATTEMPTS = 3
@@ -94,19 +100,20 @@ def main():
version_code = bundle["versionCode"] version_code = bundle["versionCode"]
print(f"Uploaded AAB, version code: {version_code}") print(f"Uploaded AAB, version code: {version_code}")
track_resp = session.put( for track in TRACKS:
f"{_BASE}/{PACKAGE_NAME}/edits/{edit_id}/tracks/{TRACK}", track_resp = session.put(
json={"releases": [{"versionCodes": [version_code], "status": "completed"}]}, f"{_BASE}/{PACKAGE_NAME}/edits/{edit_id}/tracks/{track}",
timeout=30, json={"releases": [{"versionCodes": [version_code], "status": "completed"}]},
) timeout=30,
track_resp.raise_for_status() )
track_resp.raise_for_status()
commit_resp = session.post( commit_resp = session.post(
f"{_BASE}/{PACKAGE_NAME}/edits/{edit_id}:commit", f"{_BASE}/{PACKAGE_NAME}/edits/{edit_id}:commit",
timeout=30, timeout=30,
) )
commit_resp.raise_for_status() commit_resp.raise_for_status()
print(f"Deployed version {version_code} to {TRACK} track") print(f"Deployed version {version_code} to tracks: {', '.join(TRACKS)}")
if __name__ == "__main__": if __name__ == "__main__":
+24
View File
@@ -95,6 +95,30 @@ class TestMainHappyPath(unittest.TestCase):
track_call = session.put.call_args_list[0] track_call = session.put.call_args_list[0]
self.assertIn("/tracks/", track_call[0][0]) self.assertIn("/tracks/", track_call[0][0])
def test_updates_all_configured_tracks(self):
session = self._run_main()
track_urls = [c[0][0] for c in session.put.call_args_list]
self.assertEqual(len(track_urls), len(deploy_playstore.TRACKS))
for track in deploy_playstore.TRACKS:
self.assertTrue(
any(url.endswith(f"/tracks/{track}") for url in track_urls),
f"no PUT to /tracks/{track} (saw {track_urls})",
)
def test_commits_after_all_track_updates(self):
session = self._run_main()
# All PUTs are track updates; commit is the second POST after the
# initial edit-create. Verify PUTs precede the commit by checking
# mock_calls order across both methods.
method_order = [c[0] for c in session.method_calls]
commit_idx = next(
i for i, m in enumerate(method_order)
if m == "post" and ":commit" in session.method_calls[i][1][0]
)
put_indices = [i for i, m in enumerate(method_order) if m == "put"]
self.assertEqual(len(put_indices), len(deploy_playstore.TRACKS))
self.assertTrue(all(i < commit_idx for i in put_indices))
class TestUploadRetry(unittest.TestCase): class TestUploadRetry(unittest.TestCase):
def _run_main(self, upload_side_effects, sleep_mock=None): def _run_main(self, upload_side_effects, sleep_mock=None):