CI / GitHub Actions
Workflow: .github/workflows/build.yml
Job
One job, build-and-test, on the self-hosted macOS runner. It runs on every push to main, on pull requests targeting main, and on a manual run (workflow_dispatch). Steps run in order and the job stops at the first failure:
| Step | What it does |
|---|---|
| Build & test Shared package | cd Shared && swift build && swift test |
| Build iOS app | xcodebuild build, scheme Minuta, iPhone 17 simulator, Debug |
| Run unit tests | xcodebuild test -only-testing:MinutaTests on the simulator |
| Run UI tests | xcodebuild test -only-testing:MinutaUITests on the simulator |
| Build Mac Catalyst app | xcodebuild build, scheme Minuta, platform=macOS,variant=Mac Catalyst, Debug |
| Run unit tests (Mac Catalyst) | xcodebuild test -only-testing:MinutaTests on Catalyst |
| Run UI tests (Mac Catalyst) | xcodebuild test -only-testing:MinutaUITests on Catalyst; manual runs only |
Signing
The simulator steps turn code signing off (CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO): the simulator runs unsigned binaries.
The Catalyst steps cannot do that and pass two other settings instead:
CODE_SIGN_IDENTITY=-signs ad hoc. A Mac Catalyst test runner has to be signed to launch at all, and ad-hoc signing needs no certificate or provisioning profile on the runner.ENABLE_HARDENED_RUNTIME=NO. With the hardened runtime on, library validation refuses to load an ad-hoc signed test bundle into the host process, so the tests would never start.
The Minuta scheme builds the app and the two test bundles only. The MinutaWidgets extension is not listed in the scheme: on iOS it is built through the app’s platformFilter: iOS dependency, and a Catalyst build never builds it. When the scheme listed it, every Catalyst build of the scheme failed on the widget’s missing development profile.
Mac Catalyst UI tests run by hand
The Catalyst UI tests drive real windows in the runner’s interactive session, and the runner is the development Mac: on every push they took the screen over for about 20 minutes and failed in bulk (“unable to find hit point”) whenever someone was using the machine. The step has if: github.event_name == 'workflow_dispatch', so a push or a pull request skips it and still builds the Catalyst app and runs MinutaTests on it. Start a full run when the machine is free:
gh workflow run build.yml --ref main Locally the same suites run through ./scripts/run-uitests.sh (iPhone simulator) and ./scripts/run-uitests.sh --mac (Mac Catalyst), which also export the screenshots.
Self-hosted runner
Runs on the development Mac as a launchd service. Setup lives in the hogen monorepo: ../hogen/gh-mac-runner.
Runner labels: self-hosted, macOS, X64
Why self-hosted
GitHub-hosted macOS runners cost $0.08/min with a 10x multiplier on the free tier (2000 min/month = effectively 200 macOS minutes). A single build uses ~50 billed minutes, so the free tier covers ~4 builds/month. Self-hosted is free with no limits.
Management
~/actions-runner/svc.sh status # check
~/actions-runner/svc.sh stop # stop
~/actions-runner/svc.sh start # start Workflow file
runs-on: [self-hosted, macOS, X64] No xcode-select step needed — uses whatever Xcode is installed on the runner Mac.