upload --pre-check/--evidence, shots copy translate, game-center --locale-map, play games API-disabled hint
This commit is contained in:
parent
29a9683ab7
commit
8ffa27d4f8
24 changed files with 598 additions and 27 deletions
|
|
@ -14,16 +14,30 @@ import { confirm, need } from '../lib/ctx';
|
|||
import { dryPlan } from '../lib/dry';
|
||||
import { hasAndroidProject, type ItemPlan, pgsFindings, type PlayExisting, planPlayGames, planRows, readPgsProject, usesPlayGames } from '../lib/game-services';
|
||||
import { PLAY_SCOPE, tokenSource } from '../lib/google-auth';
|
||||
import { enableUrl, findingText, parseGoogleError } from '../lib/google-errors';
|
||||
import { errorBody } from '../lib/http';
|
||||
import { done, log, warn } from '../lib/log';
|
||||
import * as rt from '../lib/rt';
|
||||
import { AmbiguousWriteError, createOnce, retrySafe } from '../lib/upsert';
|
||||
|
||||
const TAG = 'play-games';
|
||||
export const GAMES_SERVICE = 'gamesconfiguration.googleapis.com';
|
||||
export const GAMES_API_TITLE = 'Google Play Game Services Publishing API';
|
||||
export const GAMES_CONFIG_API = 'https://gamesconfiguration.googleapis.com/games/v1configuration';
|
||||
|
||||
/** The Games Configuration API is off in the service account's Cloud project (403 SERVICE_DISABLED / accessNotConfigured): the exact API name, where to turn it on and the console plan. Null for any other error. */
|
||||
export function gamesApiDisabledText(errText: string, fallbackProject: string | null): string | null {
|
||||
const f = parseGoogleError(errText);
|
||||
if (f?.kind !== 'api-disabled') return null;
|
||||
const projectId = f.projectId ?? fallbackProject;
|
||||
const line = findingText({ ...f, service: GAMES_SERVICE, serviceTitle: GAMES_API_TITLE, projectId, activationUrl: enableUrl(GAMES_SERVICE, projectId) }, { withUrl: true });
|
||||
return `${line}
|
||||
API: ${GAMES_API_TITLE} (${GAMES_SERVICE}). Enable it, wait a minute, then run storebound play games again. Plan: storebound console plan play-games-services`;
|
||||
}
|
||||
|
||||
export function gamesConfigClient(root: string) {
|
||||
const token = tokenSource(playServiceAccount(root), PLAY_SCOPE);
|
||||
const account = playServiceAccount(root);
|
||||
const token = tokenSource(account, PLAY_SCOPE);
|
||||
async function call<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {
|
||||
const url = GAMES_CONFIG_API + path;
|
||||
const safe = method !== 'POST' && retrySafe(method, url);
|
||||
|
|
@ -37,7 +51,11 @@ export function gamesConfigClient(root: string) {
|
|||
throw new Error(`${method} ${path} -> ${r.status} ${errorBody(t, 300)}`);
|
||||
}
|
||||
const t = await r.text();
|
||||
if (!r.ok) throw new Error(`${method} ${path} -> ${r.status} ${errorBody(t, 800)}`);
|
||||
if (!r.ok) {
|
||||
const msg = `${method} ${path} -> ${r.status} ${errorBody(t, 800)}`;
|
||||
const off = r.status === 403 ? gamesApiDisabledText(msg, account.project_id ?? null) : null;
|
||||
throw new Error(off ? `${msg.split(' -> ')[0]} -> 403: ${off}` : msg);
|
||||
}
|
||||
return (t ? JSON.parse(t) : {}) as T;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// 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] [--dry] [--yes] [--json]
|
||||
// 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]
|
||||
// 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
|
||||
|
|
@ -18,6 +18,7 @@ import { nextVersionCode } from '../lib/release';
|
|||
import { playClient } from './client';
|
||||
import { readPlayCopy, refusePlayNotes } from './listing';
|
||||
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';
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const sdkWhy = playTargetSdkProblem(info.targetSdk, { where: targetSdkFile(root) });
|
||||
if (sdkWhy) throw new Error(`${relative(root, aab)}: not uploaded, ${sdkWhy}`);
|
||||
assertAabSigned(aab, relative(root, aab));
|
||||
const pre = await runPreCheck({ args: a, config, root, resolve, file: aab, shown: relative(root, aab), tag: TAG });
|
||||
const size = statSync(aab).size;
|
||||
const dry = a.has('dry');
|
||||
const json = a.has('json');
|
||||
|
|
@ -82,7 +84,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
}
|
||||
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,
|
||||
track, status, notes: notes.map(n => n.language), replaces: current.map(r => `${r.status} ${(r.versionCodes ?? []).join(',')}`) };
|
||||
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`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue