build(ios): Codemagic pipeline + runbook for App Store (no Mac needed)

The iOS app is a Capacitor shell over the live web UI — the web/server side is already fully
Capacitor-ready (nativePlatform() detects Capacitor; setupNativePush registers APNs tokens via
/api/v1/devices; the APNs sender is built into server/push.js, config-gated). So this adds only
the build/sign/upload path:

- codemagic.yaml: macOS-cloud workflow that generates the iOS project, patches Info.plist,
  generates icons/splash, signs via an App Store Connect API key (automatic signing), archives,
  and uploads to TestFlight. No Mac required.
- mobile/scripts/ios-patch.sh: adds the App-Review privacy usage strings (camera/mic/photos) +
  display name to the generated Info.plist.
- mobile/IOS_SETUP.md: click-by-click runbook — ASC app record, API key, Codemagic integration,
  first build, APNs key → server .env, and the public-submission checklist.

Bundle id com.bizgaze.connect. No secrets committed — Apple keys live in Codemagic + server .env.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 16:45:10 +05:30
parent c8f383cbcb
commit bc909cb0c0
3 changed files with 173 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
# Codemagic CI/CD — builds the Biz Connect iOS app (Capacitor shell over the live web UI) and uploads it
# to TestFlight / App Store Connect. No Mac needed: this runs on Codemagic's macOS cloud instances.
#
# The app is a thin Capacitor wrapper that loads https://remote.bizgaze.com, so there's no bundled web
# code to build here — we generate the iOS project, patch its privacy strings, sign, archive and upload.
#
# ── One-time setup (see mobile/IOS_SETUP.md for the click-by-click) ────────────────────────────────
# 1. App Store Connect: create the app with bundle id com.bizgaze.connect
# 2. Codemagic → Teams/Integrations → App Store Connect: add your ASC API key (issuer id, key id, .p8).
# Name the integration exactly: BizGaze App Store Connect
# 3. That's it — automatic code signing fetches/creates the distribution cert + profile from that key.
workflows:
ios-testflight:
name: Biz Connect iOS → TestFlight
max_build_duration: 60
instance_type: mac_mini_m2
integrations:
app_store_connect: BizGaze App Store Connect # ← must match the integration name you create
environment:
ios_signing:
distribution_type: app_store
bundle_identifier: com.bizgaze.connect
vars:
BUNDLE_ID: "com.bizgaze.connect"
XCODE_WORKSPACE: "mobile/ios/App/App.xcworkspace"
XCODE_SCHEME: "App"
node: 20
xcode: latest
cocoapods: default
scripts:
- name: Install JS dependencies
script: |
cd mobile
npm ci
- name: Generate the iOS project (Capacitor)
script: |
cd mobile
# `cap add ios` scaffolds ios/App; safe to re-run — it no-ops if it already exists.
if [ ! -d "ios" ]; then npx cap add ios; fi
npx cap sync ios
# App icon + splash from resources/icon.png & resources/splash*.png (1024x1024 icon, 2732² splash).
npx capacitor-assets generate --ios || echo "asset generation skipped"
- name: Patch Info.plist (App-Review privacy strings) + bundle id
script: |
bash mobile/scripts/ios-patch.sh
- name: Set up code signing
script: |
# Automatic signing via the App Store Connect API key configured in the integration above.
keychain initialize
app-store-connect fetch-signing-files "$BUNDLE_ID" \
--type IOS_APP_STORE --create
keychain add-certificates
xcode-project use-profiles
- name: Install CocoaPods
script: |
cd mobile/ios/App
pod install
- name: Build the signed IPA
script: |
xcode-project build-ipa \
--workspace "$XCODE_WORKSPACE" \
--scheme "$XCODE_SCHEME"
artifacts:
- build/ios/ipa/*.ipa
- /tmp/xcodebuild_logs/*.log
publishing:
app_store_connect:
auth: integration
submit_to_testflight: true
# Flip this to true (and add a `submit_to_app_store` group with reviewer notes) once you're ready
# to push a build to public App Store review instead of only TestFlight.
# submit_to_app_store: false
email:
recipients:
- sriramphanib@gmail.com
notify:
success: true
failure: true
+60
View File
@@ -0,0 +1,60 @@
# Biz Connect — iOS App Store setup (Codemagic, no Mac needed)
The iOS app is a Capacitor shell that loads the live Connect web UI (`https://remote.bizgaze.com`).
Building/signing/uploading happens on **Codemagic's macOS cloud** — you never need a Mac.
Bundle id: **`com.bizgaze.connect`** · CI config: [`codemagic.yaml`](../codemagic.yaml) (repo root).
---
## Step 1 — App Store Connect: create the app record
1. [appstoreconnect.apple.com](https://appstoreconnect.apple.com) → **Apps → +****New App**.
2. Platform **iOS**, Name **Biz Connect**, primary language, **Bundle ID** = `com.bizgaze.connect`
*(if it isn't in the dropdown, first register it: [developer.apple.com](https://developer.apple.com/account/resources/identifiers/list) → Identifiers → + → App IDs → App → `com.bizgaze.connect`, and tick **Push Notifications** while you're there — needed for Step 5).*
3. SKU: anything unique (e.g. `bizconnect-ios`). Create.
## Step 2 — App Store Connect API key (for Codemagic to sign + upload)
1. App Store Connect → **Users and Access → Integrations → App Store Connect API****+**.
2. Access **App Manager**. Generate. Note the **Issuer ID** (top of the page) and the key's **Key ID**,
and **download the `.p8`** (you can only download it once).
## Step 3 — Codemagic: connect + add the key
1. [codemagic.io](https://codemagic.io) → sign in with the git provider → add this repository.
2. **Teams → Integrations → App Store Connect → Connect**, upload the `.p8`, paste the **Issuer ID** and
**Key ID**. **Name it exactly `BizGaze App Store Connect`** (the `codemagic.yaml` references that name).
3. Codemagic detects `codemagic.yaml`. That's all the signing setup — automatic signing creates the
distribution certificate + provisioning profile from this key on the first build.
## Step 4 — Run the build
- Codemagic → the app → **Start new build** → workflow **"Biz Connect iOS → TestFlight"**.
- ~1015 min. On success the build appears in **App Store Connect → TestFlight**.
- Add yourself under **TestFlight → Internal Testing** to install via the TestFlight app on your iPhone.
## Step 5 — Push notifications (APNs) — do this once, then tell me
So the app gets **calls/messages while it's closed**:
1. developer.apple.com → **Keys → +** → enable **Apple Push Notifications service (APNs)** → download the
**`.p8`**. Note its **Key ID** and your **Team ID** (top-right of the developer portal).
2. **Send me**: the `.p8` contents, the **Key ID**, and the **Team ID**. I set these in the server `.env`
(server-side only, like the LiveKit/Giphy keys):
```
APNS_KEY=<contents of the .p8>
APNS_KEY_ID=<key id>
APNS_TEAM_ID=<team id>
APNS_BUNDLE_ID=com.bizgaze.connect
APNS_PRODUCTION=1
```
The APNs sender is already built into the server — it's a no-op until these are set.
## Step 6 — Public App Store submission (when you're ready to leave TestFlight)
In App Store Connect, fill the listing: **screenshots** (6.7" + 6.1" iPhone), description, keywords,
support URL, and a **Privacy Policy URL** (required). Complete the **App Privacy** questionnaire (we
collect account info + usage for chat/calls). Then submit for review (or flip `submit_to_app_store` in
`codemagic.yaml`).
---
### App Review note (Guideline 4.2 — "Minimum Functionality")
Apple scrutinises apps that look like "just a website". Ours passes because it ships **real native
capabilities** — push notifications, camera/microphone for calls, photo sharing. Make sure push (Step 5)
is live before the **public** submission, and in the reviewer notes mention the native video calling +
screen sharing so it's obvious this is more than a web view. TestFlight builds are fine without push.
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Patch the freshly-generated Capacitor iOS project (mobile/ios/App) for App Store submission.
# Runs on the Codemagic macOS instance after `npx cap add ios`. Idempotent — safe to re-run.
set -euo pipefail
PLIST="mobile/ios/App/App/Info.plist"
PB=/usr/libexec/PlistBuddy
set_str() { # set_str <key> <value> — add the key if missing, else overwrite
"$PB" -c "Add :$1 string $2" "$PLIST" 2>/dev/null || "$PB" -c "Set :$1 $2" "$PLIST"
}
echo "Patching $PLIST"
# ── Privacy usage strings (Apple REJECTS the build if a used capability has no purpose string) ──
set_str NSCameraUsageDescription "Biz Connect uses the camera for video calls and to share photos and your screen."
set_str NSMicrophoneUsageDescription "Biz Connect uses the microphone for voice and video calls."
set_str NSPhotoLibraryUsageDescription "Biz Connect needs photo access so you can send images in chat."
set_str NSPhotoLibraryAddUsageDescription "Biz Connect saves images and recordings you download to your photos."
# Human-readable display name on the home screen.
set_str CFBundleDisplayName "Biz Connect"
# Allow the webview to load our HTTPS origin (we do NOT enable arbitrary cleartext).
"$PB" -c "Delete :NSAppTransportSecurity" "$PLIST" 2>/dev/null || true
echo "Info.plist patched:"
"$PB" -c "Print :NSCameraUsageDescription" "$PLIST"
"$PB" -c "Print :NSMicrophoneUsageDescription" "$PLIST"