## What changed - adds production iOS identity, localized storefront metadata, native privacy declarations, App Store-safe artwork, and account-deletion UX - mirrors the live Danish Google Play title, short description, and long description in the App Store metadata source - generates Android launcher/store icons from the opaque iOS marketing master so both platforms use the same white background - adds guarded GitHub Actions workflows for storefront readiness, credential health, signed TestFlight uploads, and App Store candidate preparation - adds pinned Fastlane configuration with a committed dependency lock, release manifest tooling, and an operational App Store runbook - preserves the upstream iOS safe-area implementation while retaining opaque App Store icon assets ## Why The repository previously supported development-signed device bundles but had no production App Store identity, reproducible storefront source of truth, or protected signed-release pipeline. Apple also requires in-app account deletion for apps that support account creation. The Android icon master was transparent, which rendered as black on dark store/device surfaces. ## Impact Automation remains fail-closed behind `APP_STORE_AUTOMATION_ENABLED=false`. No build can upload to TestFlight or change App Store metadata until the switch is deliberately enabled after merge and the remaining release gates are satisfied. ## Validation - focused App Store, iOS icon, and cross-platform icon-background tests pass - every generated Android store/launcher icon is opaque with pure-white corners; iOS marketing artwork is checked the same way - Android icon drift check passes for all 19 generated files - production Vite build and the broader focused release checks completed successfully - storefront metadata is valid; only the two expected screenshot-set warnings remain - App Store Readiness is green at head `4445fecc` - Apple Distribution certificate and App Store profile were independently verified for `HP3FJ4GVL7.io.truckwash.app` - live App Store Connect API authentication succeeded for app `6792777794` - App Store record, free Denmark-only availability, and automatic `Internal QA` TestFlight group are configured - EU trader status, Content Rights, 4+ age rating, and the published App Privacy label are completed in App Store Connect - iPhone and iPad accessibility declarations are configured honestly as pre-release drafts ## Remaining external gates - reviewed iPhone and iPad screenshot sets are still required - an App Review login must be supplied without creating or exposing customer credentials - the first signed TestFlight candidate must run after merge and deliberate automation enablement
37 lines
1.4 KiB
JavaScript
37 lines
1.4 KiB
JavaScript
import { createHash } from "node:crypto";
|
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { dirname } from "node:path";
|
|
import { env, exit } from "node:process";
|
|
|
|
const required = (name) => {
|
|
if (!env[name]) throw new Error(`Missing ${name}`);
|
|
return env[name];
|
|
};
|
|
|
|
try {
|
|
const ipaPath = required("IOS_IPA_PATH");
|
|
const output = env.IOS_RELEASE_MANIFEST_PATH || "output/ios-release/ios-release-manifest.json";
|
|
const manifest = {
|
|
schemaVersion: 1,
|
|
repository: required("GITHUB_REPOSITORY"),
|
|
sourceSha: required("IOS_SOURCE_SHA").toLowerCase(),
|
|
marketingVersion: required("IOS_MARKETING_VERSION"),
|
|
buildNumber: required("IOS_BUILD_NUMBER"),
|
|
appStoreBuildId: required("APP_STORE_BUILD_ID"),
|
|
appStoreConnectAppId: required("APP_STORE_CONNECT_APP_ID"),
|
|
bundleId: required("IOS_BUNDLE_ID"),
|
|
xcodeVersion: required("XCODE_VERSION"),
|
|
sdkVersion: required("IOS_SDK_VERSION"),
|
|
workflowRunId: required("GITHUB_RUN_ID"),
|
|
workflowRunAttempt: required("GITHUB_RUN_ATTEMPT"),
|
|
ipaSha256: createHash("sha256").update(readFileSync(ipaPath)).digest("hex"),
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
mkdirSync(dirname(output), { recursive: true });
|
|
writeFileSync(output, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
|
|
console.log(`Created ${output}.`);
|
|
} catch (error) {
|
|
console.error(error instanceof Error ? error.message : error);
|
|
exit(1);
|
|
}
|