2026-07-15 16:45:10 +05:30
|
|
|
# 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:
|
2026-07-17 13:30:11 +05:30
|
|
|
# NOTE: we deliberately do NOT use an `ios_signing:` block here. That block makes Codemagic
|
|
|
|
|
# try to *fetch an existing* provisioning profile at build startup — it never creates one — so on
|
|
|
|
|
# a brand-new app it fails init with "No matching profiles found …". Instead the "Set up code
|
|
|
|
|
# signing" script below runs `fetch-signing-files … --create`, which creates the distribution
|
|
|
|
|
# certificate + profile on first run, then `xcode-project use-profiles` wires them into the project.
|
2026-07-17 15:47:28 +05:30
|
|
|
groups:
|
|
|
|
|
- ios_signing # ← Codemagic variable group holding CERTIFICATE_PRIVATE_KEY (secure). See below.
|
2026-07-15 16:45:10 +05:30
|
|
|
vars:
|
|
|
|
|
BUNDLE_ID: "com.bizgaze.connect"
|
2026-08-03 15:52:46 +05:30
|
|
|
XCODE_PROJECT: "mobile/ios/App/App.xcodeproj"
|
2026-07-15 16:45:10 +05:30
|
|
|
XCODE_SCHEME: "App"
|
2026-08-01 18:48:57 +05:30
|
|
|
node: 22
|
2026-07-15 16:45:10 +05:30
|
|
|
xcode: latest
|
|
|
|
|
cocoapods: default
|
|
|
|
|
scripts:
|
|
|
|
|
- name: Install JS dependencies
|
|
|
|
|
script: |
|
|
|
|
|
cd mobile
|
2026-07-17 17:36:14 +05:30
|
|
|
# npm install (not ci): the dependency set changed to Capacitor 7 + the safe-area/keyboard
|
|
|
|
|
# plugins, so we let npm resolve a fresh tree rather than require a pre-synced lockfile.
|
|
|
|
|
npm install
|
2026-07-15 16:45:10 +05:30
|
|
|
|
|
|
|
|
- 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.
|
2026-08-03 15:52:46 +05:30
|
|
|
# Capacitor 8 Swift Package Manager: generate an SPM project (no Podfile). LiveKit is pulled via the
|
|
|
|
|
# native-call plugin's Package.swift; `cap sync` wires all local plugins into the CapApp-SPM package.
|
|
|
|
|
if [ ! -d "ios" ]; then npx cap add ios --packagemanager SPM; fi
|
2026-07-15 16:45:10 +05:30
|
|
|
npx cap sync ios
|
2026-08-03 15:52:46 +05:30
|
|
|
# Sanity: the Xcode project must exist. Print the iOS dir so the log shows the SPM layout (CapApp-SPM
|
|
|
|
|
# present, NO Podfile) — if a Podfile appears, the SPM flag didn't take and we'd need to fix it.
|
|
|
|
|
test -d ios/App/App.xcodeproj || { echo "ERROR: iOS Xcode project not generated"; ls -la ios/App || true; exit 1; }
|
|
|
|
|
echo "iOS project layout:"; ls -la ios/App
|
2026-08-03 15:56:13 +05:30
|
|
|
# ── Diagnose local-plugin SPM wiring (the regression: our file: plugins didn't function at runtime) ──
|
|
|
|
|
echo "=== local plugins in node_modules — symlink vs copy, and is Package.swift present? ==="
|
2026-08-05 21:38:48 +05:30
|
|
|
for p in native-call media-library audio-route share-inbox file-opener; do
|
2026-08-03 15:56:13 +05:30
|
|
|
echo "-- $p --"; ls -ld "node_modules/$p" 2>/dev/null || echo " (dir missing)"
|
|
|
|
|
( ls "node_modules/$p/Package.swift" >/dev/null 2>&1 && echo " Package.swift PRESENT" ) || echo " Package.swift MISSING"
|
|
|
|
|
done
|
|
|
|
|
echo "=== CapApp-SPM Package.swift — are our local plugins + LiveKit wired in? ==="
|
|
|
|
|
CAPSPM=$(find ios -path "*CapApp-SPM*Package.swift" 2>/dev/null | head -1)
|
|
|
|
|
if [ -n "$CAPSPM" ]; then echo "found: $CAPSPM"; cat "$CAPSPM"; else echo " CapApp-SPM/Package.swift NOT FOUND"; find ios -name Package.swift 2>/dev/null; fi
|
2026-07-15 16:45:10 +05:30
|
|
|
# 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
|
|
|
|
|
|
2026-07-23 22:58:08 +05:30
|
|
|
- name: Add the Share Extension target
|
|
|
|
|
script: |
|
|
|
|
|
# Inject the second target (Biz Connect in the iOS share sheet) into the freshly-generated
|
|
|
|
|
# Xcode project. Uses the `xcodeproj` gem that ships with CocoaPods, so no extra install.
|
|
|
|
|
# Runs BEFORE pod install: the extension uses no pods, and this way the workspace that pods
|
|
|
|
|
# generates already contains the new target.
|
|
|
|
|
ruby mobile/scripts/add-share-extension.rb
|
|
|
|
|
|
2026-07-15 16:45:10 +05:30
|
|
|
- name: Set up code signing
|
|
|
|
|
script: |
|
2026-07-17 13:39:01 +05:30
|
|
|
# Create the distribution certificate + provisioning profile from the ASC API key and add the
|
|
|
|
|
# cert to the keychain. NOTE: `xcode-project use-profiles` is intentionally NOT here — it must
|
|
|
|
|
# run AFTER `pod install` generates the workspace, otherwise it fails to wire the profile into
|
|
|
|
|
# the App target and the archive dies with "App requires a provisioning profile".
|
2026-07-17 15:47:28 +05:30
|
|
|
#
|
|
|
|
|
# --certificate-key is REQUIRED for reusable signing: without it, --create makes a throwaway
|
|
|
|
|
# distribution cert whose private key dies with the build machine, so the next build finds a
|
|
|
|
|
# cert it has no key for ("Cannot save Signing Certificates without certificate private key").
|
|
|
|
|
# By passing our own fixed private key (CERTIFICATE_PRIVATE_KEY, a secure var in the
|
|
|
|
|
# `ios_signing` group), the cert is created once from that key and reused by every build.
|
2026-07-23 22:58:08 +05:30
|
|
|
#
|
|
|
|
|
# TWO bundle ids now need signing: the app AND the share extension (<app>.share). Each gets its
|
|
|
|
|
# own App Store profile. The App Group capability (group.com.bizgaze.connect) must be enabled on
|
|
|
|
|
# BOTH App IDs in the Apple Developer portal — see mobile/IOS_SETUP.md. fetch-signing-files
|
|
|
|
|
# registers a missing bundle id and creates its profile, but does NOT toggle the App Group
|
|
|
|
|
# capability, so that stays a one-time manual step.
|
2026-07-15 16:45:10 +05:30
|
|
|
keychain initialize
|
|
|
|
|
app-store-connect fetch-signing-files "$BUNDLE_ID" \
|
2026-07-17 15:47:28 +05:30
|
|
|
--type IOS_APP_STORE \
|
|
|
|
|
--certificate-key="@env:CERTIFICATE_PRIVATE_KEY" \
|
|
|
|
|
--create
|
2026-07-23 22:58:08 +05:30
|
|
|
app-store-connect fetch-signing-files "${BUNDLE_ID}.share" \
|
|
|
|
|
--type IOS_APP_STORE \
|
|
|
|
|
--certificate-key="@env:CERTIFICATE_PRIVATE_KEY" \
|
|
|
|
|
--create
|
2026-07-15 16:45:10 +05:30
|
|
|
keychain add-certificates
|
|
|
|
|
|
2026-08-03 15:52:46 +05:30
|
|
|
# (No "Install CocoaPods" step under SPM — there is no Podfile. Xcode resolves the Swift packages
|
|
|
|
|
# (Capacitor, plugins, LiveKit + its WebRTC/UniFFI/SwiftProtobuf) during the archive below.)
|
2026-07-15 16:45:10 +05:30
|
|
|
|
|
|
|
|
- name: Build the signed IPA
|
|
|
|
|
script: |
|
2026-07-17 13:39:01 +05:30
|
|
|
# Apply the fetched provisioning profile(s) to the Xcode project NOW that the workspace exists,
|
|
|
|
|
# then archive. use-profiles scans for **/*.xcodeproj under the repo root and sets manual
|
|
|
|
|
# signing (team + profile specifier) on the matching App target.
|
|
|
|
|
xcode-project use-profiles
|
2026-07-24 15:40:01 +05:30
|
|
|
# `xcode-project build-ipa` prints a PRETTIFIED summary and swallows the raw xcodebuild "error:"
|
|
|
|
|
# lines — a failed archive shows only "Failed to archive" with no reason. On failure, surface the
|
|
|
|
|
# actual errors from the raw log so we don't have to dig through the artifact.
|
2026-08-03 15:52:46 +05:30
|
|
|
if ! xcode-project build-ipa --project "$XCODE_PROJECT" --scheme "$XCODE_SCHEME"; then
|
2026-07-24 15:40:01 +05:30
|
|
|
echo "======================= xcodebuild errors ======================="
|
|
|
|
|
grep -h -E "error:|errSec|Provisioning profile|entitlement|Code ?Sign|does not (support|contain)|requires a provisioning|No profile|No signing|doesn't (include|match)|Command .* failed" /tmp/xcodebuild_logs/*.log 2>/dev/null | grep -vi "warning:" | tail -60 || echo "(no matching lines — open the xcodebuild_logs artifact)"
|
|
|
|
|
echo "================================================================="
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
2026-07-15 16:45:10 +05:30
|
|
|
artifacts:
|
|
|
|
|
- build/ios/ipa/*.ipa
|
|
|
|
|
- /tmp/xcodebuild_logs/*.log
|
|
|
|
|
publishing:
|
|
|
|
|
app_store_connect:
|
|
|
|
|
auth: integration
|
2026-07-17 16:02:28 +05:30
|
|
|
# Upload the build to App Store Connect. It is immediately usable for INTERNAL TestFlight testing
|
|
|
|
|
# (no Apple review). We keep external-beta submission OFF for now: submit_to_testflight=true would
|
|
|
|
|
# push the build to EXTERNAL beta review, which requires the Test Information (feedback email +
|
|
|
|
|
# beta review contact + a demo login, since our app needs sign-in) to be filled in first, and
|
|
|
|
|
# fails the build until then. Flip to true (and add `beta_groups:` + fill Test Information at
|
|
|
|
|
# App Store Connect → TestFlight → Test Information) when you want outside testers.
|
|
|
|
|
submit_to_testflight: false
|
2026-07-15 16:45:10 +05:30
|
|
|
# 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
|
2026-07-16 18:09:26 +05:30
|
|
|
# No email recipients here on purpose — build status is watched on the Codemagic dashboard. Add
|
|
|
|
|
# per-user notifications in the Codemagic UI (or a `publishing.email` block) later if you want them.
|