Skip to content

feat(ci): add free macOS Apple Silicon E2E jobs#1

Merged
Defilan merged 2 commits into
mainfrom
feat/macos-ci-jobs
May 21, 2026
Merged

feat(ci): add free macOS Apple Silicon E2E jobs#1
Defilan merged 2 commits into
mainfrom
feat/macos-ci-jobs

Conversation

@Defilan

@Defilan Defilan commented May 21, 2026

Copy link
Copy Markdown
Member

What

Adds two macOS jobs to CI on free GitHub-hosted Apple Silicon
(macos-26) runners. Catches platform-bound regressions the Ubuntu
lint jobs cannot see, at zero cost (standard macos-26 is 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:

  • typo in a brew formula / cask name (docker vs Docker Desktop,
    etc.) — only fails on a real Mac
  • Ansible module behavior differences between Linux and macOS
  • the playbook's own pre-flight asserts (Apple Silicon arch + macOS
    Sequoia floor)

Until standard macos-26 runners were Apple Silicon, the cost of
covering that 20% was real money. They are now free for public
repos, so we should use them.

How

Two jobs:

Job What it does Wall-clock
macos-check ansible-playbook --syntax-check, then --tags always (runs only the macOS / arm64 / Sequoia preflight asserts; no installs) ~3 min
macos-homebrew Runs system + homebrew roles for real. Verifies the package set installs cleanly on a freshly-spawned Apple Silicon Mac. ~5-10 min

The macos-homebrew job stops at the homebrew role on purpose: the
kubernetes role wants to launch Docker Desktop which is not viable
inside 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 catch
forward-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

  • Promoting macos-check + macos-homebrew to required status
    checks on main. Want to see them stable across a few PRs first.
    Once they prove themselves, I'll bump branch protection contexts.
  • A full end-to-end bootstrap on a runner. Would require either a
    larger paid runner or self-hosted; not justified yet.

Checklist

  • Tests added/updated — N/A; CI surface itself.
  • make test passes locally — no-op for an Ansible repo.
  • make lint passes locally — ansible-playbook --syntax-check
    is green; the three existing CI jobs are clean.
  • Commit messages follow conventional commits.
  • All commits are signed off (git commit -s) per DCO.
  • Documentation updated — README already mentions the CI layer;
    not adding anything user-facing here.

Defilan added 2 commits May 21, 2026 00:04
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>
@Defilan Defilan merged commit e5e827e into main May 21, 2026
5 checks passed
@Defilan Defilan deleted the feat/macos-ci-jobs branch May 21, 2026 07:19
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.

1 participant