@shardev/play-store-publisher
v0.1.1
Published
Publish Android App Bundles (.aab) to Google Play tracks from Node.js and the CLI
Maintainers
Readme
@shardev/play-store-publisher
Publish Android App Bundles (.aab) to Google Play tracks from Node.js or the command
line. Built on top of the official
Google Play Developer API
(androidpublisher_v3).
- Upload an
.aaband publish it to a track (internal,alpha,beta,production) - Localized release notes (changelog) per language
- Optional bundle validation before committing (on by default)
- Keep existing version codes on a track, replace the whole track, or set the exact codes
- Standalone
validatecommand that uploads a bundle without publishing - TypeScript-first API plus a zero-config CLI
- Ships both CommonJS and ESM builds
Requirements
- Node.js >= 18
- An Android app whose Play Console account you have access to (service account)
Google Play Console + Cloud setup
- Create a service account in the Google Cloud Console: APIs & Services → Credentials → Create Credentials → Service account. Download its JSON key file.
- Enable the Android Publisher API for your Google Cloud project (APIs & Services → Library → "Android Publisher API" → Enable).
- Add the service account's email to Play Console → Users and permissions with the Release to production, testing tracks and testers permission (or a more restrictive one such as Release management → Releases for testing tracks only).
- Store the JSON key locally or in an environment variable.
Installation
npm install --save-dev @shardev/play-store-publisherCLI
# quickest: point at your service account JSON
play-store-publisher publish \
--aab ./android/app/build/outputs/bundle/release/app-release.aab \
--package com.example.app \
--track production \
--service-account ./play-store-key.json \
--name "1.2.0" \
--notes "Fixed the checkout crash" \
--notes "Nuevo modo oscuro" \
--lang en-US
# notes are repeatable; --lang applies to all of them
play-store-publisher publish --aab app.aab -p com.example.app \
--lang es-419 \
--notes "Nuevo modo oscuro" \
--notes "Corrección en el pago"
# validate a bundle without publishing anything
play-store-publisher validate \
--aab app-release.aab \
--package com.example.app \
--service-account ./play-store-key.json
# machine-friendly output
play-store-publisher publish ... --jsonCredentials resolution order:
--service-account <file.json>(a path,env:VAR_NAME, or inline JSON)GOOGLE_APPLICATION_CREDENTIALSenvironment variable
publish options
| Option | Default | Description |
| ----------------------- | ------------- | ---------------------------------------------------------------------- |
| -a, --aab <path> | – (required) | Path to the .aab file. |
| -p, --package <name> | – (required) | Application package name, e.g. com.example.app. |
| --track <track> | internal | Target track: internal, alpha, beta, production. |
| --status <status> | completed | Release status: draft, completed, inProgress, halted. |
| --name <name> | – | Release name shown in the Play Console. |
| --notes <text> | – | Release note text. Repeatable; pairs with --lang. |
| --lang <code> | en-US | BCP-47 language code applied to subsequent --notes. |
| --service-account <p> | env | Credentials file path, env:VAR_NAME or inline JSON. |
| --version-codes <l> | – | Exact version codes for the track (replaces it), comma separated. |
| --replace | false | Replace the track so only the new version code remains. |
| --no-validate | validate on | Skip Google Play bundle validation before committing. |
| --json | false | Print the result as JSON. |
Library API
import { publish } from '@shardev/play-store-publisher';
const result = await publish({
aabPath: './dist/app-release.aab',
packageName: 'com.example.app',
track: 'production',
status: 'completed',
name: '1.2.0',
releaseNotes: [
{ language: 'en-US', text: 'Fixed the checkout crash' },
{ language: 'es-419', text: 'Nuevo modo oscuro' },
],
serviceAccount: { path: './play-store-key.json' },
});
console.log(result.versionCode, result.track, result.versionCodes);publish(options) → PublishResult
| Option | Type | Default | Description |
| --------------- | ----------------------- | ----------- | ------------------------------------------------------------------------- |
| aabPath | string | – (required)| Path to the .aab file. |
| packageName | string | – (required)| Application package name. |
| track | Track | internal | internal | alpha | beta | production. |
| status | ReleaseStatus | completed | draft | completed | inProgress | halted. |
| name | string | – | Release name shown in the Play Console. |
| releaseNotes | ReleaseNote[] | – | { language, text }[]. |
| versionCodes | number[] | – | Exact version codes for the track (replaces it). |
| replace | boolean | false | Replace the track so only the new version code remains. |
| validate | boolean | true | Run Google Play bundle validation before committing. |
| serviceAccount| ServiceAccountOptions | env | { path } | { json } | { env }; falls back to GOOGLE_APPLICATION_CREDENTIALS. |
Returned PublishResult:
interface PublishResult {
editId: string; // committed Google Play edit id
packageName: string;
track: Track;
versionCode: number; // version code of the uploaded bundle
versionCodes: number[]; // all version codes now on the track
releaseName?: string;
status: ReleaseStatus;
committed: true;
}validateAab(options) → ValidateResult
Uploads a bundle, runs Google Play's validation and deletes the throwaway edit. Never publishes anything.
import { validateAab } from '@shardev/play-store-publisher';
const { versionCode } = await validateAab({
aabPath: './dist/app-release.aab',
packageName: 'com.example.app',
});Track handling
By default the new version code is added to the existing release history of the track:
previous releases and their notes are preserved, and a new release is appended. Use
replace: true to keep only the new version code, or pass versionCodes to set the
exact list.
Error handling
All failures throw PlayStorePublisherError with a stable code, plus the original
Google Play HTTP status and reason when available.
import { publish, isPlayStorePublisherError } from '@shardev/play-store-publisher';
try {
await publish({ ... });
} catch (error) {
if (isPlayStorePublisherError(error)) {
console.error(error.code, error.apiStatus, error.apiReason, error.message);
}
}| Code | Description |
| --------------------- | ------------------------------------------------------------- |
| INVALID_ARGUMENT | Missing/invalid options (track, status, notes, ...). |
| FILE_NOT_FOUND | The .aab file does not exist. |
| INVALID_CONFIG | Bad service account JSON or no credentials found. |
| EDIT_CREATE_FAILED | Could not create the Google Play edit session. |
| UPLOAD_FAILED | The bundle upload failed. |
| VALIDATION_FAILED | Google Play rejected the bundle during validation. |
| TRACK_UPDATE_FAILED | Could not read or update the target track. |
| COMMIT_FAILED | Committing the edit (the actual publish step) failed. |
| UNEXPECTED | Anything else. |
On validation or mid-publish failures the throwaway edit is deleted so no partial state is left behind.
How it works
publish() runs the standard Play Developer API edit flow:
edits.insert→ creates an edit session.edits.bundles.upload→ uploads the.aab(withackBundleInstallationWarning).edits.validate(unless--no-validate) → asks Google to validate the bundle.edits.tracks.get+edits.tracks.update→ assigns the release to the track.edits.commit→ publishes it.
Authentication uses a service account JWT with the
https://www.googleapis.com/auth/androidpublisher scope.
Contributing
npm install
npm run verify # typechecks and builds both CJS and ESM output
npm test # builds and runs the test suiteLicense
MIT
