## 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
8.8 KiB
Apple App Store Release Runbook
This is the operating runbook for the public iOS application and its signed GitHub Actions delivery. Public review submission and public release remain human actions in App Store Connect.
Storefront record
Create or reconcile one App Store Connect record:
| Setting | Value |
|---|---|
| Name | Truck Wash Kundeportal |
| Bundle ID | io.truckwash.app |
| SKU | truckwash-ios |
| Primary language | Danish |
| Category | Business |
| Price | Free |
| Availability | Denmark only |
| Support URL | https://truckwash.io/support |
| Privacy URL | https://truckwash.io/privacy-policy |
| Marketing URL | https://truckwash.io/ |
| Release | Manual after approval |
Use the standard Apple EULA and do not configure in-app purchases. Payments in the product cover physical truck-wash services. Keep iPhone and iPad enabled; disable Apple-silicon Mac and Vision Pro compatibility until those targets have been tested deliberately.
The Account Holder or Admin must complete these console-only items before the first candidate:
- Accept current Apple developer and business agreements.
- Verify Truck Wash ApS's EU Digital Services Act trader identity and contact information.
- Complete the current age-rating questionnaire. Do not hard-code an expected rating in automation.
- Approve the privacy data matrix and enter matching App Privacy answers, including third-party SDK behavior.
- Decide export compliance after reviewing the final binary. Only add
ITSAppUsesNonExemptEncryption=falsewhen the exempt determination is approved. - Complete accessibility declarations only for behavior verified on devices.
- Store a durable, sanitized review account in App Store Connect. Never commit its password, OTP seed, or recovery data.
Review notes must explain customer and driver login, the review account's 2FA path, QR/hardware behavior, camera/location denial fallbacks, and the physical service payment model.
Metadata and assets in Git
fastlane/metadata/da-DK/ is the Danish storefront source of truth.
ios/release.json is the release-version source of truth. Its version is
numeric X.Y.Z; its bundle ID must remain io.truckwash.app.
Run the readiness validation locally:
npm run mobile:ios:storefront:check
Readiness mode validates all present assets and reports missing screenshot sets as warnings. A candidate tag runs strict mode and requires exactly six reviewed images in each set:
fastlane/screenshots/da-DK/iphone-6.9-01-*.pngthrough06, 1320×2868.fastlane/screenshots/da-DK/ipad-13-01-*.pngthrough06, 2064×2752.
Use Xcode 26 simulators and the real Capacitor app. Capture dashboard, booking, self-wash/QR, vehicles, orders/history, and invoices. Screenshots must contain sanitized fixture data, no alpha channel, no real customer data, and no placeholder content. Linux CI cannot honestly synthesize authenticated native captures; capture and approve them on a controlled macOS machine before tagging.
The validator also rejects the known default Capacitor icon and splash artwork.
Native permission strings must exist in Danish and English and are included via
the InfoPlist.strings Xcode variant group.
Apple identities and GitHub configuration
Create:
- A dedicated App Store Connect team API key named
GitHub App Store CIwith the App Manager role. Team JWTs use the account issuer ID in theissclaim. - A dedicated Apple Distribution certificate for CI.
- An App Store distribution provisioning profile for
io.truckwash.app. - An internal TestFlight group named
Internal QAwith automatic distribution.
Configure two GitHub environments:
app-store-signing, branch policy limited to protectedmaster.app-store-candidate, tag policy limited to protectedios-v*tags.
Private repositories on the Team plan cannot rely on environment required
reviewers. Protect ios-v* creation/update/deletion with a repository ruleset
limited to release managers. Manual App Review submission is the final human
approval.
Environment secrets:
IOS_DISTRIBUTION_CERTIFICATE_P12_BASE64IOS_DISTRIBUTION_CERTIFICATE_PASSWORDIOS_APP_STORE_PROFILE_BASE64APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64
Environment variables:
APPLE_TEAM_ID=HP3FJ4GVL7IOS_BUNDLE_ID=io.truckwash.appIOS_SCHEME=AppIOS_PROJECT=ios/App/App.xcodeprojAPP_STORE_CONNECT_API_KEY_IDAPP_STORE_CONNECT_APP_ID(Apple's numeric app resource ID)TESTFLIGHT_INTERNAL_GROUP_ID(Apple's beta-group resource ID)APP_STORE_CONNECT_ISSUER_ID=074cc671-edc3-403d-b85f-98470f3b16bd
The repository variable APP_STORE_AUTOMATION_ENABLED is the authoritative
activation switch. Missing or any value other than true makes all signing,
credential-health, and candidate workflows succeed as safe no-ops without
selecting an App Store environment or reading Apple secrets.
Enablement and first canary
Keep APP_STORE_AUTOMATION_ENABLED=false while configuring Apple/GitHub state.
Then:
- Merge all product-readiness work and confirm
App Store Readinesspasses. After its first successful default-branch run, add that job to the protected master ruleset's required status checks. - Verify the privacy policy, account-deletion flow, icons, localized permission copy, and privacy manifest on a device.
- Set the repository switch to
trueduring a controlled release window. - Dispatch
iOS Internal TestFlightfrom themasterworkflow definition, supplying the full current master SHA and confirmationUPLOAD IOS INTERNAL BUILD. - Confirm the workflow validates Xcode 26.3/iOS 26, certificate/profile identity and expiry, the signed IPA, App Store processing, and exact Internal QA assignment.
- Install the result on a clean supported iPhone and iPad. Verify fresh install, upgrade, login, resume, offline/reconnect, permission allow/deny, booking, self-wash/QR, vehicles, orders, invoices, support/privacy, and account deletion.
- Leave the switch enabled only after the canary is accepted.
If the first live credential attempt fails, set the repository switch back to
false before investigating. This avoids red master releases while credentials
are incomplete.
Continuous TestFlight delivery
Frontend Release publishes a signed-by-CI evidence artifact only after the
production deployment, public live gate, credentialed live gate, Release
Manager gate, and server-version update all pass for current master.
iOS Internal TestFlight consumes that exact proof. It refuses a stale SHA,
uses /Applications/Xcode_26.3.app, requires an iOS 26 SDK, queries App Store
Connect for the next build number under serialized concurrency, signs and
inspects the IPA, uploads through pinned Fastlane, waits for processing, and
idempotently assigns the exact build to Internal QA.
Outputs include:
- Signed IPA, retained for 30 days.
- dSYMs, SHA-256 checksums, and
ios-release-manifest.json, retained for 90 days. - Source SHA, marketing/build versions, App Store build ID, Xcode/SDK versions, and workflow identity in the manifest.
Every successful future Frontend Release for current master triggers this
delivery automatically. Stale or proofless releases do not sign or upload.
Select a public candidate
- Verify the desired TestFlight build on iPhone and iPad.
- Confirm its commit's
ios/release.jsoncontains the public version. - Create a new protected tag such as
ios-v1.0.0on that exact commit. Never move or reuse an existing release tag. iOS App Store Candidatelocates the release manifest for that exact SHA, verifies the exact processed App Store build, enforces complete screenshots, synchronizes Danish metadata, attaches the existing build, and reads it back. It does not rebuild, submit for review, or release publicly.- In App Store Connect, review the rendered product page, review account, privacy/export/age answers, and candidate build. Submit manually.
- Release the first Denmark version manually after approval. Use phased release for later updates unless there is a reason not to.
- Merge the next
ios/release.jsonversion bump before further delivery after Apple closes the released version to new builds.
Rotation and recovery
iOS Credential Health runs every Monday and fails when certificate/profile
identity drifts or either expires within 30 days. Rotate one credential at a
time, keep automation disabled during rotation, and repeat the canary.
- Bad TestFlight build: expire it, fix master, and produce a new build number.
- Bad candidate: detach it in App Store Connect and tag a corrected tested SHA with a new version; never move the tag.
- Bad phased update: pause the phase.
- Compromised key/certificate: disable automation, revoke it in Apple, rotate GitHub secrets, inspect audit logs, and run a fresh canary.
- Public emergency: remove from sale only when necessary and prepare an expedited corrective version.