console plans: guardrails from a real run (2026-10-21): health none label and Sign in details Yes/No + Add details (old labels as altLabels, verify accepts both), finance boxes on step 1, App Privacy Additional Setup Required OK, verify from DOM input states (verify.capture, --from), Save + confirm before every Next on Play pages, IARC Digital purchases boxes and an exact-label in-page helper that refuses patterns and blanket answers; review contact found in parent folders, the profile and more key shapes with the files checked listed; one-line errors for missing required values (Args.required); pitfalls 86-87, tests

This commit is contained in:
CRTkafa 2026-10-21 05:03:02 +07:00
parent a6dab8f3aa
commit 335a4124ab
23 changed files with 486 additions and 74 deletions

View file

@ -165,8 +165,7 @@ function options(ctx: Ctx): void {
function exportIpa(ctx: Ctx): void {
const a = ctx.args;
const archive = a.get('archive');
if (!archive || archive === 'true') throw new Error('usage: storebound apple adhoc export --archive <App.xcarchive> [--out build/adhoc] [--dry]');
const archive = a.required('archive', 'App.xcarchive', 'Xcode: Product > Archive, or storebound apple build');
if (!existsSync(archive)) throw new Error(`${archive}: no such archive (Xcode: Product > Archive, or xcodebuild archive)`);
const plistRel = a.str('plist', 'build/ExportOptions.plist');
const plist = ctx.loaded.resolve(plistRel);

View file

@ -195,8 +195,7 @@ export async function run(ctx: Ctx): Promise<void> {
const { config, root } = ctx.loaded;
const a = ctx.args;
if (a._[0] === 'pull') return pull(ctx);
const version = a.get('version');
if (!version) throw new Error('usage: apple listing --version 1.0.0 [--build N] [--include-drafts] [--dry] [--json]');
const version = a.required('version', 'x.y.z', 'storebound apple status shows the editable version');
const dry = a.has('dry');
const c = await ascClient(root, need(config.apple?.bundleId, 'apple.bundleId'), config.apple?.appId);
const appId = await c.app();

View file

@ -39,8 +39,8 @@ export async function submitVersion(ctx: Ctx, mode: 'submit' | 'resubmit'): Prom
const TAG = mode;
const { config, root } = ctx.loaded;
const a = ctx.args;
const number = a.get('build');
if (!number || !/^\d+(\.\d+)*$/.test(number)) throw new Error(`usage: apple ${mode} --build <n> [--version x.y.z] [--dry] [--yes]: the build number you just uploaded (never "the latest")`);
const number = a.required('build', 'n', 'the build number you just uploaded, never "the latest"; storebound apple testflight status lists them');
if (!/^\d+(\.\d+)*$/.test(number)) throw new Error(`--build ${number}: a build number like 42 or 1.0.3 (the one you just uploaded, never "the latest")`);
const c = await ascClient(root, need(config.apple?.bundleId, 'apple.bundleId'), config.apple?.appId);
const appId = await c.app();
const ids = [...(config.products ?? []).filter(p => sellsOn(config, p, 'apple')).map(p => p.id), ...allSubscriptions(config.subscriptions).filter(x => sellsOn(config, x.sub, 'apple')).map(x => x.sub.id)];

View file

@ -8,7 +8,7 @@
import { refuseTemplate } from '../lib/template-answers';
import type { Ctx } from '../lib/ctx';
import { need } from '../lib/ctx';
import { reviewContact, reviewDemoAccount } from '../lib/credentials';
import { findReviewContact, noContactMessage, reviewDemoAccount } from '../lib/credentials';
import { reviewDetailPlan } from '../lib/review-details';
import { done, log } from '../lib/log';
import { createOnce } from '../lib/upsert';
@ -29,11 +29,15 @@ export async function run(ctx: Ctx): Promise<void> {
if (a?.contactEmail) { contact = { contactFirstName: a.contactFirstName, contactLastName: a.contactLastName, contactPhone: a.contactPhone, contactEmail: a.contactEmail }; break; }
}
if (!contact) throw new Error(`app ${from} has no review contact on its recent versions`);
} else {
const rc = reviewContact(root);
}
let checked: string[] = [];
if (!from) {
const found = findReviewContact(root);
checked = found.checked;
const rc = found.contact;
if (rc) contact = { contactFirstName: rc.firstName, contactLastName: rc.lastName, contactPhone: rc.phone, contactEmail: rc.email };
}
if (!contact) throw new Error('no review contact: add "reviewContact" to .storebound/credentials.json or pass --contact-from <app id>');
if (!contact) throw new Error(noContactMessage(root, checked));
const { attributes, summary } = reviewDetailPlan(config, contact, reviewDemoAccount(root));
if (ctx.args.has('dry')) { log('apple', `would set review details (contact ${from ? 'copied from ' + from : 'from credentials'}, ${summary})`); return; }