# TabataGo CI/CD Setup Guide > How to set up the Mac self-hosted runner and configure secrets for the > **PR → iPhone → LGTM** workflow. ## Overview The `pr-iphone-deploy.yml` workflow (`.gitea/workflows/`) automates the test-and-merge cycle for every PR targeting `main`: 1. **Build** — Checks out the PR, generates the Xcode project via XcodeGen, and builds the TabataGo app for Millian's iPhone (device build). 2. **Comment** — Posts a "Ready to test" comment on the PR. 3. **Wait for LGTM** — Polls PR comments for up to **2 hours** waiting for Millian to reply `LGTM` (merge) or `KO` (block). 4. **Auto-merge** — Merges the PR automatically when `LGTM` is received. ``` ┌────────────┐ ┌──────────────┐ ┌──────────────┐ │ PR opened │ ──► │ Build iPhone │ ──► │ Wait LGTM/KO │ │ / updated │ │ (macOS CI) │ │ (poll 2h) │ └────────────┘ └──────────────┘ └──┬────────┬──┘ │ │ LGTM │ │ KO ▼ ▼ ┌────────┐ ┌────────┐ │ Merge │ │ Block │ │ PR │ │ PR │ └────────┘ └────────┘ ``` --- ## 1. Set Up the Mac Runner You need a **macOS machine** (macOS 15+, Xcode 26+) registered as a self-hosted Gitea Actions runner. ### 1.1 Prerequisites on the Mac ```bash # Xcode 26+ (from App Store or Apple Developer) sudo xcode-select -s /Applications/Xcode.app # XcodeGen (project generator) brew install xcodegen # Python 3 (pre-installed on macOS, used by poll script) python3 --version # should be 3.x # Xcode must be signed into an Apple Developer account for automatic signing # Open Xcode → Settings → Accounts → Add your Apple ID ``` ### 1.2 Register the Runner in Gitea 1. Go to your Gitea repo: **Settings → Actions → Runners** 2. Click **Create new runner** 3. Follow the instructions to download and configure the runner binary. Use the label **`macos`** (this must match `runs-on: macos` in the workflow): ```bash # On the Mac, after downloading the runner: ./act_runner register \ --instance https://gitea.1000co.fr \ --token \ --name mac-runner \ --labels macos ``` 4. Start the runner (as a background service): ```bash ./act_runner daemon ``` For persistence across reboots, create a LaunchAgent: ```xml Label com.tabatago.runner ProgramArguments /path/to/act_runner daemon WorkingDirectory /path/to/runner RunAtLoad KeepAlive ``` Then load it: ```bash launchctl load ~/Library/LaunchAgents/com.tabatago.runner.plist ``` ### 1.3 Verify the Runner Check that the runner appears as **Idle** in: **Gitea → Settings → Actions → Runners** --- ## 2. Configure Required Secrets Go to **Gitea → Settings → Actions → Secrets** and add: ### 2.1 `IPHONE_UDID` The UDID (Unique Device Identifier) of Millian's iPhone. This tells Xcode which physical device to build for. ```bash # Find the UDID on the Mac when the iPhone is connected via USB: xcrun xctrace list devices # Look for the iPhone line, e.g.: # Millian's iPhone (26.0) (00008110-XXXXXXXXXXXX) # Or in Finder: select the iPhone in the sidebar → click the serial number # line until it switches to UDID ``` | Secret Name | Value Example | |----------------|------------------------------------| | `IPHONE_UDID` | `00008110-00123456789ABC01` | ### 2.2 `GITEA_TOKEN` A Gitea personal access token with **read/write** permissions on the repo. Create it at: **Gitea → Settings → Applications → Generate Token** Required permissions: - `read:repository` — read PR info, comments - `write:issue` — post comments on PRs - `write:pull_request` — merge PRs | Secret Name | Value Example | |-----------------|----------------------------------------| | `GITEA_TOKEN` | `e20fd3de2f79b324afde5049e7bafd60a...` | --- ## 3. Code Signing Setup The workflow uses **automatic code signing** (`CODE_SIGN_STYLE=Automatic` with `-allowProvisioningUpdates`). This requires: 1. **Apple ID signed into Xcode** on the Mac runner 2. **The iPhone UDID registered** in the Apple Developer account 3. **A valid provisioning profile** for `com.tabatago` (Debug) To set up: ```bash # On the Mac runner, open Xcode and sign in: # Xcode → Settings → Accounts → + → Apple ID # Verify the team is available: cd tabatago-swift xcodegen generate xcodebuild -showBuildSettings -scheme TabataGo -sdk iphoneos | grep DEVELOPMENT_TEAM ``` If automatic signing doesn't work (first-time setup), you may need to open the project in Xcode once manually to let it create the provisioning profile. --- ## 4. How the Workflow Works ### Trigger The workflow runs on: - `pull_request` events on `main` - Types: `opened`, `synchronize` (new commits pushed), `reopened` ### Build Job (`build-deploy`) | Step | What it does | |------|-------------| | Checkout | Clones the PR branch | | Setup Xcode | Selects Xcode.app, prints version | | Install XcodeGen | `brew install xcodegen` if not present | | Generate project | `xcodegen generate` in `tabatago-swift/` | | Build for iPhone | `xcodebuild build` for device UDID from secret | | Post comment | Posts a "Ready to test" message on the PR | The build runs with `set -o pipefail` so that any compilation error causes the job to fail. The last 40 lines of build output are shown; the full log is saved to `build/xcodebuild.log`. ### Approval Job (`wait-approval`) - Polls the Gitea API every **30 seconds** for new PR comments - **Timeout**: 120 minutes (240 iterations × 30s) - **LGTM detection**: Case-insensitive match for `lgtm` anywhere in the comment body - **KO detection**: Exact case-insensitive match for a standalone `ko` comment - On `LGTM` → merges the PR via Gitea API - On `KO` → fails the workflow (exit code 1) - On timeout → fails the workflow (exit code 1) ### Comment Format Reply on the PR with just: - **`LGTM`** — approve and auto-merge - **`KO`** — block the PR --- ## 5. Troubleshooting ### Runner doesn't pick up jobs - Check the runner label: must be `macos` exactly - Check **Settings → Actions → Runners** — runner must be **Idle**, not **Offline** - Run `./act_runner daemon` in a terminal to see logs ### Build fails with signing error ``` error: No profiles for 'com.tabatago' were found ``` - Open Xcode on the Mac runner, sign into your Apple ID - Build once in Xcode to generate the provisioning profile - Verify the iPhone UDID is correct ### Build fails with "device not found" ``` error: Unable to find a device matching the provided destination specifier ``` - Check that `IPHONE_UDID` secret matches the actual device UDID - The device must be registered in your Apple Developer account ### Comment not posted / LGTM not detected - Verify `GITEA_TOKEN` has the required permissions (read/write issues + PRs) - Check the Gitea instance URL: `https://gitea.1000co.fr` - Ensure the token hasn't expired ### Workflow runs on every PR push, canceling previous runs This is intentional — the `concurrency` setting cancels in-progress runs for the same PR when new commits are pushed. This prevents queue buildup. --- ## 6. File Locations ``` tabatago/ ├── .gitea/ │ └── workflows/ │ └── pr-iphone-deploy.yml ← iPhone build + LGTM workflow ├── .github/ │ └── workflows/ │ └── ci.yml ← Admin web + YouTube worker CI └── docs/ └── ci-cd-setup.md ← This document ``` --- ## 7. Required Tools on the Mac Runner | Tool | Version | Install | |-----------|----------|----------------------------------| | macOS | 15+ | — | | Xcode | 26.0+ | App Store or Apple Developer | | XcodeGen | latest | `brew install xcodegen` | | Python 3 | 3.x | Pre-installed on macOS | | curl | any | Pre-installed on macOS | | git | any | Pre-installed on macOS | --- ## See Also - [Gitea Actions Documentation](https://docs.gitea.com/usage/actions/overview) - [Gitea API — Merge a Pull Request](https://docs.gitea.com/api/1.22/#tag/pull-request/operation/repoMergePullRequest) - [Xcode Build Settings Reference](https://developer.apple.com/documentation/xcode/build-settings-reference)