apple: release type (apple.releaseType, apple release --after-approval|--manual), device-family preflight, TestFlight invite needs an installable build

This commit is contained in:
CRTkafa 2026-10-27 17:07:37 +07:00
parent 4763862178
commit 85cf494c0a
24 changed files with 899 additions and 18 deletions

View file

@ -320,3 +320,22 @@ export function betaDescriptions(t: TfConfig | undefined, langs: { id: string; a
}
return out;
}
// ---- invitations need an installable build ----
/** Whether a build can be installed by the testers of a group: valid, not expired, and past Beta App Review for an
* external group (externalBuildState BETA_APPROVED / IN_BETA_TESTING), ready for internal testing for an internal one
* (internalBuildState READY_FOR_BETA_TESTING / IN_BETA_TESTING). `detail`: the build's buildBetaDetails attributes. Pure. */
export function installableBuild(build: { attributes: Record<string, unknown> }, detail: Record<string, unknown> | null | undefined, internal: boolean): boolean {
if (build.attributes.expired === true) return false;
if (build.attributes.processingState !== undefined && build.attributes.processingState !== 'VALID') return false;
const state = String((internal ? detail?.internalBuildState : detail?.externalBuildState) ?? '');
return internal ? ['READY_FOR_BETA_TESTING', 'IN_BETA_TESTING'].includes(state) : ['BETA_APPROVED', 'IN_BETA_TESTING'].includes(state);
}
/** The line for testers that are added but cannot be invited yet. Pure. */
export const noInstallableBuildHint = (groups: string[]): string =>
`tester added; the invite goes out when a build in ${groups.join(', ')} passes Beta App Review (re-run with --invite then)`;
/** Apple's 409 STATE_ERROR.TESTER_INVITE.NO_INSTALLABLE_BUILDS on POST /v1/betaTesterInvitations (seen live). Pure. */
export const noInstallableBuilds409 = (e: unknown): boolean => /NO_INSTALLABLE_BUILDS/.test(String((e as Error)?.message ?? e));