merge origin/main (cross-store text checker); macOS platform pitfall renumbered to 79

This commit is contained in:
CRTkafa 2026-10-20 03:06:16 +07:00
commit 9db7180eb9
35 changed files with 618 additions and 60 deletions

View file

@ -15,6 +15,7 @@
// listing file: shows the diff, writes only with --write.
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { type AppleCopy, parseAppStoreListing } from '../lib/copy';
import { fieldsMentions, hitLine, type PlatformHit, refuseMentions } from '../lib/cross-store';
import { compactDiff } from '../lib/diff';
import { mergeAppleListing, overLimits } from '../lib/listing-pull';
import { counts, draftReason, draftSections, type ItemPlan, itemLines, planItem, planLocales, planVersion, short, type Skipped, type VersionPlan } from '../lib/listing-plan';
@ -93,6 +94,8 @@ export interface AppleListingPlan {
skipped: Skipped[];
notes: string[];
changes: number;
/** Android, Google Play, ... in the texts this run sends (Guideline 2.3.10): the run is refused (pitfall 77) */
mentions: PlatformHit[];
}
type Asc = Awaited<ReturnType<typeof ascClient>>;
@ -140,7 +143,10 @@ async function planListing(ctx: Ctx, c: Asc, appId: string, version: string, pla
const changes = (versionPlan.action === 'create' || versionPlan.action === 'update' ? 1 : 0) + (build?.action === 'attach' ? 1 : 0)
+ infoLocs.filter(x => x.action !== 'same').length + (versionLocales ? versionLocales.filter(x => x.action !== 'same').length : Object.keys(wanted).length);
return { version, platform, versionPlan, build, appInfo: { id: info.id, state: String(info.attributes?.state ?? info.attributes?.appStoreState ?? ''), locales: infoLocs },
const allow = config.listing?.allowPlatformMentions;
const mentions = Object.entries(listing).flatMap(([l, f]) => fieldsMentions('apple', l, { name: f.name, subtitle: f.subtitle, promotionalText: f.promotionalText,
keywords: f.keywords, description: f.description, whatsNew: versionPlan.first ? undefined : f.whatsNew }, allow));
return { mentions, version, platform, versionPlan, build, appInfo: { id: info.id, state: String(info.attributes?.state ?? info.attributes?.appStoreState ?? ''), locales: infoLocs },
versionLocales, wanted, skipped, notes, changes };
}
@ -163,6 +169,7 @@ export function listingPlanLines(p: AppleListingPlan): string[] {
}
for (const s of p.skipped) out.push(`Skipped ${s.key}: ${s.reason}`);
for (const n of p.notes) out.push(`Note: ${n}`);
for (const h of p.mentions ?? []) out.push(`REFUSE: ${hitLine(h)}`);
return out;
}
@ -198,7 +205,8 @@ export async function run(ctx: Ctx): Promise<void> {
const isEd = (x: J) => EDITABLE.has(x.attributes.appVersionState ?? x.attributes.appStoreState);
const platform = parsePlatform(a.get('platform')) ?? pickPlatform(await allVersions(c, appId), [(x: J) => x.attributes.versionString === version && isEd(x), isEd], undefined, `an editable version (${version})`);
const p = await planListing(ctx, c, appId, version, platform);
const refused = [p.versionPlan.action === 'refuse' ? p.versionPlan.reason : '', p.build?.action === 'refuse' ? p.build.reason : ''].filter(Boolean);
const refused = [p.versionPlan.action === 'refuse' ? p.versionPlan.reason : '', p.build?.action === 'refuse' ? p.build.reason : '',
(() => { try { refuseMentions(p.mentions, 'apple listing'); return ''; } catch (e) { return (e as Error).message; } })()].filter(Boolean);
if (a.has('json')) printJson({ dry, ...p, refused });
else for (const line of listingPlanLines(p)) console.log(line);
if (refused.length) { if (dry) { process.exitCode = 1; warn('apple', `dry run: would refuse: ${refused.join('; ')}`); return; } throw new Error(refused.join('; ')); }

View file

@ -1,5 +1,5 @@
// App Store platforms of one app record. An app can have an iOS and a macOS (tvOS, visionOS) version at the same time,
// each with its own state and its own review submission (pitfall 77): every version lookup filters by platform, and a
// each with its own state and its own review submission (pitfall 79): every version lookup filters by platform, and a
// command that needs one version picks the platform that has it, or asks for --platform when several do.
import type { Asc, J } from './asc';