Skip to content

Android release signing, AAB build, and prerelease version codes - #107

Merged
diegurrio merged 4 commits into
mainfrom
feature/android-release-signing
Aug 16, 2026
Merged

Android release signing, AAB build, and prerelease version codes#107
diegurrio merged 4 commits into
mainfrom
feature/android-release-signing

Conversation

@diegurrio

Copy link
Copy Markdown
Collaborator

Adds the repository half of the signing work tracked in #106.

  • Release builds sign with an upload keystore when the environment provides one (HAWKEYE_UPLOAD_KEYSTORE, HAWKEYE_UPLOAD_KEYSTORE_PASSWORD, HAWKEYE_UPLOAD_KEY_ALIAS). Local, PR, and fork builds have no keystore and keep producing an unsigned APK, exactly as before.
  • versionCode gains a prerelease component: major*100000000 + minor*100000 + patch*100 + rc, where a final release takes 99, rcN takes N (1..98), and dev/ci builds take 0. v0.4.0-rc1 now yields 400001 instead of colliding with v0.4.0 (400099), which unblocks rc tags and Google Play uploads. Major is capped at 20 to stay under Play's version code ceiling of 2,100,000,000.
  • verify-release-apk.sh accepts --signed, which runs apksigner verify from the newest installed build-tools.
  • New verify-release-bundle.sh asserts the AAB contains both ABIs and all four asset trees.
  • The android.yml release-build job now runs assembleRelease bundleRelease, decodes the keystore from the ANDROID_UPLOAD_KEYSTORE_BASE64 secret when present, and verifies both artifacts, so the entire signing and bundling path is exercised on pushes to main before any tag exists.

Verified locally:

  • Unsigned build of 0.4.0-rc1 passes the existing checks with versionCode 400001.
  • --signed fails on an unsigned APK (DOES NOT VERIFY) and passes on a build signed with a throwaway test keystore.
  • The AAB check passes on a real bundleRelease output and fails when pointed at an APK.
  • Suffix parsing rejects beta1, rc0, rc99, and major 21 with clear messages; rc1, finals, dev, ci, and 20.999.999 configure cleanly.

The release.yml changes that consume this (signed asset name, Play upload) come in a follow-up so this lands independently. Part of #106.

Release builds sign with an upload keystore when the environment provides
one; local, PR, and fork builds keep producing an unsigned APK. The
versionCode gains a prerelease component so rc tags no longer collide with
their final release. verify-release-apk.sh learns --signed (apksigner), a
new verify-release-bundle.sh checks the AAB, and the android.yml
release-build job exercises the whole signing and bundling path on main
before any tag.
Scope the keystore secret to the decode step and branch later steps on
the exported path, reject trailing-hyphen versions instead of silently
taking the final release's code, treat a blank keystore env var as
absent and fail fast when the password or alias is missing, list
archive entries with unzip -Z1 so a file's on-disk path cannot satisfy
a content check, and verify the AAB's jar signature on the signed path
since Play rejects a bundle whose signature does not match the upload
key. Comments updated to match.
The derivation is release-critical pure logic, so it moves to
build-logic (VersionCode.kt) where a JUnit boundary-table test covers
every rule: rc ordering, the trailing-hyphen rejection, the rc99
collision, the dev and ci floor, and the major cap under Google Play's
version code ceiling. build-logic is an included build whose tests no
app-side task reaches, so android.yml runs them as an explicit step.
The secrets context is not available in any if condition, step-level
included, so the previous step-level secret guard failed the whole
workflow file at parse time. Presence is now computed once in the job
env as a boolean expression, which is permitted and yields only the
string true or false, so the guards work again while the keystore
material itself stays scoped to the decode step.
@diegurrio
diegurrio merged commit 3eee85d into main Aug 16, 2026
10 checks passed
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