Updates are agent steps: --yes ships an update of a live app (Play production, App Store submit / release) without an owner gate; the first release stays the owner's (exit 3 OWNER_STEP, --first-release); apple release is a ship step
This commit is contained in:
parent
c247b7e54a
commit
ae9d26d3f6
28 changed files with 562 additions and 44 deletions
|
|
@ -1,12 +1,14 @@
|
|||
// Upload an Android App Bundle to a Google Play testing track.
|
||||
// storebound play upload [<app.aab>] [--track internal|alpha|beta|production] [--status completed|draft] [--version-code N] [--name text] [--not-sent-for-review] [--pre-check "<cmd with {file}>"] [--evidence <path.json>] [--dry] [--yes] [--json]
|
||||
// Upload an Android App Bundle to a Google Play track (testing tracks, or production for an UPDATE of a published app).
|
||||
// storebound play upload [<app.aab>] [--track internal|alpha|beta|production] [--status completed|draft] [--version-code N] [--name text] [--not-sent-for-review] [--first-release] [--pre-check "<cmd with {file}>"] [--evidence <path.json>] [--dry] [--yes] [--json]
|
||||
// Default AAB: the newest in build/android (storebound play build), or with --version-code N the one with that
|
||||
// versionCode. Package and versionCode are read from the bundle; the upload is refused when Play already has that
|
||||
// versionCode (exit 1), and under the ship-step contract (--json / --result-file, docs/ship-step.md) it is exit 7
|
||||
// "nothing to do" with the track and state that are there: never a second upload. Release notes: the "What's new" of
|
||||
// every listing language. --dry is read-only (one throwaway edit: version codes and the track are read, the edit is
|
||||
// deleted). A real run asks first (--yes in scripts). --track production sends the release to Google's review (a
|
||||
// pipeline's update step); --status draft keeps it for the Console.
|
||||
// pipeline's update step): no owner gate when the production track already holds a published release (an update of
|
||||
// a live app, cli/lib/release-kind.ts); a first-ever production release with --yes is exit 3 OWNER_STEP unless the owner
|
||||
// approved it (--first-release, or bypass mode). --status draft keeps the release for the Console.
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { join, relative } from 'node:path';
|
||||
import type { Ctx } from '../lib/ctx';
|
||||
|
|
@ -21,6 +23,7 @@ import { usedVersionCodes } from './next-version-code';
|
|||
import { runPreCheck } from '../lib/pre-check';
|
||||
import { shipNoop, shipPhase, shipReport, shipRun, shipWarn } from '../lib/ship-step';
|
||||
import { consoleUrl, playTrackPage } from '../lib/console-links';
|
||||
import { enforceFirstRelease, playReleaseKind } from '../lib/release-kind';
|
||||
|
||||
const TAG = 'upload';
|
||||
export const UPLOAD_TRACKS = ['internal', 'alpha', 'beta', 'production'] as const;
|
||||
|
|
@ -83,11 +86,15 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
if (shipRun()) { done(TAG, `versionCode ${info.versionCode} is on Google Play already: nothing uploaded`); return; }
|
||||
}
|
||||
if (used.includes(info.versionCode)) throw new Error(`versionCode ${info.versionCode} is already on Google Play: rebuild with --version-code ${nextVersionCode(used)} (storebound play build)`);
|
||||
const summary = { aab: relative(root, aab), packageName: info.packageName, versionCode: info.versionCode, versionName: info.versionName ?? null, sizeMB: Math.round(size / 104857.6) / 10,
|
||||
// production: update of a published app (no owner gate) or the first release (the owner's step)
|
||||
const kind = track === 'production' && status !== 'draft' ? playReleaseKind(current) : null;
|
||||
if (kind && !dry && !enforceFirstRelease(ctx, kind, { store: 'play', what: 'the first production release', consoleUrl: consoleUrl('play.publishing') })) return;
|
||||
const summary = { ...(kind ? { releaseKind: kind.kind } : {}), aab: relative(root, aab), packageName: info.packageName, versionCode: info.versionCode, versionName: info.versionName ?? null, sizeMB: Math.round(size / 104857.6) / 10,
|
||||
track, status, notes: notes.map(n => n.language), replaces: current.map(r => `${r.status} ${(r.versionCodes ?? []).join(',')}`), ...(pre ? { preCheck: pre } : {}) };
|
||||
if (dry) {
|
||||
if (json) { printJson(summary); return; }
|
||||
log(TAG, `${summary.aab}: ${info.packageName} ${info.versionName ?? ''} (${info.versionCode}), ${summary.sizeMB} MB, versionCode free`);
|
||||
if (kind) log(TAG, `${kind.kind === 'update' ? 'update of a published app' : 'FIRST production release (the owner approves it once: --first-release)'}: ${kind.why}`);
|
||||
log(TAG, `would upload it and set track ${track} to ${status} ${info.versionCode}${notes.length ? `, notes in ${notes.length} language(s)` : ', no release notes'}${current.length ? ` (now on ${track}: ${summary.replaces.join('; ')})` : ''}`);
|
||||
done(TAG, 'upload planned (dry run, read-only)');
|
||||
return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue