* fix(release): require platform asset digests * fix(release): preserve immutable closeout replay * fix(release): verify stable closeout asset digests * fix(release): preserve strict closeout replay * fix(release): validate recorded appcast snapshot
6.9 KiB
OpenClaw Android Versioning
Android release builds use pinned app metadata instead of auto-bumping build.gradle.kts.
Version model
apps/mobile/version.jsonis the shared mobile gateway version source.apps/android/version.jsonis the committed Android store version/code source.versionis the PlayversionNameand uses CalVer:YYYY.M.D.versionCodeusesYYYYMMDDNN, where phone build numberNNis01through49.- The matching Wear APK reserves
51through99by adding50to the pinned phoneversionCode; Play requires a unique code per form factor under the shared application ID. apps/android/Config/Version.propertiesis generated fromversion.jsonand read by Gradle.apps/ios/CHANGELOG.mdsupplies shared mobile release notes.apps/android/fastlane/metadata/android/en-US/release_notes.txtis generated from the pre-cut iOSUnreleasedsection, then checked against the exact App Store section during iOS finalization.apps/android/CHANGELOG.mdremains historical Android release documentation; the shared mobile cutter does not modify or read it.
Examples:
version = 2026.6.2versionCode = 2026060201- matching Wear
versionCode = 2026060251 - another upload on the same release train:
versionCode = 2026060202
Commands
pnpm android:version
pnpm android:version:check
pnpm android:release:signing:plan
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pull
pnpm android:release:preflight
Release Workflow
- Add shared mobile notes under
apps/ios/CHANGELOG.md## Unreleased. - Prepare the intended mobile version:
node --import tsx scripts/mobile-release-version.ts --prepare --version 2026.8.2 --write. - Run the live iOS planner, then finalize the shared release with its JSON plan.
- Run
pnpm android:version:checkto verify the committed Android properties and release notes. This command never writes release metadata. - Run
MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pullto materialize encrypted Android signing assets fromapps-signing. - Run
pnpm android:release:preflightto validate Play auth, signing, committed cutter outputs, and release notes. - Run
pnpm android:screenshotsto refresh phone and Wear OS Google Play screenshots with the script-managed Pixel 2 and Wear OS Large Round emulators. - Run
pnpm android:release:archiveto produce the signed phone Play AAB, Wear AAB, and third-party APK. - Run
pnpm android:release:uploadto upload metadata, screenshots, the phone AAB, and the Wear AAB to their phone andwear:tracks in one atomic Google Play edit. - For a regular final or correction OpenClaw release whose tagged Android pin matches the stable train, let
OpenClaw Release Publishdispatch the protectedAndroid Releaseworkflow after core npm publishes successfully. A mismatched pin records an explicit skip. The workflow builds the signed third-party APK from the exact tag and attaches the verified APK, checksum manifest, and GitHub provenance; publication may finish after the GitHub release becomes public. Before tagging a correction with its own package version, increment the pinnedversionCode; the workflow verifies it is higher than the preceding final or correction APK. A same-commit fallback correction reuses the base release's verified APK and adds provenance for the correction tag. - Complete production rollout manually in Google Play Console when needed.
pnpm android:version:sync and pnpm android:version:pin are retired release
entry points. They fail without writing; use the shared mobile cutter for every
version, code, properties, or release-note change.
The check command can verify the frozen pre-cutter Android baseline against its
exact historical changelog entry. Fastlane release lanes additionally require
the Android pin to match apps/mobile/version.json, so that baseline cannot be
uploaded as a new mobile release.
If pnpm android:release:upload fails, stop at that failure. Do not continue by
uploading archived artifacts through pnpm android:release:archive,
pnpm android:release:metadata, direct Fastlane lanes, Gradle release artifacts,
Google Play API mutation commands, or Play Console mutation commands. Fix the
failing release-lane step, then rerun pnpm android:release:upload.
The third-party flavor is archived as a signed APK for non-Play distribution. The Play release lane never uploads it. Official GitHub distribution is owned only by .github/workflows/android-release.yml, which publishes regular final and correction tags through the protected android-release environment as OpenClaw-Android.apk.
Release SHA tracking
Successful Play build uploads create a non-tag Git ref that records the source commit for the uploaded store build:
refs/openclaw/mobile-releases/android/<versionName>-<versionCode>
Example:
refs/openclaw/mobile-releases/android/2026.6.10-2026061008
These refs are intentionally outside refs/tags/* and refs/heads/*. They do
not appear on GitHub release or tag pages, and they do not participate in the
core OpenClaw release machinery.
pnpm android:release:upload checks the ref before uploading the Play build and
records it only after the atomic phone and Wear Play edit commits. Existing refs are
immutable: the same ref at the same SHA is accepted, while the same ref at a
different SHA fails. GOOGLE_PLAY_VALIDATE_ONLY=1 still checks the ref but does
not record it because no Play build is published.
Do not create this ref after a manual fallback upload. The ref is release-lane
evidence, not a repair mechanism for a failed pnpm android:release:upload run.
Useful direct commands:
pnpm mobile:release:preflight -- --platform android --version 2026.6.10 --version-code 2026061008
pnpm mobile:release:resolve -- --platform android --version 2026.6.10 --version-code 2026061008
Signing model
apps/android/Config/ReleaseSigning.json pins the Android signing assets in the shared private apps-signing repo. The Android pipeline uses the same MATCH_PASSWORD release-owner secret as iOS, but the Android files are managed by scripts/android-release-signing.mjs instead of Fastlane match.
sync:pull decrypts the Play upload keystore and Gradle signing properties into apps/android/build/release-signing/. That directory is gitignored, and Fastlane exports the materialized values as Gradle project properties for the current release command.
If MATCH_PASSWORD is not set, the existing manual Gradle-property signing path still works: provide OPENCLAW_ANDROID_STORE_FILE, OPENCLAW_ANDROID_STORE_PASSWORD, OPENCLAW_ANDROID_KEY_ALIAS, and OPENCLAW_ANDROID_KEY_PASSWORD through your local Gradle user properties before running release tasks.
Agent-driven releases must not use those lower-level signing and upload surfaces
to bypass a failed pnpm android:release:upload attempt. Report the failing
step and wait for maintainer direction instead.