- Add set -o pipefail for proper build error detection - Replace PIPESTATUS with $? (works correctly with pipefail) - Fix Python indentation in inline scripts (avoids YAML linter false positives) - Remove MergeTitle from Gitea merge API call (uses default) - Add docs/ci-cd-setup.md with Mac runner and secrets setup guide
9.3 KiB
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:
- Build — Checks out the PR, generates the Xcode project via XcodeGen, and builds the TabataGo app for Millian's iPhone (device build).
- Comment — Posts a "Ready to test" comment on the PR.
- Wait for LGTM — Polls PR comments for up to 2 hours waiting for
Millian to reply
LGTM(merge) orKO(block). - Auto-merge — Merges the PR automatically when
LGTMis 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
# 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
-
Go to your Gitea repo: Settings → Actions → Runners
-
Click Create new runner
-
Follow the instructions to download and configure the runner binary. Use the label
macos(this must matchruns-on: macosin the workflow):# On the Mac, after downloading the runner: ./act_runner register \ --instance https://gitea.1000co.fr \ --token <RUNNER_TOKEN_FROM_GITEA> \ --name mac-runner \ --labels macos -
Start the runner (as a background service):
./act_runner daemonFor persistence across reboots, create a LaunchAgent:
<!-- ~/Library/LaunchAgents/com.tabatago.runner.plist --> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.tabatago.runner</string> <key>ProgramArguments</key> <array> <string>/path/to/act_runner</string> <string>daemon</string> </array> <key>WorkingDirectory</key> <string>/path/to/runner</string> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> </dict> </plist>Then load it:
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.
# 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, commentswrite:issue— post comments on PRswrite: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:
- Apple ID signed into Xcode on the Mac runner
- The iPhone UDID registered in the Apple Developer account
- A valid provisioning profile for
com.tabatago(Debug)
To set up:
# 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_requestevents onmain- 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
lgtmanywhere in the comment body - KO detection: Exact case-insensitive match for a standalone
kocomment - 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-mergeKO— block the PR
5. Troubleshooting
Runner doesn't pick up jobs
- Check the runner label: must be
macosexactly - Check Settings → Actions → Runners — runner must be Idle, not Offline
- Run
./act_runner daemonin 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_UDIDsecret matches the actual device UDID - The device must be registered in your Apple Developer account
Comment not posted / LGTM not detected
- Verify
GITEA_TOKENhas 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 |