apple resubmit: REJECTED review submission items are marked resolved before the submit (PATCH reviewSubmissionItems resolved: true; the version item by default, IAP / subscription items with --resolve-iaps), plan table 'mark resolved' rows; on 409 'not ready yet' the items are re-read and a REJECTED item out of scope stops the run at once naming it and how to resolve it, backoff only when none is left; an empty READY_FOR_REVIEW draft is never canceled (409 'not in cancellable state'): left as is, no cancel command, apple status labels it harmless and treats the cancel 409 as such; fake ASC blocks submit on REJECTED items and refuses to cancel empty drafts. Pitfall 30
This commit is contained in:
parent
0da5ee9d01
commit
1724296e59
17 changed files with 291 additions and 78 deletions
|
|
@ -2,6 +2,8 @@
|
|||
// storebound apple status [--json]
|
||||
// And the one write it offers: cancel a review submission (to re-add first IAPs to it from the web UI):
|
||||
// storebound apple status --cancel-submission <id> [--yes]
|
||||
// An empty draft (READY_FOR_REVIEW, 0 items) cannot be canceled (409 "Resource is not in cancellable state"); it is
|
||||
// harmless, shown as such and left as is.
|
||||
// A submission back from App Review (UNRESOLVED_ISSUES) is shown with its items; resubmit it with
|
||||
// storebound apple resubmit --build <n>
|
||||
// Wait for App Review (polls with backoff: 1 min, x1.5, max 15 min):
|
||||
|
|
@ -11,8 +13,9 @@ import type { Ctx } from '../lib/ctx';
|
|||
import { confirm, need } from '../lib/ctx';
|
||||
import { log, printJson } from '../lib/log';
|
||||
import { classifyReviewState, nextPollDelay, parseDuration, WAIT_EXIT } from '../lib/release';
|
||||
import { EMPTY_DRAFT } from '../lib/review-submit';
|
||||
import { type Asc, ascClient, type J } from './asc';
|
||||
import { readItems, type SubItem } from './submission';
|
||||
import { notCancellable, readItems, type SubItem } from './submission';
|
||||
|
||||
async function waitReview(ctx: Ctx, c: Asc, appId: string): Promise<never> {
|
||||
const want = ctx.args.get('version');
|
||||
|
|
@ -47,7 +50,14 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const cancel = ctx.args.get('cancel-submission');
|
||||
if (cancel) {
|
||||
if (!(await confirm(ctx, `Cancel review submission ${cancel}? (the version goes back to "Prepare for Submission")`))) return;
|
||||
await c.asc('PATCH', `/v1/reviewSubmissions/${cancel}`, { data: { type: 'reviewSubmissions', id: cancel, attributes: { canceled: true } } });
|
||||
try {
|
||||
await c.asc('PATCH', `/v1/reviewSubmissions/${cancel}`, { data: { type: 'reviewSubmissions', id: cancel, attributes: { canceled: true } } });
|
||||
} catch (e) {
|
||||
if (!notCancellable(e)) throw e;
|
||||
log('apple', `review submission ${cancel} is not cancellable (App Store Connect answered "Resource is not in cancellable state"); left as is. `
|
||||
+ `An ${EMPTY_DRAFT} does no harm: a later run reuses it`);
|
||||
return;
|
||||
}
|
||||
log('apple', `review submission ${cancel} canceled; it takes a minute to settle`);
|
||||
return;
|
||||
}
|
||||
|
|
@ -60,6 +70,9 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
// a submission back from App Review: its items and their states, and how to resubmit it
|
||||
const unresolved: { id: string; items: SubItem[] }[] = [];
|
||||
for (const s of subs.filter((x: J) => x.attributes?.state === 'UNRESOLVED_ISSUES')) unresolved.push({ id: s.id, items: await readItems(c, s.id) });
|
||||
// an empty draft: App Store Connect keeps it and refuses to cancel it; harmless
|
||||
const empty = new Set<string>();
|
||||
for (const s of subs.filter((x: J) => x.attributes?.state === 'READY_FOR_REVIEW')) if (!(await readItems(c, s.id)).length) empty.add(s.id);
|
||||
const itemLabel = (kind: string, id: string): string => {
|
||||
const v = versions.find((x: J) => x.id === id);
|
||||
return v ? `app version ${v.attributes.versionString}` : `${kind} ${id}`;
|
||||
|
|
@ -83,7 +96,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const v = pre.get(b.relationships?.preReleaseVersion?.data?.id ?? '');
|
||||
if (v) buildVersions[b.id] = v;
|
||||
}
|
||||
printJson({ app, versions, builds: builds.data, attachedBuilds, buildVersions, iaps, subs, phased, unresolved });
|
||||
printJson({ app, versions, builds: builds.data, attachedBuilds, buildVersions, iaps, subs, phased, unresolved, emptyDrafts: [...empty] });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -92,10 +105,10 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const pre = new Map((builds.included ?? []).map((p: { id: string; attributes: { version: string } }) => [p.id, p.attributes.version]));
|
||||
for (const b of builds.data) console.log(` build ${String(pre.get(b.relationships?.preReleaseVersion?.data?.id) ?? '?')} (${b.attributes.version}) ${b.attributes.processingState} ${b.attributes.uploadedDate?.slice(0, 10) ?? ''}`);
|
||||
for (const p of iaps) console.log(` iap ${p.attributes.productId.padEnd(40)} ${p.attributes.inAppPurchaseType} ${p.attributes.state}`);
|
||||
for (const s of subs) console.log(` review submission ${s.id} ${s.attributes.state} ${s.attributes.submittedDate?.slice(0, 10) ?? ''}`);
|
||||
for (const s of subs) console.log(` review submission ${s.id} ${s.attributes.state}${empty.has(s.id) ? `: ${EMPTY_DRAFT}` : ''} ${s.attributes.submittedDate?.slice(0, 10) ?? ''}`.trimEnd());
|
||||
for (const u of unresolved) {
|
||||
console.log(` review submission ${u.id} is back from App Review with UNRESOLVED_ISSUES; its items:`);
|
||||
for (const it of u.items) console.log(` ${itemLabel(it.kind, it.versionId).padEnd(36)} ${it.state}`);
|
||||
for (const it of u.items) console.log(` ${itemLabel(it.kind, it.versionId).padEnd(36)} ${it.state}${it.state === 'REJECTED' ? ' (not resolved: Apple takes no resubmit until it is; apple resubmit marks it resolved)' : ''}`);
|
||||
const newest = builds.data[0]?.attributes?.version;
|
||||
console.log(` resubmit with: storebound apple resubmit --build <n>${newest ? ` (newest uploaded build: ${newest})` : ''}`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue