ADR-0047: Mobile CI/CD on Codemagic (backend stays on GitHub Actions)
Status
Accepted (pending FieldForce Phase 1 implementation)Tags
fieldforce, digital-worker, mobile, flutter, ci-cd, codemagic, github-actions, code-signing, monorepoDecision
The Flutter apps build, sign, and distribute through Codemagic. The Go/TS backend stays on GitHub Actions. Right tool per domain — not one CI everywhere.- Codemagic owns Flutter builds, iOS/Android code signing, and store distribution.
- Path-based triggers: a change under
apps/<app>/**or apackages/*it depends on builds only the affected app(s) (ADR-0038 monorepo). - Shorebird (Dart code push) may be added later via Codemagic if OTA-style updates are wanted (deferred).
Why
iOS builds require macOS, and the homelab K3s/Raspberry Pi cluster is ARM Linux — it cannot build or sign iOS. So mobile CI/CD must use a cloud provider with macOS runners, unlike the backend which self-hosts comfortably on existing hardware. This is the framing constraint (ADR-0037). Given a cloud-mac requirement either way, the choice is Codemagic vs GitHub Actions + Fastlane. The hardest, most error-prone part of mobile CI/CD is iOS code signing, provisioning, and store submission. Codemagic is purpose-built for Flutter and handles this natively (App Store Connect API key signing, provisioning, TestFlight/store upload), whereas GitHub Actions requires assembling Fastlane and paying the 10× macOS-minute multiplier. For a solo/lead who does not want to babysit pipeline plumbing (the recurring “least debugging” theme, ADR-0037), Codemagic removes the nastiest part with the least setup. The must-record nuance is the per-domain split rather than one-tool-everywhere. The backend is already on GitHub Actions and fits it perfectly (self-hostable-adjacent, same GitOps mental model). Mobile CI/CD’s hard part (iOS signing) is a genuinely different and nastier problem than backend CI/CD — the one place a specialized tool earns its keep over a general one. So the decision deliberately runs two CI systems, each where it’s strongest, rather than forcing the backend onto Codemagic or the mobile apps onto Actions. Rejected alternatives:- GitHub Actions + Fastlane for mobile too (one tool everywhere). Single pane of glass and consistent with the backend, but requires Fastlane setup for iOS signing and incurs 10× macOS-minute cost; more plumbing to maintain. Defensible, but rejected in favor of least-friction iOS handling.
- Self-host mobile CI on the homelab. Impossible for iOS (no macOS on ARM Linux). Rejected.
- Native-only pipelines (Xcode Cloud + Play Console internal). Splits the two platforms across two unrelated systems; loses unified Flutter build config. Rejected.
Consequences
- Two CI systems to maintain (Codemagic for mobile, GitHub Actions for backend) — accepted as the per-domain trade-off.
- Codemagic free tier covers low build volume; a fixed-price plan gives unlimited builds if volume grows.
- iOS signing secrets and App Store Connect keys live in Codemagic secrets, never the repo (consistent with the config/secrets boundary, CLAUDE.md §11).
- Path-based triggers must be configured per app so the monorepo builds only affected apps.
- Shorebird integration remains an available later option for OTA Dart code push.
Rules for agents
- Mobile apps build/sign/distribute via Codemagic; the backend stays on GitHub Actions. Do NOT merge the two.
- Configure path-based triggers so each app builds only on changes to its own code or a dependency package.
- Signing secrets / store keys live in Codemagic secrets, never in the repo or app bundle.
- Treat Shorebird (code push) as a deferred, optional later addition.