Skip to content

ci: fail closed if a pre-release tag would publish to Play production - #245

Merged
ErikBjare merged 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:ci/assert-play-track
Aug 30, 2026
Merged

ci: fail closed if a pre-release tag would publish to Play production#245
ErikBjare merged 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:ci/assert-play-track

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Why

#243 reported a GitHub pre-release (v0.14.0b2) reaching production Play devices. Erik confirmed that was an intentional Play Console promotion from the internal track, not a CI mapping bug (comment).

The CI mapping itself is already correct after #182: X.Y.Zproduction, anything else → internal. What was missing is a fail-closed assert at publish time, so a later regression (or copying the leftover test-job SUPPLY_TRACK: production env onto the publish job) cannot silently ship a pre-release tag to production.

This does not close #243. Play Console can still promote a track by hand; that is how b2 reached production. The remaining user-facing work on that issue is the bucket-migration path (aw-android-test-bucket-migration-merge / #244), not CI.

What changed

  • scripts/assert-play-track.sh — resolve + assert + --self-test (stable v0.14.0 → production; v0.14.0b2 / dev / rc → internal; production + pre-release tag → hard fail).
  • release-fastlane uses the script to set SUPPLY_TRACK, writes tag+track to the job summary, asserts after resolve, and asserts again immediately before fastlane supply run.
  • Test job runs --self-test on every PR and drops the unused job-level SUPPLY_TRACK: production (it never reached fastlane).

Overlap

#212 also edits build.yml (signing hardening). Different hunks; rebase this if #212 lands first.

Test plan

  • bash scripts/assert-play-track.sh --self-test
  • resolve v0.14.0b2internal
  • assert v0.14.0b2 production → exit 1
  • CI: Test job Assert Play-track resolver green on this PR

The tag→track mapping already sends X.Y.Z to production and everything
else to internal (ActivityWatch#182). This makes that contract executable: resolve
via scripts/assert-play-track.sh, write the track into the job summary,
and refuse to run supply when a pre-release tag would hit production.

Also drop the leftover test-job `SUPPLY_TRACK: production` env (it never
reached fastlane) so it cannot be copied onto the publish job.

Git-Session-Id: 5802
@greptile-apps

greptile-apps Bot commented Aug 29, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a fail-closed Play-track resolver and validates the selected track at the publication boundary.

  • Stable tags resolve to production, while pre-release tags resolve to internal.
  • The workflow passes the validated track explicitly to Fastlane with --track.
  • A self-test exercises the resolver and rejection cases on every PR.

Confidence Score: 5/5

The PR appears safe to merge.

The prior missing-track handoff is fixed because the value validated immediately before publication is now passed explicitly to Fastlane, and no blocking failure remains.

Important Files Changed

Filename Overview
.github/workflows/build.yml Resolves and validates the Play track, then passes it explicitly to the Fastlane upload command.
scripts/assert-play-track.sh Implements stable-tag classification, fail-closed track validation, and focused self-tests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    Tag[Release tag] --> Resolve[Resolve Play track]
    Resolve --> Assert[Assert tag-track contract]
    Assert --> Secrets[Restore Play credentials]
    Secrets --> Reassert[Reassert at publish boundary]
    Reassert --> Fastlane[Fastlane supply with explicit track]
    Fastlane --> Play[Google Play selected track]
Loading

Reviews (2): Last reviewed commit: "ci: pass asserted Play track to fastlane..." | Re-trigger Greptile

Comment thread .github/workflows/build.yml
supply defaults to production. The historical path was SUPPLY_TRACK
via GITHUB_ENV; pass --track explicitly so the asserted value is the
one that actually uploads.

Git-Session-Id: 5802
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click.

This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted.

@ErikBjare
ErikBjare merged commit 078e3ec into ActivityWatch:master Aug 30, 2026
8 checks passed
Judemasic added a commit to Judemasic/aw-android that referenced this pull request Sep 2, 2026
Takes ActivityWatch#250 (shared DeviceHostname helper), ActivityWatch#242, ActivityWatch#234, ActivityWatch#244, ActivityWatch#248, ActivityWatch#245.

Deliberately NOT taken: ActivityWatch#249's Kotlin half. It declares
  private external fun setDataDir(path: String)
and calls it from SyncInterface's init block, but this fork's android.rs
exports no Java_..._SyncInterface_setDataDir - the constructor would throw
UnsatisfiedLinkError and SyncScheduler would disable itself, which is Blocker 6
verbatim. This fork already gets the same fix via XDG_DATA_HOME (step 1.0b),
which needs no new JNI symbol. The refusal is documented at the call site so
the next merge re-checks it.

Submodule pointer kept at our fork (c6f7df2); upstream's 2ded7d7 has none of
the aw-sync fixes from Phase 1.

getDeviceName() now delegates to upstream's deviceHostname(), which is
byte-for-byte equivalent to the implementation it replaces - no device
directory in the shared folder changes name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Releases marked "pre-release" on github seem to be getting automatically pushed to the Play Store & auto-installed on devices

2 participants