Skip to content
Agentmart
Submit a tool

android-release-play

Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.

Skill by zakariaf

IndexedMIT★ 11
Write a review

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.

We use your email only for this newsletter. Privacy

About

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).

No 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 code
  • codeload.github.com · found in install command
  • googleapis.com · found in code
  • oauth2.googleapis.com · found in code
  • schemas.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

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
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from zakariaf

Cite this page

android-release-play skill. Agent Mart. https://agentmart.dev/skills/zakariaf-android-release-play (last updated 2026-10-24)