feat(ci): add free macOS Apple Silicon E2E jobs#1
Merged
Conversation
Standard macos-26 runners on GitHub Actions are Apple Silicon (M1, 3
CPU, 7 GB RAM, 14 GB disk) and free for public repos with no minute
limit. That's exactly the platform this repo targets, so it would be
silly not to use them.
What
- playbook.yml: add per-role tags so CI can run a subset (system +
homebrew) cleanly. Every base-path role now carries both 'base' and
its own role name as tags.
- .github/workflows/ci.yml: two new macOS jobs.
- macos-check: ansible-playbook --syntax-check, then runs ONLY the
pre_tasks asserts via --tags always. Proves the playbook boots
cleanly on a real Apple Silicon mac without doing any installs.
~3 min.
- macos-homebrew: runs system + homebrew roles for real. Catches
typo'd brew formulas / casks before a user hits them on their
own Mac. Stops there because the kubernetes role wants to launch
Docker Desktop which is not viable inside a CI runner. ~5-10
min.
Why
The Ubuntu lint jobs catch about 80% of the things that would break
the bootstrap. The remaining 20% are platform-bound: a brew formula
typo, an Ansible module that behaves differently on macOS than
linux, the arm64 + Sequoia preflight asserts themselves. Free macOS
runners make checking that 20% on every PR cost zero.
What is NOT in this PR
- Full bootstrap end-to-end. The kubernetes role tries to start
Docker Desktop; the model_starter role wants a real llama-server.
Both are out of reach of a 7 GB / no-GPU-passthrough runner. Could
be a label-gated Tier-3 job later if we move to a self-hosted Mac
Mini.
- Adding these new jobs as required status checks on main. Want to
see them stable across a few PRs before promoting; require
yamllint + ansible-lint + shellcheck for now.
Signed-off-by: Christopher Maher <chris@mahercode.io>
CI's macos-26 runner installs ansible 13.6.0 (current) which ships community.general 12.0.0+. That release removed the community.general.yaml callback plugin that our ansible.cfg was asking for under stdout_callback = yaml: [ERROR]: The 'community.general.yaml' callback plugin has been removed. The plugin has been superseded by the option `result_format=yaml` in callback plugin ansible.builtin.default from ansible-core 2.13 onwards. ansible-playbook exited 1 before our preflight tasks could run, so both macos-check and macos-homebrew jobs failed at first contact. What - stdout_callback = yaml -> stdout_callback = ansible.builtin.default - Add [callback_default] section with result_format = yaml so the output still renders in YAML (more readable when debug tasks fire) - collections_paths (deprecated) -> collections_path (current) Why The old config was written against an older Ansible cookbook style; this brings it forward to ansible-core 2.13+ idioms. No behavior change for users; CI now matches what local installs see on a fresh brew install ansible. How Verified locally: the same command CI runs (`ansible-playbook -i inventory/localhost.yml playbook.yml --tags always`) now passes with the four preflight asserts all OK. Signed-off-by: Christopher Maher <chris@mahercode.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds two macOS jobs to CI on free GitHub-hosted Apple Silicon
(
macos-26) runners. Catches platform-bound regressions the Ubuntulint jobs cannot see, at zero cost (standard
macos-26is free +unlimited for public repos).
Files touched:
playbook.yml— per-role tags so CI can run subsets cleanly..github/workflows/ci.yml— two new jobs (macos-check,macos-homebrew).Why
The Ubuntu lint jobs catch ~80% of regressions (Ansible syntax,
yamllint, shellcheck). The remaining 20% are platform-bound:
dockervsDocker Desktop,etc.) — only fails on a real Mac
Sequoia floor)
Until standard
macos-26runners were Apple Silicon, the cost ofcovering that 20% was real money. They are now free for public
repos, so we should use them.
How
Two jobs:
macos-checkansible-playbook --syntax-check, then--tags always(runs only the macOS / arm64 / Sequoia preflight asserts; no installs)macos-homebrewsystem+homebrewroles for real. Verifies the package set installs cleanly on a freshly-spawned Apple Silicon Mac.The
macos-homebrewjob stops at the homebrew role on purpose: thekubernetesrole wants to launch Docker Desktop which is not viableinside a CI runner. Same applies to
llmkube_core/model_starter(no Metal GPU pass-through). A future tier-3 "full bootstrap" job
would need a self-hosted Mac Mini or similar; out of scope here.
How I picked the runner
macos-26(Tahoe) — standard, free for public repos, Apple Silicon.Chosen over
macos-15(Sequoia, also free) to catchforward-compatibility issues early. Our playbook's pre-flight asserts
already require macOS 15 or later; macos-26 satisfies that with
margin.
What's NOT in this PR
macos-check+macos-homebrewto required statuschecks on
main. Want to see them stable across a few PRs first.Once they prove themselves, I'll bump
branch protection contexts.larger paid runner or self-hosted; not justified yet.
Checklist
make testpasses locally — no-op for an Ansible repo.make lintpasses locally —ansible-playbook --syntax-checkis green; the three existing CI jobs are clean.
git commit -s) per DCO.not adding anything user-facing here.