Skip to content

ci: build and test on a self-hosted macOS runner - #2

Merged
Skeyelab merged 2 commits into
mainfrom
ci/self-hosted-runner
Jul 14, 2026
Merged

Skeyelab merged 2 commits into
mainfrom
ci/self-hosted-runner

Conversation

@Skeyelab

Copy link
Copy Markdown
Member

There was no CI at all — zero workflows, zero runners.

Why self-hosted rather than GitHub-hosted

GitHub bills macOS runners at a 10× minute multiplier on private repos (Linux 1×, Windows 2×). Our pipeline is xcodegen + xcodebuild + boot a simulator, roughly 5–8 minutes wall clock — so 50–80 billed minutes per run. The free allowance (2,000 min/month → 200 macOS minutes) covers about three runs a month. Untenable for a repo under active development.

Worth knowing where the cost actually sits: the 86 tests run in 0.7 seconds. Everything else is compiling and booting a simulator — both of which a warm, persistent machine keeps around. Incremental runs on the self-hosted box should land well under a minute, versus 5–8 cold.

Safety

Self-hosted is safe here specifically because this repo is private. Do NOT copy this workflow to a public repo — a fork's pull_request can execute arbitrary code on the runner. That caveat applies to link-devices, which is public.

Two things that would otherwise bite

  • The runner service does not inherit a login shell's PATH, so /opt/homebrew/bin is added explicitly via $GITHUB_PATH. Without it, xcodegen isn't found.
  • The .xcodeproj is gitignored, so it's regenerated from project.yml on every run — which also regenerates Info.plist.

Gated on Sources/**, Tests/**, project.yml, and the workflow itself, with cancel-in-progress so two quick pushes don't queue two builds on one machine. Failed runs upload the .xcresult bundle.

Runner registered and online as LIB-2833 (self-hosted,macOS,ARM64), installed as a launchd service so it survives reboots.

https://claude.ai/code/session_01F7YsQgso3KNpeXbP5vNVj6

No CI existed. GitHub bills macOS runners at a 10x minute multiplier on private
repos, so one 5-8 minute build+test burns 50-80 billed minutes — the free
allowance (2,000 min/month = 200 macOS minutes) covers roughly three runs a
month. Untenable for a repo under active development.

Self-hosted is safe HERE because this repo is private. Do not copy this workflow
to a public repo: a fork's pull_request can execute arbitrary code on the runner.
That caveat applies to link-devices.

The suite itself takes 0.7 seconds; all the cost is compiling and booting a
simulator, both of which a warm persistent machine keeps around.

Notes: the runner service does not inherit a login shell's PATH, so /opt/homebrew/bin
is added explicitly or xcodegen is not found. The .xcodeproj is gitignored and
regenerated from project.yml on every run.
…om the code

gstack: /browse for all web browsing, never mcp__claude-in-chrome__* directly.

The KitchenSync rules worth encoding are the ones a new agent (or human) will get
WRONG by reading the code alone:

- Live vs reboot IS the KsLiveEdit case list, and it is not guessable from field
  names: metro_accent is live, metronome ENABLE is not; led enable IS live,
  follow_beat is not. Names the three structural defenses and says not to weaken
  them.
- Transport is quantized. Never predict armed->running client-side.
- XcodeGen REGENERATES Info.plist from project.yml, so hand-edits to it vanish.
  And NSLocalNetworkUsageDescription / NSBonjourServices are load-bearing: without
  them iOS silently kills Bonjour and .local resolution with no useful error.
- TDD is the rule; network tests run the real client against a stubbed wire.
- The known gap: nothing has ever spoken to real hardware.
@Skeyelab
Skeyelab merged commit ab14597 into main Jul 14, 2026
1 check passed
@Skeyelab
Skeyelab deleted the ci/self-hosted-runner branch July 14, 2026 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants