Skip to content

ci(minvmd): run macOS tests on the self-hosted Apple Silicon runner - #283

Merged
norrietaylor merged 8 commits into
mainfrom
ci/macos-tests
Jun 3, 2026
Merged

ci(minvmd): run macOS tests on the self-hosted Apple Silicon runner#283
norrietaylor merged 8 commits into
mainfrom
ci/macos-tests

Conversation

@norrietaylor

@norrietaylor norrietaylor commented Jun 2, 2026

Copy link
Copy Markdown
Member

Adds a build-macos job to .github/workflows/ci.yml, running on the org self-hosted Apple Silicon runner (jks-Mac-mini — labels self-hosted, macOS, ARM64).

Why

minvmd links libkrun (#[link(name = "krun")]) and only compiles on macOS; the Linux CI builds it as a runtime stub. Its krun FFI module, safe wrappers, and smoke test have had zero CI coverage until now.

What it runs

  • Ensures libkrun is installed (brew install libkrun if missing).
  • cargo clippy -p minvmd --all-targets -- -D warnings (mac-only code path).
  • cargo test -p minvmd (unit tests; the boot smoke stays #[ignore]).
  • MINVMD_E2E=1 cargo test -p minvmd --test krun_smoke -- --include-ignored — exercises create_ctx → set_vm_config → set_exec against real libkrun. Skips krun_start_enter (no kernel/rootfs), so no codesigned hypervisor entitlement is required.

Scope / notes

  • Scoped to minvmd (the only macOS-gated crate). Widen to --workspace once the rest of the tree is confirmed mac-buildable.
  • The runner is self-hosted and currently offline — this job will queue until it's online. It inherits the workflow's paths-ignore (docs/markdown changes don't trigger it).
  • Real hardware, so the hypervisor path is runnable later; GitHub-hosted macOS VMs can't.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Added a macOS CI workflow triggered on push/PR to main and via manual dispatch.
    • Runs on macOS ARM64 runners: sets up Rust, runs linting (warnings as errors), full tests and an FFI smoke test.
    • Includes configurable execution control, 30-minute timeout, concurrency/cancel behavior, and a hard fail if a required runner dependency is missing.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5b0db1c-7e81-4902-a4ce-0e9e63456964

📥 Commits

Reviewing files that changed from the base of the PR and between 1a56282 and 7477c4d.

📒 Files selected for processing (1)
  • .github/workflows/ci-macos.yml
💤 Files with no reviewable changes (1)
  • .github/workflows/ci-macos.yml

📝 Walkthrough

Walkthrough

Adds .github/workflows/ci-macos.yml: a macOS CI workflow that triggers on scoped pushes/PRs and workflow_dispatch, enqueues a guarded build-macos job on a self-hosted macOS ARM64 runner, installs Rust+clippy, verifies /opt/homebrew/lib/libkrun.dylib, runs clippy and unit tests for minvmd, and runs the MINVMD_E2E krun_smoke test.

Changes

macOS CI workflow and FFI smoke testing

Layer / File(s) Summary
Workflow triggers and job declaration
.github/workflows/ci-macos.yml
Defines PATH-scoped triggers (push/PR to main, workflow_dispatch), sets CARGO_TERM_COLOR, concurrency key ci-macos-${{ github.ref }} with cancel-in-progress for non-main refs, minimal contents: read permissions, and declares build-macos job guarded by vars.RUN_MACOS_CI on a self-hosted macOS ARM64 runner with 30m timeout.
Toolchain install, libkrun check, clippy and tests
.github/workflows/ci-macos.yml
Checkout with persist-credentials: false, install stable Rust + clippy, fail if /opt/homebrew/lib/libkrun.dylib missing, run cargo clippy -p minvmd --all-targets -- -D warnings, cargo test -p minvmd, and MINVMD_E2E=1 cargo test -p minvmd --test krun_smoke -- --include-ignored.

Sequence Diagram(s)

sequenceDiagram
  participant GitHub as GitHub (push/PR/workflow_dispatch)
  participant Workflow as ci-macos workflow
  participant RunnerQueue as Runner queue
  participant Runner as Self-hosted macOS ARM64 Runner
  participant Rustup as rustup (stable + clippy)
  participant FS as Filesystem (/opt/homebrew/lib/libkrun.dylib)
  participant Cargo as Cargo (clippy/tests)

  GitHub->>Workflow: trigger ci-macos on matching paths/refs
  Workflow->>Workflow: set env, concurrency, permissions
  Workflow->>RunnerQueue: enqueue build-macos job (if RUN_MACOS_CI != 'false')
  RunnerQueue->>Runner: start job on self-hosted macOS ARM64
  Runner->>Rustup: install stable toolchain + clippy
  Runner->>FS: verify /opt/homebrew/lib/libkrun.dylib
  Runner->>Cargo: run cargo clippy -p minvmd --all-targets -- -D warnings
  Runner->>Cargo: run cargo test -p minvmd
  Runner->>Cargo: run MINVMD_E2E=1 cargo test -p minvmd --test krun_smoke -- --include-ignored
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Possibly related PRs

  • gominimal/minimal#237: Introduced the krun_smoke end-to-end smoke test and MINVMD_E2E gating used by this workflow.

Suggested reviewers

  • evanspearman

Poem

🐰 I hop on macOS wood and pine,
I fetch Rust, clippy, and libkrun fine,
I run the tests with gentle cheer,
krun_smoke hums, the logs appear,
small paws tidy up—CI's clear!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a macOS CI workflow for the minvmd crate on a self-hosted Apple Silicon runner.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 76-84: The build-macos job currently runs unconditionally and can
hang waiting for an offline self-hosted runner; add a gate to skip it when the
runner isn't available by adding an if: condition to the build-macos job
(referencing the job name build-macos and its runs-on/timeout-minutes settings)
that checks a repository or workflow input variable (e.g., a repo secret or
github.event.inputs flag like run_macos) so the job only runs when that flag
indicates the mac runner is online or when manually triggered; alternatively
move build-macos behind a workflow_dispatch/manual trigger and document the
required flag to enable the job.
- Around line 83-86: The build-macos job is inheriting workflow-wide write
permissions; add a job-level permissions override to restrict the token to
read-only for checkout-sensitive actions: under the job named build-macos (the
job that runs on [self-hosted, macOS, ARM64] and uses actions/checkout@v6) add a
permissions block with contents: read (and no broader scopes) so the self-hosted
runner does not receive a write token.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 444ce649-6f5b-4f25-b0c6-cac437951881

📥 Commits

Reviewing files that changed from the base of the PR and between 48eda35 and 36669db.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
norrietaylor added a commit that referenced this pull request Jun 2, 2026
Address CodeRabbit on PR #283:
- Gate build-macos behind the RUN_MACOS_CI repo variable (defaults to
  enabled). timeout-minutes counts time queued for a self-hosted runner, so
  set RUN_MACOS_CI=false to skip the job while the runner is offline rather
  than letting it stall and time out.
- Add job-level `permissions: contents: read` (workflow level is
  contents: write) and `persist-credentials: false` on checkout, since the
  job only builds and runs on a persistent self-hosted runner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci-macos.yml:
- Around line 10-21: The ci-macos workflow is referencing a non-existent crate
and tests (paths: "crates/minvmd/**" and cargo invocation "cargo ... -p minvmd
--test krun_smoke"), so update .github/workflows/ci-macos.yml to point at the
real crate and test targets used in this repo (replace the paths filter entries
and the cargo package/test names such as "-p minvmd", "--test krun_smoke", and
any mention of "krun_start_enter" with the actual crate directory/package name
and test target present in this repo), or alternatively ensure the missing
minvmd package and tests are added/submoduled/generated so the existing filters
and cargo flags are valid.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8d2d9659-6195-46af-b738-7dea88931877

📥 Commits

Reviewing files that changed from the base of the PR and between 8ea8a30 and af4f5f0.

📒 Files selected for processing (1)
  • .github/workflows/ci-macos.yml

Comment thread .github/workflows/ci-macos.yml
norrietaylor and others added 5 commits June 1, 2026 21:40
Add a build-macos job to CI on the org self-hosted runner (jks-Mac-mini,
labels self-hosted/macOS/ARM64). minvmd links libkrun and only compiles on
macOS, so its krun module and FFI smoke have had no CI coverage — the Linux
jobs build it as a stub.

The job ensures libkrun is installed, then runs clippy + tests for minvmd and
the libkrun FFI smoke (MINVMD_E2E=1, create_ctx -> set_vm_config -> set_exec;
skips krun_start_enter so no codesigned hypervisor entitlement is needed).

Scoped to minvmd, the only macOS-gated crate; widen to --workspace once the
rest of the tree is confirmed mac-buildable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
libkrun is not in homebrew-core; `brew install libkrun` fails with "no
available formula". Use the fully-qualified `slp/krun/libkrun`, which
auto-taps github.com/slp/homebrew-krun.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address CodeRabbit on PR #283:
- Gate build-macos behind the RUN_MACOS_CI repo variable (defaults to
  enabled). timeout-minutes counts time queued for a self-hosted runner, so
  set RUN_MACOS_CI=false to skip the job while the runner is offline rather
  than letting it stall and time out.
- Add job-level `permissions: contents: read` (workflow level is
  contents: write) and `persist-credentials: false` on checkout, since the
  job only builds and runs on a persistent self-hosted runner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The self-hosted Apple Silicon runner is a single shared bottleneck. Two
optimizations so it doesn't block CI on a large repo:

- Move build-macos out of ci.yml into ci-macos.yml, scoped via on.*.paths to
  crates/minvmd/**, Cargo.toml, Cargo.lock, and the workflow itself. PRs that
  don't touch minvmd skip the workflow entirely, leaving the runner free.
- Warm builds: checkout with clean: false so the persistent runner's target/
  survives between runs instead of cold-rebuilding minvmd's dependency tree
  every time. concurrency cancel-in-progress (carried over) keeps superseded
  commits from queueing on the one runner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
clean: false on the persistent self-hosted runner let a prior checkout's
crates/minvmd and target/ persist, so the job compiled and "passed" code that
was not in the branch — a false green (the branch was forked from main before
minvmd merged). Use the default clean checkout: correctness over a warm-build
speedup on a shared runner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci-macos.yml:
- Around line 44-49: The workflow currently treats an unset RUN_MACOS_CI repo
variable as enabled because it checks for inequality with 'false'; change the
job condition so the macOS self-hosted job only runs when RUN_MACOS_CI is
explicitly set to 'true' (i.e., require equality to 'true' rather than checking
!= 'false'), which prevents PRs from queuing on self-hosted macOS runners by
default; update the workflow's if condition that references RUN_MACOS_CI
accordingly and leave the runs-on/timeout configuration unchanged.
- Around line 62-67: The workflow is disabling Homebrew tap trust by setting
HOMEBREW_NO_REQUIRE_TAP_TRUST="1", which bypasses tap verification before
running the conditional installation (the brew install slp/krun/libkrun step);
remove that environment override and instead ensure the tap is trusted or
preprovision libkrun on the runner (e.g., remove HOMEBREW_NO_REQUIRE_TAP_TRUST
from the env block and add a preparatory step that either runs brew tap
--repair/--trust for slp/krun or installs libkrun in the runner image so the
conditional if [ ! -f /opt/homebrew/lib/libkrun.dylib ] ; then brew install
slp/krun/libkrun ; fi runs without bypassing trust).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 081eeb9d-0095-4553-9bd1-40262c257d5c

📥 Commits

Reviewing files that changed from the base of the PR and between af4f5f0 and 1a69059.

📒 Files selected for processing (1)
  • .github/workflows/ci-macos.yml

Comment thread .github/workflows/ci-macos.yml
Comment thread .github/workflows/ci-macos.yml Outdated
norrietaylor and others added 2 commits June 1, 2026 21:48
Address CodeRabbit: HOMEBREW_NO_REQUIRE_TAP_TRUST=1 bypassed Homebrew's tap
trust, and auto-installing the third-party slp/krun tap on a persistent
self-hosted runner is a supply-chain risk. Provision libkrun as part of runner
setup; CI now only verifies the dylib is present and fails with a clear
message otherwise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The self-hosted Apple Silicon runner (jks-Mac-mini) is shared with
gominimal/minimal-vm-mac's apple/container nightly matrix. libkrun and
apple/container both drive Virtualization.framework. Leaving the apple/container
BuildKit builder VM running after this job wedges it with stale runc-native
snapshot mounts, after which the next tenant's `container build` RUN steps fail
with "operation not permitted" unmounting /var/lib/buildkit/runc-native/
snapshots — observed deterministically on the matrix's `latest` entry on 06-02,
the first day this workflow ran on the shared runner (gominimal/minimal-vm-mac#25).

Add an always() cleanup step that deletes the builder and stops the
apple/container system so the next workload starts from clean BuildKit state.
Best-effort and guarded: no-op when the container CLI is absent, never fails
this job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@norrietaylor

Copy link
Copy Markdown
Member Author

Added a post-job cleanup step to ci-macos.yml (commit on this branch).

This workflow shares the self-hosted runner (jks-Mac-mini) with gominimal/minimal-vm-mac's apple/container nightly matrix. Both libkrun (here) and apple/container drive Virtualization.framework. On 06-02 — the first day this workflow ran on the shared runner — the matrix's latest job began failing deterministically with:

operation not permitted unmounting /var/lib/buildkit/runc-native/snapshots/snapshots/4

Same apple/container 0.12.3 had passed every prior night. Details and correlation: gominimal/minimal-vm-mac#25.

The new always() step deletes the apple/container builder and stops its system service on exit, so a wedged builder isn't carried into the next tenant. Guarded (command -v container) and best-effort — no-op when the CLI is absent, never fails this job.

Defense-in-depth on the matrix side: gominimal/minimal-vm-mac#26 recreates the builder at job start.

@norrietaylor

Copy link
Copy Markdown
Member Author

Retracting the previous comment and reverting the cleanup commit (7477c4d8 reverts 1a56282b).

The change was wrong. libkrun uses Hypervisor.framework directly; apple/container uses Virtualization.framework — different APIs — and this workflow never invokes the container CLI, nor does the FFI smoke test boot a VM (it skips krun_start_enter). Putting apple/container builder cleanup here was scope-leak based on a temporal correlation with no mechanism. This PR has nothing to do with apple/container's BuildKit builder.

The builder wedge belongs to minimal-vm-mac's matrix itself (sequential apple/container version-switching on the shared runner without recreating the builder) and is fixed there in gominimal/minimal-vm-mac#26. No change to this PR is warranted.

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