Skip to content

omniaura/mac-runner

Repository files navigation

Mac Runner

Simple Mac menu bar app and CLI for managing GitHub Actions self-hosted runners.

Features

  • πŸƒ Run multiple GitHub Actions runners on a single Mac
  • πŸ–₯️ Dual CLI + GUI β€” manage runners from terminal or menu bar
  • πŸ”‘ gh CLI integration β€” no manual PAT tokens, uses your existing gh auth
  • πŸ”’ Hybrid isolation β€” choose user isolation (macOS runners) or container isolation (Linux workflows)
  • 🎭 Headless by default β€” runners run without GUI access (enable when needed for visual testing)
  • ⏸️ Pause/Resume all runners with one click
  • 🎯 Perfect for when you need your Mac's resources for intensive work
  • πŸ“Š Monitor runner status from menu bar
  • ⚑ Native Mac app, lightweight and fast
  • πŸ€– Fully automated setup β€” downloads and configures runners automatically

Why?

GitHub Actions self-hosted runners are great, but:

  • Official runner only supports one instance per machine
  • No easy way to pause runners when you need CPU/memory
  • Managing multiple repos means multiple runner processes
  • No visual indication of runner status

Mac Runner solves this.

Installation

Homebrew

brew tap omniaura/tap https://github.com/omniaura/mac-runner
brew install --cask mac-runner

Direct Download

Download the latest DMG from Releases

Unsigned Binary

Mac Runner is not code-signed yet. macOS Gatekeeper will block the first launch. To allow it:

xattr -cr /Applications/MacRunner.app

Or: right-click the app β†’ Open β†’ click "Open" in the dialog.

Prerequisites

  • macOS 13+ (macOS 15+ for user isolation, macOS 26+ for container isolation)
  • gh CLI installed and authenticated (gh auth login)
  • Apple Silicon Mac (for container isolation)

Quick Start

GUI

  1. Launch Mac Runner β€” appears in menu bar
  2. Click "Add Runner"
  3. Browse your repos (fetched via gh), pick one
  4. Runner downloads, configures, registers, and starts automatically

CLI

# Check GitHub auth
mac-runner auth

# Add a runner (downloads, configures, starts in background)
mac-runner add owner/repo --name my-runner --labels macos,mac-runner

# Add a runner with GUI access (for visual tests, Xcode UI tests, etc.)
mac-runner add owner/repo --enable-gui

# List runners
mac-runner list

# Start/stop
mac-runner start my-runner
mac-runner stop my-runner

# Remove (also deletes from GitHub)
mac-runner remove my-runner

# Status summary
mac-runner status

Runners started via CLI persist in the background β€” they survive the terminal session. Stop and start them from any terminal or from the GUI.

CI/CD: Self-Hosted Runner with Automatic Cloud Fallback

Mac Runner uses a pattern that automatically routes CI jobs to your self-hosted Mac when it's online, and falls back to GitHub-hosted cloud runners when it's not. This means pushes to main always build, regardless of whether your Mac is on.

How It Works

The release workflow uses mikehardy/runner-fallback-action to query the GitHub API for available self-hosted runners before the build job starts:

jobs:
  preflight:
    runs-on: ubuntu-latest
    outputs:
      runner: ${{ steps.runner.outputs.use-runner }}
    steps:
      - name: Select runner
        id: runner
        uses: mikehardy/runner-fallback-action@v1
        with:
          primary-runner: mac-runner
          fallback-runner: macos-latest
          fallback-on-error: true
          github-token: ${{ secrets.RUNNER_TOKEN }}

  build:
    needs: preflight
    runs-on: ${{ fromJson(needs.preflight.outputs.runner) }}
    steps:
      - run: echo "Running on the best available runner"
Mac online Runner used Cost
Yes mac-runner (self-hosted) Free
No macos-latest (cloud) GitHub Actions minutes

This pattern is useful for any project that wants fast, free self-hosted builds when available, with reliable cloud fallback. See the community discussion for background on why this isn't built into GitHub Actions natively.

Token Setup

The runner-fallback-action queries the GitHub REST API to check runner availability. This requires a token with admin read access β€” the default GITHUB_TOKEN does not have this permission.

  1. Create a fine-grained Personal Access Token:
    • Repository access: select "Only select repositories" and pick your repo
    • Permissions β†’ Repository β†’ Administration: Read-only
  2. Add it as a repository secret named RUNNER_TOKEN:
    gh secret set RUNNER_TOKEN

Note: If the token is missing or invalid, fallback-on-error: true ensures the workflow still runs β€” it just falls back to cloud runners.

References

Isolation Modes

Mac Runner supports three isolation modes to protect your development environment:

1. No Isolation (Default)

  • Runners execute directly as your current user
  • Simple setup, works on any macOS version
  • ⚠️ Not recommended for untrusted workflows β€” CI jobs have full access to your files

2. User Isolation (macOS 15+)

  • Each runner runs as a dedicated system user (e.g., _macrunner)
  • Prevents CI jobs from accessing your files, credentials, and desktop
  • Best for macOS-based workflows
  • How to enable:
    # CLI
    mac-runner add owner/repo --isolation user
    
    # GUI
    Settings β†’ Isolation Mode β†’ Dedicated User

3. Container Isolation (macOS 26+, Apple Silicon)

  • Runs Linux workflows in isolated, lightweight virtual machines
  • Uses Apple's Containerization framework
  • Sub-second startup times with Rosetta 2 for linux/amd64 emulation
  • Best for Linux-based workflows, Docker builds, cross-platform testing
  • Requirements: macOS 26+, Apple Silicon, Linux kernel 6.14.9+
  • How to enable:
    # CLI
    mac-runner add owner/repo --isolation container
    
    # GUI
    Settings β†’ Isolation Mode β†’ Container

Per-Runner Isolation Override

You can set a global default isolation mode and override it per-runner:

# Set global default to user isolation
mac-runner settings --isolation user

# Add a Linux runner with container isolation
mac-runner add owner/linux-project --isolation container

# Add a macOS runner with no isolation (for trusted workflows)
mac-runner add owner/trusted-project --isolation none

In the GUI, each runner displays its isolation mode with an icon:

  • πŸ”“ No isolation
  • πŸ‘€ User isolation
  • πŸ“¦ Container isolation

GUI Access

By default, runners operate in headless mode β€” they run without access to the GUI (display, windows, etc.). This is optimal for most CI jobs and prevents interference with your desktop.

When to enable GUI access:

  • Visual testing (screenshot comparisons, E2E tests with browsers)
  • Xcode UI tests
  • iOS Simulator tests
  • macOS app GUI automation

How to enable:

# CLI
mac-runner add owner/repo --enable-gui

# GUI
Add Runner β†’ Enable GUI Access (toggle)

Note: GUI sessions are currently shared across runners. For full isolation (separate GUI session per runner), see Issue #27.

Planned Features

  • Auto-provision CI tools (node, npm, gh) in runner environments
  • Automatic pause when battery low
  • Pause during specific hours
  • Resource usage monitoring
  • Notifications for job starts
  • Custom container images for containerized runners

Architecture

  • Swift 6 / SwiftUI β€” Native Mac app
  • Menu Bar Interface β€” Always accessible, minimal UI
  • gh CLI β€” All GitHub API calls go through gh (auth, repos, runner tokens, CRUD)
  • PID-based process management β€” Runners persist across CLI sessions
  • Dual entry point β€” main.swift dispatches to CLI handler or SwiftUI app

Development

# Build
swift build

# Run CLI
.build/debug/mac-runner --help

# Run GUI (no args)
.build/debug/mac-runner

# Test
swift test

Contributing

We use Conventional Commits for automatic versioning:

  • feat: β€” New feature (minor bump)
  • fix: β€” Bug fix (patch bump)
  • chore: β€” No release

See CONTRIBUTING.md for full guidelines.

License

MIT

About

Mac menu bar app for managing GitHub Actions self-hosted runners

Resources

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages