play managed publishing: console plan/verify play-publish-changes, ready-to-publish items in play status, desktop hint
This commit is contained in:
parent
29a9683ab7
commit
1bb43fd4a0
20 changed files with 184 additions and 9 deletions
|
|
@ -29,7 +29,7 @@ export type FormId = typeof FORM_IDS[number];
|
|||
* API cannot set), the App content overview and its declarations without a fill form (cli/lib/console-play-plans.ts). */
|
||||
export const PLAN_ONLY_FORM_IDS = ['play-store-settings', 'play-app-content', 'play-privacy-policy', 'play-ads', 'play-sign-in-details',
|
||||
'play-advertising-id', 'play-government-apps', 'play-financial-features', 'play-health-apps', 'play-production-countries', 'play-production-release',
|
||||
'play-games-services'] as const;
|
||||
'play-games-services', 'play-publish-changes'] as const;
|
||||
export type PlanOnlyFormId = typeof PLAN_ONLY_FORM_IDS[number];
|
||||
/** Every form `console plan` / `console verify` know. */
|
||||
export const PLAN_FORM_IDS = [...FORM_IDS, ...PLAN_ONLY_FORM_IDS] as const;
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ export interface PlanIds {
|
|||
demoAccount?: boolean;
|
||||
/** What the project scan found (ad / attribution SDKs, the AD_ID permission, support and AR code); undefined: not scanned. */
|
||||
signals?: ProjectSignals;
|
||||
/** play-publish-changes: the production releases ready to publish per the API (null/undefined: not read). */
|
||||
readyChanges?: { track: string; version: string | null; build: string | null; rollout: string }[] | null;
|
||||
}
|
||||
|
||||
const ASC = 'https://appstoreconnect.apple.com';
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { NEVER_PRESS, PLAY_NEVER_PRESS, playAppList } from './console-forms';
|
|||
import type { ConsolePlan, PlanAnswer, PlanIds, PlanStatus, PlanStep } from './console-plan';
|
||||
import { AD_ID_PERMISSION, type ProjectSignals } from './project-signals';
|
||||
import { playGamesPart } from './console-games-plan';
|
||||
import { PUBLISH_BUTTON, PUBLISH_CONFIRM } from './play-publish';
|
||||
|
||||
const PLAY = 'https://play.google.com/console';
|
||||
|
||||
|
|
@ -503,10 +504,41 @@ function productionRelease(config: ShipConfig, p: PlanIds): Part {
|
|||
};
|
||||
}
|
||||
|
||||
function publishChanges(config: ShipConfig, p: PlanIds): Part {
|
||||
const ready = p.readyChanges ?? null;
|
||||
const readyText = ready === null ? 'not read (add --lookup, or run `storebound play status`)' : ready.length ? ready.map(r => `${r.track} ${r.version ?? '?'} (${r.build ?? '?'}) ${r.rollout}`).join('; ') : 'nothing ready to publish per the API';
|
||||
return {
|
||||
title: 'Play Console: publish the approved changes (managed publishing)',
|
||||
start: start(config, p, 'publishing', ['Publishing overview (left menu)']),
|
||||
steps: steps([
|
||||
{ text: 'Precondition: `storebound play status` shows "production <version>: In review / ready to publish" (managed publishing on, Google approved).' },
|
||||
{ text: 'Open Publishing overview. The section "Changes ready to publish" lists every approved change (e.g. the production release and Data safety); read the list as text and compare it with the ready changes below.' },
|
||||
{ text: `Stop. The owner presses "${PUBLISH_BUTTON}" (N = the number in the list) and then confirms in its dialog (button "${PUBLISH_CONFIRM}"; a real mouse click, a JS .click() may do nothing). It makes every listed change live at once.`, answers: ['ready'] },
|
||||
{ text: 'Verify: `storebound console verify play-publish-changes` (API: no production release is left ready to publish) or pipe the page text to `--from-text -`.' },
|
||||
]),
|
||||
answers: [ans('ready', 'Changes ready to publish (API)', readyText, 'certain', 'play status (public store page + tracks)', { note: 'The API sees production releases only; Data safety, listing and other non-track changes show in the console list only.', where: 'Publishing overview > Changes ready to publish' })],
|
||||
final: PUBLISH_BUTTON,
|
||||
neverPress: ['Publish N changes', 'Send changes for review', 'Submit N changes for review', 'Restart review', 'Discard changes', 'Stop managed publishing', 'Turn off managed publishing', 'Halt rollout', 'Unpublish app', 'Deactivate', ...NEVER_PRESS],
|
||||
allowed: [
|
||||
{ button: 'Publishing overview (left menu), expand Changes ready to publish', class: 'navigation', where: 'read-only: lists the approved changes' },
|
||||
],
|
||||
uiFacts: [
|
||||
'Managed publishing (play.managedPublishing: true): after approval the changes wait in Publishing overview > "Changes ready to publish"; the button is "Publish N changes" (N counts every change, e.g. production full rollout + Data safety) and opens a confirm dialog.',
|
||||
'The Developer API has no publishing-state endpoint: storebound infers "ready to publish" from a completed production release the public store page does not show yet.',
|
||||
'Never "Discard changes" or "Stop managed publishing" (it would unpublish or drop the approved changes).',
|
||||
managedPublishingNote(config),
|
||||
PLAY_MATERIAL_FACT,
|
||||
],
|
||||
verify: { command: 'storebound console verify play-publish-changes', source: 'page-text', capture: PAGE_TEXT, expect: ['No production release is left ready to publish (API), or the page shows no "Publish N changes" button / "Changes ready to publish".'] },
|
||||
guidance: ['Open: Publishing overview > Changes ready to publish'],
|
||||
};
|
||||
}
|
||||
|
||||
export function playOnlyPart(form: Exclude<PlanOnlyFormId, 'play-app-content'>, config: ShipConfig, p: PlanIds): Part {
|
||||
switch (form) {
|
||||
case 'play-production-countries': return productionCountries(config, p);
|
||||
case 'play-production-release': return productionRelease(config, p);
|
||||
case 'play-publish-changes': return publishChanges(config, p);
|
||||
case 'play-store-settings': return storeSettings(config, p);
|
||||
case 'play-privacy-policy': return privacyPolicy(config, p);
|
||||
case 'play-ads': return adsDeclaration(config, p);
|
||||
|
|
|
|||
|
|
@ -426,11 +426,29 @@ export function verifyProductionRelease(text: string): VerifyResult {
|
|||
return result('play-production-release', 'page-text', rows);
|
||||
}
|
||||
|
||||
/** Publishing overview as text (or the API's ready list): nothing left to publish. */
|
||||
export function verifyPublishChanges(text: string): VerifyResult {
|
||||
const f = fold(text);
|
||||
const pending = /publish \d+ changes?|changes ready to publish/.test(f) && !/no changes ready to publish|0 changes ready/.test(f);
|
||||
const rows: VerifyRow[] = [{ item: 'Changes ready to publish', expected: 'none left', seen: pending ? 'changes ready to publish' : 'none listed', status: pending ? 'mismatch' : 'match',
|
||||
...(pending ? { note: 'the owner presses "Publish N changes" and confirms' } : {}) }];
|
||||
return result('play-publish-changes', 'page-text', rows);
|
||||
}
|
||||
|
||||
/** The same check from the API's ready list (`readyToPublish`). */
|
||||
export function verifyPublishChangesApi(ready: { track: string; version: string | null; build: string | null; rollout: string }[]): VerifyResult {
|
||||
const seen = ready.map(r => `${r.track} ${r.version ?? '?'} (${r.build ?? '?'}) ${r.rollout}`).join('; ');
|
||||
const rows: VerifyRow[] = [{ item: 'Production releases ready to publish', expected: 'none left', seen: ready.length ? seen : 'none', status: ready.length ? 'mismatch' : 'match',
|
||||
...(ready.length ? { note: 'the owner presses "Publish N changes" and confirms (storebound console plan play-publish-changes)' } : {}) }];
|
||||
return result('play-publish-changes', 'api', rows, ['The API sees production releases only; Data safety and other non-track changes are not readable.']);
|
||||
}
|
||||
|
||||
/** Page-text verification of any form that has one. */
|
||||
export function verifyText(form: PlanFormId, config: ShipConfig, text: string): VerifyResult {
|
||||
if (form === 'play-app-content') return verifyAppContent(text);
|
||||
if (form === 'play-production-countries') return verifyProductionCountries(config, text);
|
||||
if (form === 'play-production-release') return verifyProductionRelease(text);
|
||||
if (form === 'play-publish-changes') return verifyPublishChanges(text);
|
||||
if (form === 'play-games-services') return result(form, 'page-text', verifyPlayGames(config, text));
|
||||
if (form === 'play-store-settings' || form === 'play-privacy-policy' || form === 'play-ads' || form === 'play-sign-in-details' || form === 'play-advertising-id'
|
||||
|| form === 'play-government-apps' || form === 'play-financial-features' || form === 'play-health-apps') return verifyPlayDeclaration(form, config, text);
|
||||
|
|
|
|||
29
cli/lib/play-publish.ts
Normal file
29
cli/lib/play-publish.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Play managed publishing: what waits in Publishing overview as "Changes ready to publish" (button "Publish N changes").
|
||||
// The Developer API has no publishing-state endpoint; `play status` decides it the same way portfolio.playReleases does:
|
||||
// a completed production release that the public store page does not show yet (404, or an older version) while
|
||||
// play.managedPublishing is on is "In review / ready to publish". Pure. Non-track changes (Data safety, listing, content
|
||||
// rating) are listed by the console page only; the API cannot see them.
|
||||
import { playReleases } from './portfolio';
|
||||
import { consoleUrl } from './console-links';
|
||||
|
||||
export const PUBLISH_PLAN_HINT = 'storebound console plan play-publish-changes';
|
||||
/** The owner's final button: "Publish N changes" (N is the page's count) and its confirm dialog. */
|
||||
export const PUBLISH_BUTTON = 'Publish N changes';
|
||||
export const PUBLISH_CONFIRM = 'Publish';
|
||||
|
||||
export interface ReadyChange { track: string; version: string | null; build: string | null; rollout: string; liveVersion: string | null }
|
||||
|
||||
/** Ready-to-publish production releases from the `play status --json` data ({ tracks, publicPage, managedPublishing }). */
|
||||
export function readyToPublish(data: unknown): ReadyChange[] {
|
||||
return playReleases(data).filter(r => r.track === 'production' && r.managed === true && r.phase === 'review').map(r => ({
|
||||
track: r.track ?? 'production', version: r.version, build: r.build,
|
||||
rollout: r.fraction !== null && r.fraction < 1 ? `${Math.round(r.fraction * 1000) / 10}% staged` : 'full rollout', liveVersion: r.liveVersion ?? null,
|
||||
}));
|
||||
}
|
||||
|
||||
/** Status lines for `play status` / `status`: the items and the hint (empty when nothing is ready). */
|
||||
export function readyLines(items: ReadyChange[]): string[] {
|
||||
if (!items.length) return [];
|
||||
return [` ready to publish (managed publishing): ${items.map(i => `${i.track} ${i.version ?? '?'} (${i.build ?? '?'}) ${i.rollout}${i.liveVersion ? `, live: ${i.liveVersion}` : ''}`).join(' | ')}; non-track changes (Data safety, listing) show only in the console`,
|
||||
` next: ${PUBLISH_PLAN_HINT} (${consoleUrl('play.publishing')})`];
|
||||
}
|
||||
|
|
@ -239,7 +239,7 @@ function storeSteps(store: 'ios' | 'android', v: string, r: StoreRelease | null,
|
|||
break;
|
||||
case 'review':
|
||||
if (store === 'android' && r!.track === 'production') {
|
||||
steps.push({ kind: r!.managed ? 'owner' : 'wait', text: `${v}: ${playReleaseLabel(r!)}`, fix: r!.managed ? `${consoleLink('play.publishing')} (publish once approved)` : 'storebound play status', ...(r!.managed ? { url: consoleUrl('play.publishing') } : {}) });
|
||||
steps.push({ kind: r!.managed ? 'owner' : 'wait', text: `${v}: ${playReleaseLabel(r!)}`, fix: r!.managed ? `${consoleLink('play.publishing')} (publish once approved; \`storebound console plan play-publish-changes\`)` : 'storebound play status', ...(r!.managed ? { url: consoleUrl('play.publishing') } : {}) });
|
||||
break;
|
||||
}
|
||||
steps.push({ kind: 'wait', text: `${v} is in review (${r!.state}); nothing to do until the store answers`, fix: store === 'ios' ? 'storebound apple status --wait-review' : 'storebound play status' });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue