iOS Build & Distribution¶
iOS apps build and ship to TestFlight via GitHub Actions — a build-and-upload.yml workflow in each app repo. Xcode Cloud has been retired in favour of GitHub Actions (repeatable builds + full control over runners and secrets). The historical Xcode Cloud setup is kept under Legacy for reference.
Pipeline (GitHub Actions)¶
Each dashecorp/dashe-* app repo has .github/workflows/build-and-upload.yml:
| Aspect | Value |
|---|---|
| Runner | macos-26 (GitHub-hosted) |
| Trigger | workflow_dispatch (manual) |
| Signing | fastlane match (read-only) — appstore cert + profile pulled from dashecorp/ios-certs (git storage, master), via a token minted from the iBuild-E GitHub App |
| Build number | computed from App Store Connect (latest build + 1) via the ASC API |
| Archive / export | xcodebuild |
| Upload | ASC API key → TestFlight (internal). App Store submission stays human-gated. |
Apps with the workflow: Nutri-E, Fast-E, Count-E, Drink-E, Heart-E, Reward-E (all dashecorp/dashe-*).
Secrets (per app repo)¶
Provisioned via dashecorp/infra (dashe-secrets.tf, OpenTofu — never hand-set):
| Secret | Purpose |
|---|---|
IBUILD_E_APP_ID / IBUILD_E_PRIVATE_KEY |
GitHub App to read dashecorp/ios-certs |
MATCH_PASSWORD |
fastlane match decryption |
ASC_KEY_ID / ASC_ISSUER_ID / ASC_KEY_P8_BASE64 |
App Store Connect API key (build number + upload) |
FIREBASE_* (app-specific) |
e.g. Crashlytics GoogleService-Info.plist generation |
Signing (fastlane match)¶
Distribution certs + provisioning profiles live encrypted in dashecorp/ios-certs (git storage). CI runs match … readonly:true and never mints certs in CI. A new app / bundle id therefore needs a one-time fastlane match appstore write run to populate ios-certs before its workflow can sign.
Onboarding a new iOS app¶
- Register the bundle id (Apple Developer / ASC API
register_bundle_id). - Create the App Store Connect app record in the web UI — the ASC API has no create-app endpoint.
- One-time
fastlane match appstorewrite for the bundle id →dashecorp/ios-certs. - Fan the iOS secrets above to the repo via
dashe-secrets.tf. - Add
build-and-upload.yml(copy from anydashe-*app and adaptAPP_IDENTIFIER/SCHEME/ project path).
Legacy — Xcode Cloud (retired)¶
iOS CI previously ran on Apple's Xcode Cloud (archive → App Store Connect → TestFlight, triggered on main push). Retired in favour of the GitHub Actions pipeline above. Historical product IDs (no longer triggered):
| App | Xcode Cloud product ID |
|---|---|
| Nutri-E | A7AEE8F6 |
| Reward-E | 3B4EECB2 |
| Fast-E | AAFB4E73 |
| Count-E | AEB723D3 |
| Drink-E | 275BD2D2 / 8F9F479E (dup) |
| SupTrackAI (pre-Nutri-E) | 8B960BC7 |
| Cuti-E Admin / Feedback / Super Admin | 2F32A418 / 046A65C8 / 063839D0 (cuti-e/cutie) |
Cleanup notes for the retired setup:
- The Xcode Cloud GitHub App may still be installed account-wide on Stig-Johnny (creates check suites on all repos, incl. non-iOS); auto-merge workflows filter out xcode-cloud checks. Restrict/remove it in GitHub Settings → Integrations.
- Disable any monitor-xcode-cloud.yml workflow on a migrated repo — it alarms on absent Xcode Cloud builds.
- Apps use XcodeGen; the old ci_scripts/ci_post_clone.sh ran xcodegen generate on Xcode Cloud. The GitHub Actions workflow handles project generation + Info.plist itself.