Skip to content

Structured, gated release flow: one command + version guard, restore portal-kit.zip#40

Merged
starbrightlab merged 1 commit into
mainfrom
claude/release-cut-tooling
Jun 22, 2026
Merged

Structured, gated release flow: one command + version guard, restore portal-kit.zip#40
starbrightlab merged 1 commit into
mainfrom
claude/release-cut-tooling

Conversation

@starbrightlab

Copy link
Copy Markdown
Owner

Cutting a release was a 6-step manual checklist across three files (app/build.gradle.kts, version.json, the GitHub Release) with no gate that the pieces agreed. The recurring failures: forgetting the version.json bump (devices never see the update) and misnaming the APK (the stable latest/download/immortal.apk 404s → self-update breaks fleet-wide). Recent releases also dropped portal-kit.zip (missing since v1.40) and shipped a stray versioned APK.

One command, gated at every step

scripts/cut-release.sh 1.44 "release notes…"
  1. Preflightgh authed; clean tree on an up-to-date main; signing key present; build-tools found; tag free.
  2. Lockstep bumpversionCode(+1)/versionName/notes in both gradle and version.json, then re-check they agree.
  3. Build + verifyaapt confirms the APK's version matches what was written (no stale build); apksigner confirms it's signed with the same key as the published immortal.apk (a key change silently bricks self-update).
  4. portal-kit.zip — built from the committed provisioning/ tree via git archive (only tracked files; no .DS_Store, platform-tools/, downloaded APKs, or backups/ secrets).
  5. Commit → tag → push → draft release → upload exactly immortal.apk + portal-kit.zip → publish → verify both latest/download/ URLs resolve and the published versionCode is correct.

Drafts aren't "latest", so latest/download keeps resolving to the prior release until the new assets are live.

Drift blocked in CI

scripts/check-version-sync.sh + .github/workflows/release-guard.yml fail any change where gradle ≠ version.json, apkUrl isn't the stable URL, or notes is empty.

Also

  • Restores portal-kit.zip; drops the versioned-APK copy. A release now attaches exactly two assets.
  • provisioning/.gitattributes export-ignores the kit's own dotfiles so they don't ship.
  • Retires scripts/publish-release.sh (folded in); documents the flow in docs/releasing.md.

Verification

  • check-version-sync.sh: passes clean; fails on injected drift (gradle bumped, version.json not) with a clear message + non-zero exit.
  • cut-release.sh: rejects missing/empty/bad args; refuses to release off a feature branch (preflight). Full publish path not exercised (it cuts a real release) but each stage is syntax-checked and the verify gates are unit-tested against the real APK (aapt/apksigner parsing confirmed).
  • portal-kit.zip archive verified to exclude .gitignore/.gitattributes and contain exactly the kit files.

🤖 Generated with Claude Code

Cutting a release was a 6-step manual checklist across 3 files with no gate that the pieces agreed; the recurring failures were forgetting the version.json bump and misnaming the APK.

- scripts/cut-release.sh: one command does the whole release with a hard gate at each step — preflight (clean synced main, gh auth, signing key, build-tools), lockstep bump of versionCode(+1)/versionName/notes in app/build.gradle.kts AND version.json, signed build, then VERIFY the APK's version (aapt) and that it's signed with the SAME key as the published immortal.apk (apksigner). Builds portal-kit.zip from the committed provisioning/ tree (git archive), then draft-release → upload exactly immortal.apk + portal-kit.zip → publish → verify both latest/download URLs and the published versionCode.
- scripts/check-version-sync.sh + .github/workflows/release-guard.yml: CI fails any change where gradle and version.json disagree, apkUrl isn't the stable URL, or notes is empty.
- Restores portal-kit.zip (the provisioning kit), an asset missing since v1.40, and drops the versioned-APK copy that broke things. .gitattributes export-ignores the kit's own .gitignore.
- Retire scripts/publish-release.sh (folded into cut-release.sh); document the flow in docs/releasing.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@starbrightlab starbrightlab merged commit 93014df into main Jun 22, 2026
4 checks passed
@starbrightlab starbrightlab deleted the claude/release-cut-tooling branch June 22, 2026 14:15
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.

1 participant