Structured, gated release flow: one command + version guard, restore portal-kit.zip#40
Merged
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 theversion.jsonbump (devices never see the update) and misnaming the APK (the stablelatest/download/immortal.apk404s → self-update breaks fleet-wide). Recent releases also droppedportal-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…"ghauthed; clean tree on an up-to-datemain; signing key present; build-tools found; tag free.versionCode(+1)/versionName/notesin both gradle andversion.json, then re-check they agree.aaptconfirms the APK's version matches what was written (no stale build);apksignerconfirms it's signed with the same key as the publishedimmortal.apk(a key change silently bricks self-update).portal-kit.zip— built from the committedprovisioning/tree viagit archive(only tracked files; no.DS_Store,platform-tools/, downloaded APKs, orbackups/secrets).immortal.apk+portal-kit.zip→ publish → verify bothlatest/download/URLs resolve and the publishedversionCodeis correct.Drafts aren't "latest", so
latest/downloadkeeps resolving to the prior release until the new assets are live.Drift blocked in CI
scripts/check-version-sync.sh+.github/workflows/release-guard.ymlfail any change where gradle ≠version.json,apkUrlisn't the stable URL, ornotesis empty.Also
portal-kit.zip; drops the versioned-APK copy. A release now attaches exactly two assets.provisioning/.gitattributesexport-ignores the kit's own dotfiles so they don't ship.scripts/publish-release.sh(folded in); documents the flow indocs/releasing.md.Verification
check-version-sync.sh: passes clean; fails on injected drift (gradle bumped,version.jsonnot) 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/apksignerparsing confirmed).portal-kit.ziparchive verified to exclude.gitignore/.gitattributesand contain exactly the kit files.🤖 Generated with Claude Code