apple listing: asking for the version that is in review says it is locked (it said "already exists; a new version needs a higher number"); play images: a listing language missing from languages[] is skipped with the fix instead of stopping the run (play listing pushes its text). Both found by the live dry-run sweep; tests

This commit is contained in:
CRTkafa 2026-10-14 08:43:59 +08:00
parent 9d6f741092
commit b29f1c407c
4 changed files with 28 additions and 1 deletions

View file

@ -75,6 +75,10 @@ export function planVersion(rows: { id: string; attributes: Record<string, unkno
const base = { existing, first, changes: [] as FieldDiff[], attributes: {} as Record<string, string> };
const refuse = (reason: string): VersionPlan => ({ ...base, action: 'refuse', reason });
const taken = others.find(v => v.versionString === want);
// the version asked for is in review / pending release: it exists and is locked; "needs a higher number" would send
// the owner off to make a second version while the first is still in review
if (taken && IN_PROGRESS.has(taken.state))
return refuse(`version ${want} is ${taken.state}: App Store Connect locks its texts and build while it is in review or pending release (remove it from review in App Store Connect to change it, or wait for the release and then make the next version)`);
if (taken) return refuse(`version ${want} already exists (${taken.state}); a new version needs a higher number`);
const higher = others.filter(v => compareVersions(v.versionString, want) >= 0).sort((a, b) => compareVersions(b.versionString, a.versionString))[0];
if (higher && (!editable || editable.versionString !== want))

View file

@ -141,7 +141,9 @@ export async function run(ctx: Ctx): Promise<void> {
const errors: string[] = [];
for (const code of codes) {
const l = langOf.get(code);
if (!l) throw new Error(`${code}: no entry in languages[] has this Play code`);
// the listing file has a section for a language the config does not list (play listing still pushes its text):
// there is no renders folder for it, so its images are skipped with the fix, not the whole run stopped
if (!l) { skipped.push(`skipped ${code}: in the listing file but no entry in languages[] has play: '${code}' (add it to upload its images)`); continue; }
const li = langImages(code, join(src, l.id), icon, tablets);
errors.push(...li.errors);
sets.set(code, li);

View file

@ -30,6 +30,11 @@ describe('version decision', () => {
expect(planVersion([v('v-0', '1.0.0', 'READY_FOR_DISTRIBUTION')], '1.1.0', attrs)).toMatchObject({ action: 'create', first: false });
expect(planVersion([v('v-0', '1.0.0', 'READY_FOR_DISTRIBUTION')], '1.0.0', attrs).reason).toContain('already exists (READY_FOR_DISTRIBUTION)');
expect(planVersion([v('v-0', '1.2.0', 'READY_FOR_DISTRIBUTION')], '1.1.0', attrs).reason).toContain('not higher than 1.2.0');
// the version asked for is the one in review (live sweep: apple listing --version 1.0.0 on an app in review said
// "already exists; a new version needs a higher number"): say it is locked, not to make another one
const inReview = planVersion([v('v-0', '1.0.0', 'WAITING_FOR_REVIEW')], '1.0.0', attrs);
expect(inReview).toMatchObject({ action: 'refuse', reason: expect.stringContaining('version 1.0.0 is WAITING_FOR_REVIEW: App Store Connect locks its texts and build') });
expect(String(inReview.reason)).not.toContain('higher number');
expect(planVersion([v('v-0', '1.0.0', 'READY_FOR_DISTRIBUTION'), v('v-1', '1.1.0', 'WAITING_FOR_REVIEW')], '1.2.0', attrs).reason).toContain('1.1.0 is WAITING_FOR_REVIEW');
expect(planVersion([v('v-0', '1.0.0', 'READY_FOR_DISTRIBUTION'), v('v-1', '1.1', 'REJECTED')], '1.1.0', attrs)).toMatchObject({ action: 'refuse', reason: expect.stringContaining('only while PREPARE_FOR_SUBMISSION') });
expect(planVersion([], '1.0.0', attrs)).toMatchObject({ action: 'create', first: true });

View file

@ -36,3 +36,19 @@ describe('bugs the matrix found', () => {
for (const f of ['cli/reviews.ts', 'cli/apple/previews.ts']) expect(readFileSync(join(ROOT, f), 'utf8'), f).not.toMatch(/\.split\('\/'\)\.pop\(\)/);
});
});
// live sweep (2026-10-14): a listing file with a tr-TR section the config's languages[] does not have stopped
// `play images --dry` with "tr-TR: no entry in languages[] has this Play code" while `play listing` pushes that text
describe('live sweep', () => {
test('play images skips a listing language that is not in languages[] (with the fix) instead of failing', async () => {
const { run: playImages } = await import('../cli/play/images');
const listing = (lang: string) => `## ${lang}\n<!-- play: ${lang} -->\n\n### Play short description\n\`\`\`\nNotes.\n\`\`\`\n\n### Play full description\n\`\`\`\nNotes for everyone.\n\`\`\`\n`;
const cfg = { ...BASE, languages: [{ id: 'en', apple: 'en-US', play: 'en-US' }], play: { ...BASE.play!, listing: 'store/listing.md' } };
const p = project(cfg, { 'store/listing.md': `# Play\n\n${listing('en-US')}\n${listing('tr-TR')}` });
let msg = '';
const r = await capture(() => playImages(p.ctx(['--dry']))).catch(e => { msg = (e as Error).message; return null; });
expect(msg).not.toContain('no entry in languages[] has this Play code');
if (r) expect(r.out + r.err).toContain("skipped tr-TR: in the listing file but no entry in languages[] has play: 'tr-TR'");
else expect(msg).toMatch(/en-US/); // en-US has no renders in the temp project: that error, not the tr-TR one
});
});