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

@ -38,6 +38,7 @@ import { iphoneDuoNote } from './shots/spec';
import { hitLine } from './lib/cross-store';
import { findFastlaneTraces, traceMessage } from './lib/fastlane-traces';
import { listingFileMentions } from './lib/listing-mentions';
import { projectFamilies } from './lib/device-families';
/** The newest .ipa in a folder (apple build's export), or null. */
function newestIpa(dir: string): string | null {
@ -173,6 +174,14 @@ export async function run(ctx: Ctx): Promise<void> {
note('shots.capture.auth signs in to a local stack with reviewDemoAccount, the account App Review gets from apple review-details: put the local demo sign-in in captureAuth in .storebound/credentials.json and use ${cred:<name>}'); }
const duo = iphoneDuoNote({ apple: !!config.apple, duo: !!config.apple?.iphoneDuo, play: !!config.play });
if (duo?.level === 'info') { hints.push(duo.msg); log('check', `hint: ${duo.msg}`); } else if (duo) note(duo.msg);
// device families: offline only the project and the config can be compared; the live version's families need the store
if (config.apple) {
const proj = projectFamilies(ctx.loaded.root, config.apple.bundleId, config.apple.build?.project);
const iosOnly = !proj || !proj.families.ipad;
if (proj && proj.families.ipad && config.apple.ipad !== true) note(`${proj.file} builds for iPad (TARGETED_DEVICE_FAMILY ${proj.configurations.join(' / ')}) but apple.ipad is not true: iPad screenshots are not required or checked (set apple.ipad: true)`);
else if (proj && !proj.families.ipad && config.apple.ipad === true) note(`apple.ipad is true but ${proj.file} builds TARGETED_DEVICE_FAMILY ${proj.configurations.join(' / ')} (no iPad): set "1,2" or apple.ipad: false`);
else if (iosOnly && config.apple.ipad !== true) note(`device families: ${proj ? `iPhone only (apple.ipad not true, TARGETED_DEVICE_FAMILY ${proj.configurations.join(' / ')})` : 'no Xcode project found here'}, and the families of the live version are not known offline: if it supports iPad, Apple refuses an iPhone-only upload after the build (ITMS-90101). storebound validate and apple build compare with the store first`);
}
const creative = creativeNote(config, ctx.loaded.resolve(config.renders ?? config.shots?.out ?? 'store/renders'));
if (creative) { hints.push(creative); log('check', `hint: ${creative}`); }
// the scaffolder's own placeholders would reach the stores as the app's copyright, contact and legal pages