diff --git a/docs/ci-cd-setup.md b/docs/ci-cd-setup.md index bde8dac..0c79b10 100644 --- a/docs/ci-cd-setup.md +++ b/docs/ci-cd-setup.md @@ -7,14 +7,12 @@ This guide covers setting up a **Gitea Actions** macOS runner to automatically b ### Hardware - **Mac** running macOS 15 (Sequoia) or later - **Xcode 26+** installed (required for iOS 26.0 / watchOS 11.0 targets) -- **iPhone** (iOS 26+) connected via USB, with Developer Mode enabled +- **iPhone** (iOS 26+) with the Firebase App Tester app installed ### Software - [Gitea Actions Runner](https://docs.gitea.com/usage/actions/act-runner) installed and registered - `brew` (Homebrew) installed - `xcodegen` — `brew install xcodegen` (also installed automatically by the workflow) -- `ios-deploy` — `brew install ios-deploy` (also installed automatically by the workflow) -- `libimobiledevice` + `ideviceinstaller` (installed automatically by the workflow) ## 1. Register the macOS Runner @@ -32,35 +30,15 @@ This guide covers setting up a **Gitea Actions** macOS runner to automatically b > **Important:** The runner must have the label `macos` because the workflow uses `runs-on: macos`. -## 2. iPhone Preparation - -1. Connect the iPhone to the Mac via USB -2. Trust the computer on the iPhone when prompted -3. Enable **Developer Mode** on the iPhone: - `Settings → Privacy & Security → Developer Mode` -4. Get the device UDID: - ```bash - # Method 1: Via libimobiledevice (preferred) - idevice_id -l - - # Method 2: Via system_profiler (no extra tools needed) - system_profiler SPUSBDataType | grep -A 20 "iPhone" | grep "Serial Number" - # The UDID is the 40-character hex string (not the ECID) - - # Method 3: Via Xcode - # Window → Devices and Simulators - ``` - -> **Important:** Bonjour/mDNS discovery does **not** work over VPN connections. Always use the USB-based UDID connection (`--id` flag with `ios-deploy`) rather than relying on network Bonjour discovery. The workflow uses `--id ${IPHONE_UDID}` which works over both direct USB and USB-over-network setups. - -## 3. Configure Secrets +## 2. Configure Secrets Add these secrets in the Gitea repository: `Settings → Actions → Secrets` | Secret Name | Description | How to Get | |-------------|-------------|------------| -| `IPHONE_UDID` | 40-character UDID of the target iPhone | `idevice_id -l` on the connected Mac | +| `FIREBASE_APP_ID` | Firebase App ID (iOS app) | Firebase Console → Project Settings → General → App ID | +| `FIREBASE_SERVICE_ACCOUNT` | Base64-encoded Firebase service account JSON key | See [Firebase Setup](#6-firebase-app-distribution-setup) below | | `GITEA_TOKEN` | Gitea API token with repo + pull request scope | `Gitea → Settings → Applications → Generate Token` | ### Gitea Token Permissions @@ -69,7 +47,7 @@ The token needs: - `write:issue` — to post LGTM comments - `write:pull_request` — to merge approved PRs -## 4. Xcode Signing +## 3. Xcode Signing The workflow uses **automatic code signing**. Ensure: @@ -86,7 +64,7 @@ The workflow uses **automatic code signing**. Ensure: Xcode → Settings → Accounts → Add Apple ID ``` -## 5. Workflow Overview +## 4. Workflow Overview The workflow at `.gitea/workflows/pr-iphone-deploy.yml` triggers on PRs to `main`: @@ -97,18 +75,16 @@ PR Opened / Updated ↓ Resolve SPM packages ↓ - Build for iPhone (Release) + Build + Archive IPA ↓ - Export IPA + Install on device + Upload to Firebase App Distribution ↓ - Run unit tests + Post "Ready to test" comment on PR ↓ - Post LGTM comment on PR - ↓ - Auto-merge (if no failures) + Wait for LGTM → Auto-merge ``` -## 6. Manual Test Run +## 5. Manual Test Run To test the workflow locally on the runner Mac: @@ -119,20 +95,104 @@ xcodegen generate xcodebuild build \ -project TabataGo.xcodeproj \ -scheme TabataGo \ - -destination "id=" \ + -sdk iphoneos \ -configuration Release \ + CODE_SIGN_STYLE=Automatic \ -allowProvisioningUpdates ``` +## 6. Firebase App Distribution Setup + +### 6.1 Create a Firebase Project + +1. Go to [Firebase Console](https://console.firebase.google.com) +2. Click **Add project** (or select an existing one) +3. Follow the wizard to create the project (Google Analytics is optional) + +### 6.2 Enable App Distribution + +1. In the Firebase Console, open your project +2. Navigate to **App Distribution** in the left sidebar +3. Click **Get started** to enable the service + +### 6.3 Get the App ID + +1. Go to **Project Settings** (gear icon) → **General** +2. Scroll down to **Your apps** → find the iOS app +3. Copy the **App ID** (looks like `1:123456789012:ios:abc123def456`) + +### 6.4 Create a Service Account + +1. Go to **Project Settings** → **Service accounts** +2. Click **Create service account** (or use Firebase Admin SDK → Generate new private key) +3. Give it a name like `github-actions-deploy` +4. Assign the role: **Firebase App Distribution Admin** +5. Click **Generate new private key** → download the JSON file + +### 6.5 Encode the Service Account Key + +```bash +# Convert the JSON key to a base64 string (single line, no wrapping) +base64 -i firebase-key.json | tr -d '\n' +``` + +Copy the output — this is your `FIREBASE_SERVICE_ACCOUNT` secret value. + +### 6.6 Add Testers and Groups + +1. In Firebase Console, go to **App Distribution** → **Testers & Groups** +2. Click **Add group** → name it `millian-test` +3. Click **Add testers** → enter Millian's email +4. Add the tester to the `millian-test` group + +### 6.7 Install the Firebase App Tester on the iPhone + +1. Send an invite to Millian's iPhone from the Firebase Console +2. Millian accepts the invite and installs the **Firebase App Tester** app from the App Store +3. Open the app and sign in with the invited Google account +4. The app will now receive builds pushed from the CI workflow + ## Troubleshooting | Issue | Solution | |-------|----------| -| "Device not found" | Check USB connection, trust prompt, UDID in secrets | | "Code signing failed" | Sign into Xcode on the runner Mac, verify team membership | | "No provisioning profile" | Run `-allowProvisioningUpdates` or open project in Xcode once | | `xcodegen: command not found` | `brew install xcodegen` (workflow handles this) | -| `ideviceinstaller` fails | Ensure iPhone is unlocked and trusted; try Xcode Organizer manually | -| `ios-deploy` fails to find device | Use `--id` with explicit UDID. Bonjour discovery fails over VPN — always use UDID-based connection | | Runner doesn't pick up job | Verify runner label is `macos` and runner is connected | | Token permission errors | Ensure `GITEA_TOKEN` has `write:issue` and `write:pull_request` scopes | +| Firebase upload fails | Verify `FIREBASE_APP_ID` and `FIREBASE_SERVICE_ACCOUNT` secrets are set correctly | +| "App not found" in Firebase | Check that the iOS app is registered in Firebase with the correct bundle ID | +| Testers don't receive build | Verify tester is in the `millian-test` group and has accepted the invite | + +## Compilation Error Troubleshooting + +### xcodegen Clearing Signing Team + +After running `xcodegen generate`, the `.xcodeproj` may have a cleared signing team. Always re-check your team settings: + +```bash +cd tabatago-swift +xcodegen generate +# Then check that project.yml has the correct team ID in settings +``` + +### Complication Targets Missing Team ID + +Targets like **TabataGoWatchWidget** (watchOS complications) require an explicit team ID in `project.yml` settings. If you see signing errors for the widget target: + +1. Open `tabatago-swift/project.yml` +2. Find the `TabataGoWatchWidget` target section +3. Ensure it has an explicit `settings:` block with the team ID: + ```yaml + target: TabataGoWatchWidget + settings: + base: + DEVELOPMENT_TEAM: "2MJF39L8VY" + ``` + +### General Build Debugging + +- Run `xcodegen generate` before every build to ensure the project is in sync +- Check `xcodebuild` output for specific file/line errors +- Verify SPM packages resolve: `xcodebuild -resolvePackageDependencies`