apple.macos listing / review notes per platform + iOS-only claim warnings; apple upload of a .pkg: bounded pkgutil, macOS processing wait, heartbeats; release 409 pattern

This commit is contained in:
CRTkafa 2026-11-03 23:30:59 +07:00
parent 53d57544bf
commit 1e47c2ee60
18 changed files with 417 additions and 63 deletions

View file

@ -707,6 +707,15 @@ export interface ShipConfig {
* 2560 x 1600, 1440 x 900, 1280 x 800). Landscape layout from <frames>/<lang>/landscape/ (shots.landscape). Uploaded
* as APP_DESKTOP to the macOS version: `apple screenshots --platform macos`. */
mac?: boolean;
/**
* macOS App Store overrides (not `mac`, which only switches Mac screenshots on). An app with an iOS and a macOS
* version often needs different version texts and review notes (the Mac app may have no Game Center, in-app
* purchases or ads). `listing`: an App Store listing file used instead of apple.listing for `apple listing
* --platform macos`; `reviewNotes`: used instead of apple.info.reviewNotes by `apple review-details --platform macos`.
* `gameCenter: true`: the Mac app has Game Center (silences the Game Center check of `validate --platform macos`).
* `noIap: true`: the Mac app sells nothing (validate then flags in-app purchase lines in the Mac texts).
*/
macos?: { listing?: string; reviewNotes?: string; gameCenter?: boolean; noIap?: boolean };
/** "Header and Search Results" creative assets (`storebound apple creative-assets`, iOS / iPadOS 27+). Files under
* <renders>/<lang>/creative/: header.{png,jpg,mov,m4v,mp4} (3840x1646, 21:9), search.{...} (3:2, 1920x1280 to
* 3840x2560) or universal.png (5244x2950) for both. `header` / `search` false skip that slot; `universal` is a
@ -1036,6 +1045,14 @@ export function validateExtras(cfg: Partial<ShipConfig>): string[] {
e.push('play.developerAccount: the developer account name as Play Console shows it (1-100 characters)');
if (cfg.play?.name !== undefined && !(typeof cfg.play.name === 'string' && cfg.play.name.trim() && [...cfg.play.name].length <= 30)) e.push('play.name: 1-30 characters (Play app name)');
if (cfg.play?.free !== undefined && typeof cfg.play.free !== 'boolean') e.push('play.free: true (free) | false (paid)');
const mo = cfg.apple?.macos;
if (mo !== undefined) {
if (typeof mo !== 'object' || mo === null || Array.isArray(mo)) e.push('apple.macos: { listing?: "store/appstore-macos.md", reviewNotes?: "...", gameCenter?: true, noIap?: true }');
else {
for (const k of ['listing', 'reviewNotes'] as const) if (mo[k] !== undefined && (typeof mo[k] !== 'string' || !mo[k]!.trim())) e.push(`apple.macos.${k}: a non-empty string${k === 'listing' ? ' (path of the macOS App Store listing file)' : ''}`);
for (const k of ['gameCenter', 'noIap'] as const) if (mo[k] !== undefined && typeof mo[k] !== 'boolean') e.push(`apple.macos.${k}: true | false`);
}
}
const ad = cfg.apple?.adhoc;
if (ad?.method !== undefined && ad.method !== 'release-testing' && ad.method !== 'ad-hoc') e.push('apple.adhoc.method: release-testing | ad-hoc');
const ppp = cfg.prices?.ppp;
@ -1114,6 +1131,23 @@ function validateIarcDetails(d: IarcDetails | undefined): string[] {
}
/** Platforms of the app (app identity): app.platforms, else from the store sections. */
/** True for the macOS platform in any spelling: "macos", "mac_os" (API name MAC_OS), "mac". Pure. */
export const isMacosPlatform = (p: string | undefined | null): boolean => !!p && /^mac(_?os)?$/i.test(p.replace(/[\s-]/g, ''));
/** The App Store listing file (as written in the config, relative) for a platform: apple.macos.listing for macOS when
* set, else apple.listing, else store/appstore.md. Pure. */
export function listingPathFor(config: Pick<ShipConfig, 'apple'>, platform?: string | null): string {
const mac = isMacosPlatform(platform) ? config.apple?.macos?.listing?.trim() : '';
return mac || config.apple?.listing || 'store/appstore.md';
}
/** The App Review notes text of the config for a platform: apple.macos.reviewNotes for macOS when set, else
* apple.info.reviewNotes. Pure; reviewAccess and account deletion lines are added by reviewNotes() in lib/review-details. */
export function reviewNotesFor(config: Pick<ShipConfig, 'apple'>, platform?: string | null): string | undefined {
const mac = isMacosPlatform(platform) ? config.apple?.macos?.reviewNotes : undefined;
return mac?.trim() ? mac : config.apple?.info?.reviewNotes;
}
/** App Store name limit (characters). */
export const APPLE_NAME_MAX = 30;