libfreenect2 0.4
Open source driver for the Kinect for Windows v2 (K4W2) sensor
Loading...
Searching...
No Matches
Self-hosted macOS CI runner setup

Most of the CI workflow (.github/workflows/ci.yml) runs on GitHub-hosted ubuntu-24.04. Exactly one job needs a real Apple GPU and therefore a self-hosted macOS runner:

  • build-test-metal builds with Metal enabled at C++17 with warnings-as-errors and hardened libc++, then runs the full test suite including the Metal-vs-CPU depth parity test on the GPU.

Every other job — python-quality, linux-builds, linux-filter-backends, linux-vaapi, sanitizers, fuzzers, coverage, format, semgrep, and static-analysis — runs on GitHub-hosted Linux and needs nothing from this machine.

This guide sets up a machine to serve that one job. No Kinect hardware is required — every test in it is hardware-free; only a Metal-capable GPU is used.

The separate, manually dispatched Kinect hardware soak workflow targets a runner that also has the kinect label and a physically connected Kinect v2. It defaults to 100 start/capture/stop/close cycles on both CPU and Metal, then starts an operator-assisted reconnect check. Watch the job log; when KinectReconnect asks, unplug the Kinect and reconnect it within 60 seconds. The check requires the old pointer to reach a terminal state and capture to resume through a newly opened device object.

Set the workflow's reconnect_cycles input to 0 only when the runner is unattended. That run does not satisfy the unplug/replug release gate and must be identified as such in the release checklist.

1. Machine requirements

  • A Mac (Apple Silicon or Intel) running a current macOS.
  • A real GPU and a logged-in graphics session — the Metal parity test creates an MTLDevice. A headless daemon with no window-server session will make that test SKIP rather than run, so prefer running the runner inside a logged-in user session (see step 4step 4

).

  • Outbound network access — the test build fetches GoogleTest via CMake FetchContent at configure time.
  • A few GB of free disk for the toolchains, Homebrew packages, and per-job build directories.

2. Install prerequisites

Xcode + the Metal toolchain

Install full Xcode (not just the Command Line Tools) — the Metal shader compiler ships only with Xcode. Then verify the compiler actually runs:

sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcrun -sdk macosx metal --version

Supported across recent macOS releases, including macOS 15 Sequoia (Xcode 16) and macOS 26 Tahoe (Xcode 26):

  • macOS 15 Sequoia / Xcode 16 — the Metal toolchain is bundled with Xcode. xcrun -sdk macosx metal --version works out of the box; nothing extra to do.
  • macOS 26 Tahoe / Xcode 26 and later — the metal launcher stub is present but cannot execute until the Metal Toolchain component is downloaded once:

    xcodebuild -downloadComponent MetalToolchain

If metal --version fails, the build still succeeds but Metal support is disabled and the parity test skips — so fix this before relying on CI.

Build dependencies (Homebrew)

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # if needed
brew install cmake pkg-config libusb glfw3 jpeg-turbo

Use the Homebrew prefix that matches the machine: /opt/homebrew on Apple Silicon, /usr/local on Intel. Do not run the shell/CMake under Rosetta on Apple Silicon (see the macOS notes in the top-level README.md).

Lint tools (optional)

The format and static-analysis jobs run on GitHub-hosted Linux and install their own tooling, so this machine does not need either. Install them only if you want to reproduce those checks locally before pushing:

brew install clang-format llvm # llvm provides clang-tidy

Note that format is advisory (continue-on-error: true) but static-analysis is a hard gate — a clang-tidy failure fails CI.

3. Register the runner with GitHub

In the repository: Settings → Actions → Runners → New self-hosted runner → macOS. GitHub shows the exact download/configure commands with a registration token. They look like:

mkdir -p ~/actions-runner && cd ~/actions-runner
curl -o actions-runner-osx.tar.gz -L <url-from-github>
tar xzf actions-runner-osx.tar.gz
./config.sh --url https://github.com/<owner>/<repo> --token <token>

During config.sh, when prompted for labels, keep the defaults and make sure the runner ends up with both self-hosted and macOS labels — the workflow targets runs-on: [self-hosted, macOS], so a runner missing either label will never be picked. (self-hosted is added automatically; macOS is a default label for the macOS runner package.)

4. Run the runner

For occasional/interactive use:

cd ~/actions-runner
./run.sh

To keep it running across reboots, install it as a launchd service:

./svc.sh install
./svc.sh start
./svc.sh status

Metal note: the service must run in a context that has access to the window server, or the Metal parity test will skip. Running ./run.sh (or the service) from inside a logged-in desktop session is the simplest way to guarantee GPU access. If you rely on svc.sh, log in as the runner's user and confirm the parity test runs (not skips) on the first CI run — see the verification below.

5. Verify the setup

Either push a branch and watch the CI jobs, or reproduce them locally on the runner:

# build-test-metal job — the one this machine actually serves
cmake -B build -DBUILD_TESTING=ON -DENABLE_METAL=ON -DBUILD_EXAMPLES=ON
cmake --build build -j
ctest --test-dir build --output-on-failure
# Confirm the Metal parity test actually RAN (not skipped):
build/bin/unit_tests --gtest_filter='MetalCpuParity.*'
# Expect: "using device <your GPU>" and [ PASSED ], not [ SKIPPED ].
# Optional: the sanitizers job runs on GitHub-hosted Linux, but reproduces here
cmake -B build-asan -DBUILD_TESTING=ON -DENABLE_SANITIZERS=ON -DENABLE_METAL=OFF -DBUILD_EXAMPLES=OFF
cmake --build build-asan -j
ctest --test-dir build-asan -LE gpu --output-on-failure

A healthy runner produces a green build-test-metal with the parity cases running on the GPU.

6. Maintenance & security notes

  • Trust boundary. A self-hosted runner executes any workflow triggered against the repo. Keep it dedicated to this repository and restrict who can open PRs that run CI; treat the machine as compromisable by workflow code.
  • Clean checkouts. The workflow checks out with clean: true and each job uses its own build directory (build, build-asan, build-tidy) to avoid cross-contamination on the persistent machine. Occasionally prune the ~/actions-runner/_work tree if disk fills up.
  • Toolchain drift. After a macOS or Xcode update, re-run xcrun -sdk macosx metal --version (and, on Xcode 26+, re-download the Metal Toolchain) so the parity test keeps running instead of silently skipping.
  • Updating the runner. The GitHub runner agent self-updates; if it falls too far behind, re-run config.sh remove --token <token> and re-register.