Skip to content

intelligent-iterations/ii-queue-runner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pipeline — Self-Hosted GitHub Actions on Apple Silicon

Ephemeral Tart VM runners on macOS. One config file, dynamic job profiles, shared host cache, zero idle resource consumption.

Architecture

GitHub Actions (cloud)
    |
    |  workflow triggers (push, PR, @claude mention, @codex mention, dispatch)
    v
+------------------------------------------------------------+
|  Your Mac (Apple Silicon)                                  |
|                                                            |
|  Coordinator Runner (always-on, ~128 MB)                   |
|    - Receives boot-vm jobs                                 |
|    - Checks RAM budget (ram-ledger.sh)                     |
|    - Clones golden image (APFS CoW, near-instant)          |
|    - Generates JIT runner token via GitHub API              |
|    - Boots ephemeral VM with shared cache mount             |
|    - VM picks up the real job, runs it, exits               |
|    - Cleanup job destroys VM and releases RAM               |
|                                                            |
|  +------------------------------------------------------+  |
|  |  Tart VM (ephemeral, destroyed after each job)       |  |
|  |  Image: configurable golden image                    |  |
|  |  Tools: Node, Python, Flutter, Claude Code, etc.     |  |
|  |  Mounts:                                             |  |
|  |    shared/ -> JIT config + runner script              |  |
|  |    cache/  -> host-level dependency cache             |  |
|  +------------------------------------------------------+  |
+------------------------------------------------------------+

Quick Start

1. Prerequisites

brew install cirruslabs/cli/tart
brew install esolitos/ipa/sshpass
brew install gh jq
gh auth login    # needs admin:org scope

2. Configure

Edit pipeline.yml with your GitHub org and desired profiles:

github:
  org: your-org-name
  runner_name: my-coordinator

host:
  reserved_ram_gb: 4    # rest goes to VMs

profiles:
  developer:
    ram_mb: 8192
    cpu: 6
    timeout_minutes: 60
  coder:
    ram_mb: 2048
    cpu: 2
    timeout_minutes: 60
  marketer:
    ram_mb: 5120
    cpu: 4
    timeout_minutes: 30

3. Run setup

./setup.sh install

This will:

  1. Check prerequisites (tart, sshpass, gh, jq, python3)
  2. Generate coordinator/config.env, run-coordinator.sh, and the LaunchDaemon plist
  3. Download the GitHub Actions runner binary
  4. Register the coordinator runner with your org
  5. Print the sudo commands to install the LaunchDaemon

4. Build a golden image

# Pull a pre-built macOS base
tart pull ghcr.io/cirruslabs/macos-sequoia-base:latest
tart clone ghcr.io/cirruslabs/macos-sequoia-base:latest base-macos

# Apply base config (disables sleep, installs runner binary)
# Boot base-macos, SSH in, run: bash /Volumes/My\ Shared\ Files/shared/setup-base.sh
tart run base-macos --no-graphics --dir="shared:images/base"

# Build your workhorse image
bash images/videogen-and-browser/provision.sh
tart clone videogen-and-browser workhorse
tart delete videogen-and-browser

5. Copy workflows to your repos

Copy from workflows/ to each repo's .github/workflows/:

Workflow Trigger Profile Use Case
developer.yml PR, dispatch developer (8 GB) Claude Code review, scripting, Flutter
coder.yml PR, dispatch coder (2 GB) Lightweight AI coding
marketer.yml push, PR, dispatch marketer (5 GB) Playwright, FFmpeg, content pipeline
claude-agent.yml @claude mention coder (2 GB) AI agent triggered by issue/PR comments
codex-agent.yml @codex mention coder (2 GB) AI agent triggered by issue/PR comments

6. Validate

./setup.sh validate    # health check
./setup.sh test        # integration tests

Job Profiles

Same golden image, different resource allocations. Profiles are defined in pipeline.yml:

Profile RAM CPU Timeout Runner Label
developer 8 GB 6 60 min self-hosted, developer
coder 2 GB 2 60 min self-hosted, coder
marketer 5 GB 4 30 min self-hosted, marketer

Adding a new profile — just add it to pipeline.yml and run ./setup.sh config:

profiles:
  my-new-profile:
    ram_mb: 4096
    cpu: 4
    timeout_minutes: 45

No script edits required. Create a matching workflow template with VM_TYPE: my-new-profile.

Concurrency

VM RAM ceiling = total host RAM minus reserved. Jobs exceeding the ceiling queue automatically.

Example (16 GB Mac, 4 GB reserved = 12 GB ceiling):

  • 5x coder (10 GB) — yes
  • 2x marketer (10 GB) — yes
  • 1x developer + 1x marketer (13 GB) — no, marketer queues
  • 1x developer + 2x coder (12 GB) — yes (exactly at ceiling)

AI Agent Workflows (@claude / @codex)

Comment @claude <prompt> or @codex <prompt> on any issue or PR to trigger an AI coding agent. Both use the lightweight coder profile (2 GB) and participate in the normal RAM queue.

Required secrets:

  • ANTHROPIC_API_KEY — for Claude Code workflows
  • OPENAI_API_KEY — for Codex workflows
  • GITHUB_TOKEN — automatic, used for PR comments

Cache Strategy

System tools (Node, Python, FFmpeg) are baked into the golden image. Project deps (node_modules, pip) are cached on the host and mounted into VMs:

Host: <pipeline-dir>/cache/    ← persists across VM lifetimes
  │
  └── mounted into every VM via Tart --dir
  │
VM: /Volumes/My Shared Files/cache/

How Jobs Flow

1. Trigger (push, PR, @claude mention, dispatch)
2. GitHub queues boot-vm job → [self-hosted, coordinator]
3. Coordinator runs dispatch.sh:
   a. ram-ledger.sh acquire → check RAM budget
   b. tart clone → APFS CoW clone (~1s)
   c. gh api generate-jitconfig → one-time runner token
   d. tart run → boot VM with mounts
   e. SSH in, start runner with JIT config
4. VM runner registers, picks up the real job
5. Job runs inside isolated VM
6. Runner exits
7. Cleanup job: tart delete + ram-ledger.sh release
8. Machine returns to idle

Operations

./setup.sh validate           # health check
./setup.sh test               # integration tests
./setup.sh config             # regenerate config after editing pipeline.yml

bash coordinator/ram-ledger.sh status    # view RAM allocation
bash scripts/cache-cleanup.sh            # prune stale cache (>7 days)
bash scripts/teardown-all.sh             # emergency: kill all VMs
bash scripts/disk-audit.sh              # report disk consumers
bash scripts/health-check.sh            # detailed system check

Directory Layout

pipeline/
├── pipeline.yml                # Configuration (edit this)
├── setup.sh                    # Setup / config / validate / test
├── lib/
│   └── parse-config.py         # YAML → shell config generator
├── coordinator/                # VM lifecycle + RAM accounting
│   ├── config.env              # Generated by setup.sh
│   ├── dispatch.sh             # Core: clone → JIT → boot → run
│   ├── ram-ledger.sh           # RAM budget: acquire/release/status
│   ├── run-coordinator.sh      # Generated by setup.sh
│   └── install-coordinator.sh  # Legacy wrapper → setup.sh
├── images/                     # Golden image provisioning
│   ├── base/                   # Base macOS setup
│   └── videogen-and-browser/   # Workhorse image
├── workflows/                  # GitHub Actions templates
│   ├── developer.yml           # Claude Code (8 GB)
│   ├── coder.yml               # Lightweight coding (2 GB)
│   ├── marketer.yml            # Content pipeline (5 GB)
│   ├── claude-agent.yml        # @claude trigger
│   ├── codex-agent.yml         # @codex trigger
│   └── pipeline-selftest.yml   # System validation
├── tests/
│   ├── integration/            # setup.sh test suite
│   ├── node-basic/             # Environment check
│   ├── playwright/             # Browser test
│   └── flutter-hello/          # Flutter test
├── scripts/                    # Operational utilities
│   ├── health-check.sh
│   ├── cache-cleanup.sh
│   ├── teardown-all.sh
│   ├── rebuild-image.sh
│   └── disk-audit.sh
└── launchd/                    # Generated plist

Multi-Machine Scaling

Each Mac runs its own coordinator. Set a unique runner_name in each machine's pipeline.yml:

# Machine A
github:
  runner_name: mac-mini-a

# Machine B
github:
  runner_name: mac-mini-b

GitHub Actions distributes jobs across all coordinators automatically.

Troubleshooting

Problem Solution
VM doesn't boot ./setup.sh validate — check Tart, golden image, disk space
Runner doesn't register Check gh auth status, verify org name in pipeline.yml
SSH timeout VM may need more boot time. Check tart ip <vm-name>
RAM denied bash coordinator/ram-ledger.sh status — check for stuck VMs
Stale VMs bash scripts/teardown-all.sh to reset
Cache mount fails Verify VirtioFS works: tart run <vm> --dir=test:/tmp/test
LaunchDaemon not starting sudo launchctl print system/com.pipeline.coordinator

About

Self-hosted GitHub Actions on Apple Silicon — ephemeral Tart VMs, YAML config, RAM-based queuing, @claude/@codex agent triggers

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors