android-release-play
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
Skill by zakariaf
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/zakariaf-android-release-play
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Node.js package
Network hosts it talks to 5
androidpublisher.googleapis.com· found in codecodeload.github.com· found in install commandgoogleapis.com· found in codeoauth2.googleapis.com· found in codeschemas.android.com· found in code
Files it touches
~/.claude/skills/android-release-play- Your home directory (~)
What's inside
- Skill name
android-release-play- Description
- Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout. Use when releasing or signing for Play, or an upload, rollout or version code fails. Not for eas.json (eas-build-and-submit).
- License
- MIT
- Instructions
- 194 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (60)
- SKILL.md
- play-facts.json · assets/
- shared.json · assets/
- template-placeholders.json · assets/
- app-config-local-lane.md · examples/
- release-run.md · examples/
- failure-playbook.md · references/
- human-steps.md · references/
- play-api.md · references/
- play-policy-and-artifact.md · references/
- release-pipeline.md · references/
- signing-and-keys.md · references/
- tracks-and-rollout.md · references/
- check-lib.mjs · scripts/
- check-play-release-setup.mjs · scripts/
- check-store-aab.mjs · scripts/
- aab-fixture.mjs · scripts/lib/
- aab.mjs · scripts/lib/
- release-facts.mjs · scripts/lib/
- ship-placeholders.mjs · scripts/lib/
- selftest.mjs · scripts/
- android-release.json · templates/contracts/
- android-release.schema.json · templates/contracts/
- with-upload-signing.test.ts · templates/plugins/
- with-upload-signing.ts · templates/plugins/
- system-clock.test.ts · templates/scripts/clock/
- system-clock.ts · templates/scripts/clock/
- play-client.test.ts · templates/scripts/release-android/
- play-client.ts · templates/scripts/release-android/
- play-credentials.test.ts · templates/scripts/release-android/
- play-credentials.ts · templates/scripts/release-android/
- release-android.ts · templates/scripts/release-android/
- release-args.test.ts · templates/scripts/release-android/
- release-args.ts · templates/scripts/release-android/
- release-plan.test.ts · templates/scripts/release-android/
- release-plan.ts · templates/scripts/release-android/
- rollout.test.ts · templates/scripts/release-android/
- rollout.ts · templates/scripts/release-android/
- upload-key.test.ts · templates/scripts/release-android/
- upload-key.ts · templates/scripts/release-android/
- version-code.test.ts · templates/scripts/release-android/
- version-code.ts · templates/scripts/release-android/
- app-variant.test.ts · templates/src/config/
- app-variant.ts · templates/src/config/
- aab.json · tests/bases/app/
- app.config.ts · tests/bases/app/
- android-release.json · tests/bases/app/contracts/
- settings.json · tests/bases/app/dot-claude/
- dot-gitignore · tests/bases/app/
- package.json · tests/bases/app/
- test-only-entry.ts · tests/bases/app/src/
- test-only.ts · tests/bases/app/src/
- tags.txt · tests/bases/app/
- CASES.json · tests/fixtures/check-play-release-setup/
- SUITE.json · tests/fixtures/check-play-release-setup/
- EXPECT.txt · tests/fixtures/check-play-release-setup/good/
- CASES.json · tests/fixtures/check-store-aab/
- SUITE.json · tests/fixtures/check-store-aab/
- EXPECT.txt · tests/fixtures/check-store-aab/good/
- real-tools.json · tests/
SKILL.md (first lines)
# Android release to Google Play
Takes one green commit to a signed Android App Bundle on a Google Play testing track, then to users by
staged rollout, on the EAS lane (EAS Build + EAS Submit, the default) or the local lane (Gradle + the
Play Developer API). Before anything reaches Google, the store-AAB gate reads the exact bundle and
proves what users would get: the registered upload key, a fresh versionCode, the production package,
target SDK 36, 16 KB pages, every permission accounted for, no test code and no template value.
Everything that needs a person is asked for, never worked around. Verified on 2026-10-14 with Expo
SDK 57 (React Native 0.86.3, targetSdk 36, AGP 8.12), eas-cli 24.10.0, aapt2 36.0.0, NDK r28.2 and
JDK 17; the iOS twin is `ios-release-testflight`.
## Rules that must hold
1. **Nothing reaches Google Play without the developer's word in this session, and uploads go only to
the contract's testing track.** `contracts/android-release.json` `uploadTrack` is `internal`, `alpha`
or `beta`, and `eas.json`'s Android submit track equals it. Why: an upload, a track change or a tag
push cannot be undone, and a production upload reaches users before anyone installed it.
(`contract`, `lane`)
2. **The gate passes on the exact bytes that are uploaded.** `check-store-aab.mjs --stamp` writes the
bundle's sha256 only after a PASS; the local `upload` refuses any other bytes, and the EAS lane
submits that file with `eas submit --path` after eas-build-and-submit's record check (never
`--latest`, never `--auto-submit`, for Android not even from the store profile). Why: a
bundle rebuilt or picked after the gate is an unchecked bundle. (`gated-upload`, `lane`: `eas.json`
pins the contract `applicationId`)
3. **A store bundle is signed by the registered upload key, never the debug key.** Play App Signing
holds the app-signing key; the contract registers the upload certificate's SHA-256 and the gate
compares it. Why: Play refuses debug-signed bundles and bundles from any other key, and React
Native's template signs release builds with the debug keystore. (`signing`)
4. **Keys and passwords never enter the repo, a log or the chat, and are never opened.** The EAS lane
keeps the keystore and the service-account key on EAS; the local lane keeps them in
`~/.android-keys/` (mode 600), passwords in `~/.gradle/gradle.properties`, read only by Gradle and
`play-credentials.ts`, never a repo `gradle.properties`, `keystore.properties` or `.env*`; git
ignores and agents are denied `*.jks`, `*.keystore`, service-account JSON. Why: whoever holds the upload key and the service account releases as the app.
(`secrets-outside`, `secrets-ignored`, `credentials-read`, `inline-secrets`)
5. **versionCode only goes up, is never reused, and Android counts its own.** EAS counts remotely
(`appVersionSource: remote`, `autoIncrement`); the local lane keeps one literal
`const ANDROID_VERSION_CODE = <n>;` feeding only `android.versionCode` (the iOS twin keeps its own
`IOS_BUILD_NUMBER`), bumped and committed by the build; uploads are tagged `vX.Y.Z+android.<n>` on
both lanes, and every bundle is above every Android upload tag. Why: Play refuses a versionCode it
has seen, forever, and one scheme per app keeps the tag order meaningful. (`version-code`,
`version-name`, `version-source`)
6. **A store bundle is the production variant: the contract's package, a matching embedded config, no
test-only code, not debuggable, no cleartext traffic.** Why: a lost `APP_VARIANT` ships the debug
menu or another app id, and these are invisible in the source. (`package`, `constants-variant`,
`test-code`, `debuggable`, `cleartext`)
7. **targetSdkVersion is 36 or higher, in the bundle and in the config.** Why: from 2026-08-31 Play
refuses new apps and updates below API 36; Expo SDK 57 targets 36, so only an override lowers it.
(`target-sdk`, `target-sdk-config`)
8. **Native libraries support 16 KB pages and ship 64-bit.** Every arm64-v8a/x86_64 `.so` has 16 KB
LOAD alignment, uncompressed libraries are 16 KB zip-aligned, every 32-bit library has its 64-bit
twin. Why: a 4 KB library crashes at start on 16 KB devices, and Play refuses such updates from
2027-02-01 and 32-bit-only code already. (`page-alignment`, `abi-64bit`)
9. **Every permission in the bundle is in the contract with its reason, and Play-declared ones name
their declaration.** Development-only permissions (`SYSTEM_ALERT_WINDOW`) are blocked for release.
Why: libraries add permissions silently, each changes the Data safety answers, and an undeclared
sensitive permission gets the release rejected. (`permissions`, `permission-declaration`)
10. **No template value ships.** No `com.example.*`, Google sample or all-zero AdMob id, `example.com`
link or `__NAME__` token in the package, the manifest or the embedded config; the AdMob app id is
present and the app's own. Why: they are format-valid and pass every other check. (`ship-placeholder`,
`ad-app-id`, `upload-cert` with `--release`)
11. **Production is reached only by staged rollout of an uploaded versionCode, at the contract's
steps; a bad release is halted, never rebuilt.** Why: a fraction limits the harm of a regression,
and a halted versionCode is spent. (`rollout`, `halt`, contract `rollout`)
12. **Stop and ask, never retry, on 401/403, an app not found, the API not enabled, a draft app, a used
version code or missing declarations; a `--rehearsal` is never release evidence.** Why: each
needs a person or a new number; retries lock accounts and burn numbers.
## Workflow
1. **Before the first release:** read [references/human-steps.md](references/human-steps.md) and
[references/signing-and-keys.md](references/signing-and-keys.md); ask the developer for every
missing human step in one message, each with its default.
2. **Install the templates** (each to the path its first line names; tests first, see them fail on an
assertion, then the module: `tdd-workflow`):
- both lanes: `templates/contracts/android-release.json` + schema; fill `applicationId`,
`permissions` (from the merged manifest), `lane`;
- local lane: `templates/scripts/release-android/` (CLI and seven tested modules),
`templates/scripts/clock/system-clock.ts` + test (the CLI's clock; synced, skip when present),
`templates/plugins/with-upload-signing.ts` + test, and the three app edits in
[examples/app-config-local-lane.md](examples/app-config-local-lane.md) (`ANDROID_VERSION_CODE`,
the plugin line, `"release:android": "node scripts/release-android/release-android.ts"`);
- EAS lane: `eas.json` comes from `eas-build-and-submit`; make its Android submit track equal
`uploadTrack`, and keep `--auto-submit` off Android.
Then `npm run -s check:fast`.
3. **Check the setup:** `node ${CLAUDE_SKILL_DIR}/scripts/check-play-release-setup.mjs .` until
`RESULT: PASS`; before a store build add `--release`.
4. **Build** with the developer's go: EAS (`eas build --platform android --profile production --non-interactive --freeze-credentials --json`,
eas-build-and-submit's `check-eas-build` on the record, then download its
`artifacts.applicationArchiveUrl` to `build/release-android/app-release.aab`) or local
(`npm run release:android -- build`). Steps and by-hand
commands: [references/release-pipeline.md](references/release-pipeline.md). Without a key yet,
rehearse (same reference, "Rehearsal without a key").
5. **Gate:** `node ${CLAUDE_SKILL_DIR}/scripts/check-store-aab.mjs . --aab <bundle> --version-code <n> --stamp build/release-android/aab-gate.json`
until `RESULT: PASS`. Each rule and its source: [references/play-policy-and-artifact.md](references/play-policy-and-artifact.md).
First release: register the printed fingerprint after confirming it.
6. **Upload** with the developer's word (name a pending schema version in the ask: run-migration
records it with `record-shipped` after the upload; design-review-workflow's `check-signoff.mjs` passes
first when the app uses it): `eas submit --platform android --profile production --path build/release-android/app-release.aab --non-interactive`
or `npm run release:android -- upload`; then the annotated tag
`vX.Y.Z+android.<n>`. Calls and stops:
[references/play-api.md](references/play-api.md).
7. **Roll out** later, each step with its own word:
[references/tracks-and-rollout.md](references/tracks-and-rollout.md).
8. **On any failure** match [references/failure-playbook.md](references/failure-playbook.md); a stop
goes to the developer as one message and is never retried.
9. **Report** (git-commits-and-reporting): version, versionCode, the gate's RESULT line, the track,
the tag, the human checks; model: [examples/release-run.md](examples/release-run.md).
## Definition of done
- [ ] The developer agreed to the upload in this session; nothing was pushed, promoted or submitted
without their word; production changed only by a contract rollout step.
- [ ] The bundle came from a clean, verified commit; the gate passed on those exact bytes with a stamp,
and the uploaded file's sha256 equals the stamp's.
- [ ] No key, password, service-account JSON or token appears in the repo, a log or the report, and
none was opened.
- [ ] The upload tag is annotated and local (pushed only with the developer's word).
- [ ] Before the first key exists: the rehearsal printed `REHEARSAL: not a release gate` and the
report lists it under "Not tested or not verified".From zakariaf/ReactNative-Skills, MIT.
More from zakariaf/ReactNative-Skills
- 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
architecture-and-boundaries
zakariaf
Places app code in the src/ layout and checks import boundaries: one-way zones, pure core, ports with fakes, one composition root.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
async-safety
zakariaf
Prevents silent async bugs: effect races, stale results, lost promises, leaked listeners and timers, AbortSignal gaps.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
audio-and-haptics-feedback
zakariaf
Builds SFX and haptics behind ports.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
auth-and-secure-storage
zakariaf
Builds sign-in - tokens in expo-secure-store, shared refresh, protected routes, OAuth PKCE, Apple sign-in.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
background-tasks
zakariaf
Builds expo-background-task jobs: idempotent, budgeted runs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
ci-pipeline-and-gates
zakariaf
Builds GitHub Actions CI for Expo apps - verify on every PR, SHA-pinned actions, pinned runners and Node, device lanes, manual EAS releases.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9
Signals
- Stars
- 11
- Forks
- 2
- Open issues
- 0
- Last commit (this folder)
- Oct 17, 2026 (11 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/zakariaf/ReactNative-Skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from zakariaf
- 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9
Cite this page
android-release-play skill. Agent Mart. https://agentmart.dev/skills/zakariaf-android-release-play (last updated 2026-10-24)