apple signing: App Group association fails early under automatic signing too (API capability + newest profile, steps and deep link, exit before the archive); console plan/verify apple-app-group

This commit is contained in:
CRTkafa 2026-10-25 22:34:14 +07:00
parent d978bb7a8b
commit 304407e523
20 changed files with 590 additions and 60 deletions

View file

@ -19,6 +19,7 @@ import { APPLE_PRIVACY_TYPES } from './app-privacy';
import { typeLabels } from './console-forms';
import { playAppIdInText } from './play-app-id';
import { APPLE_PURPOSES } from './console-answers';
import type { AppGroupGap } from './targets';
export type RowStatus = 'match' | 'mismatch' | 'not-found' | 'ask-owner';
export interface VerifyRow { item: string; expected: string; seen: string; status: RowStatus; note?: string }
@ -360,7 +361,7 @@ export function parseStates(raw: string): { states: InputState[]; text: string }
* the privacy policy URL) and option labels must appear; the App category's "Not selected" is a mismatch. Yes / No
* radios cannot be told apart in page text: reported, never counted as a match.
*/
export function verifyPlayDeclaration(form: Exclude<PlanOnlyFormId, 'play-app-content'>, config: ShipConfig, text: string): VerifyResult {
export function verifyPlayDeclaration(form: Exclude<PlanOnlyFormId, 'play-app-content' | 'apple-app-group'>, config: ShipConfig, text: string): VerifyResult {
const part = playOnlyPart(form, config, {});
const st = parseStates(text);
const blob = fold(st ? st.text : text);
@ -444,6 +445,27 @@ export function verifyPublishChangesApi(ready: { track: string; version: string
...(ready.undetermined ? ['A release counts as ready only when the public store page shows an OLDER version; a 404, failed read or same version is never ready. Check Publishing overview ("Last published on ...") or pass the page text with --from-text -.'] : [])]);
}
/**
* apple-app-group (read-only, API): one row per (bundle id, App Group). match: the group is on the newest readable
* profile or named by the capability settings; mismatch: the evidence shows it missing; not-found: nothing readable
* proves it (after the portal Save the old profiles are invalid: run `storebound apple signing ensure --refresh-profile`,
* then verify again). ok only when every row matches.
*/
export function verifyAppGroupsApi(targets: { bundleId: string; appGroups: string[] }[], verdicts: { gaps: AppGroupGap[]; unproven: AppGroupGap[]; ok: string[] }): VerifyResult {
const rows: VerifyRow[] = [];
for (const t of targets.filter(x => x.appGroups.length)) {
const gap = verdicts.gaps.find(g => g.bundleId === t.bundleId), un = verdicts.unproven.find(g => g.bundleId === t.bundleId);
for (const g of t.appGroups) {
const item = `${t.bundleId}: ${g}`;
if (gap?.groups.includes(g)) rows.push({ item, expected: 'on the App ID (profile carries it)', seen: 'missing', status: 'mismatch', ...(gap.why ? { note: gap.why } : {}) });
else if (un?.groups.includes(g)) rows.push({ item, expected: 'on the App ID (profile carries it)', seen: 'cannot be proven', status: 'not-found', note: 'no ACTIVE profile shows it: run storebound apple signing ensure --refresh-profile, then verify again' });
else rows.push({ item, expected: 'on the App ID (profile carries it)', seen: 'present', status: 'match' });
}
}
const r = result('apple-app-group', 'api', rows, ['Reads the App Store Connect API: the App Groups capability and the newest readable provisioning profile of each bundle id.']);
return rows.some(x => x.status === 'not-found') ? { ...r, ok: false } : r;
}
/** Page-text verification of any form that has one. */
export function verifyText(form: PlanFormId, config: ShipConfig, text: string): VerifyResult {
if (form === 'play-app-content') return verifyAppContent(text);
@ -460,6 +482,7 @@ export function verifyText(form: PlanFormId, config: ShipConfig, text: string):
case 'play-target-audience': return verifyTargetAudience(config, text);
case 'apple-app-record': return verifyAppRecordText(config, text);
case 'apple-age-rating': throw new Error('console verify apple-age-rating reads the App Store Connect API (no --from-text)');
case 'apple-app-group': throw new Error('console verify apple-app-group reads the App Store Connect API (no --from-text)');
}
}