This document describes every layer of testing available for mac-dev-setup,
from automated CI to full acceptance tests on a clean machine.
Every pull request and push to main runs three jobs on GitHub Actions:
| Check | How | What it catches |
|---|---|---|
| Shell script linting | shellcheck on all .sh files |
Broken syntax, unsafe patterns, unused variables |
| Secret safety scan | Regex against secrets.template |
Real credentials accidentally committed |
.gitignore verification |
Grep for .secrets entry |
Secrets file exposed in git |
| Check | How | What it catches |
|---|---|---|
| Formula correctness | brew audit --strict |
Invalid formula structure, bad depends_on |
| Formula style | brew style |
Ordering violations, deprecated API usage |
| Check | How | What it catches |
|---|---|---|
| Package install | brew bundle --file=Brewfile.ci |
Broken package names, dependency conflicts |
| Dotfile install | bash scripts/install-dotfiles.sh |
Script errors, missing source files |
| Symlinks created | test -L ~/.zshrc && test -L ~/.vimrc |
install-dotfiles.sh not linking correctly |
| Secrets template | test -f ~/.secrets |
Template not copied on first run |
| File permissions | stat -f "%Mp%Lp" ~/.secrets == 0600 |
Secrets file world-readable |
| Tool availability | command -v for go, kubectl, helm, terraform, doctl, jq, gh |
Package installs failing silently |
What CI does NOT cover:
- GUI cask installs (Cursor, Docker Desktop, GoLand, etc.). Too slow for CI
- The full
Brewfile.Brewfile.ciis a CLI-only subset - Interactive scripts (
setup-credentials.sh,setup-mcps.sh) - Vim plugin installation
- Homebrew bootstrap (runner already has Homebrew)
- Apple Silicon vs Intel differences (runner is fixed architecture)
These gaps are covered by the acceptance testing options below.
act runs GitHub Actions workflows locally using Docker.
Use this to iterate on workflow changes without pushing.
Install:
brew install actUsage:
# Run the full validate pipeline
act pull_request
# Run a single job
act pull_request -j lint
act pull_request -j formula-audit
act pull_request -j integration
# List available workflows and jobs
act --list
# Dry run — show what would execute without running
act pull_request --dryrunNotes:
actruns on Linux containers by default. Thelintjob works perfectly- The
formula-auditandintegrationjobs require macOS and will be skipped or emulated - Use
actprimarily for rapid iteration on thelintjob before pushing - First run downloads a Docker image (~1GB). Subsequent runs are fast
Create a second user on your Mac with a clean home directory. This is the closest thing to a real zero-state install without any additional hardware.
What it tests:
- Full
Brewfileincluding all GUI casks - Complete
setup.shfrom start to finish - Dotfile symlinking
- Vim plugin bootstrap
setup-credentials.shinteractive flowsetup-mcps.shMCP configuration- The actual user experience of a fresh install
Steps:
-
Create a new user account:
System Settings → Users & Groups → Add Account Name: testenv (or any name) Account type: Administrator (needed for Homebrew) -
Log in as the new user (or use fast user switching):
Apple menu → Lock Screen, then log in as testenv -
Open Terminal and run setup:
git clone https://github.com/amcheste/mac-dev-setup \ ~/Repos/amcheste/mac-dev-setup cd ~/Repos/amcheste/mac-dev-setup bash setup.sh -
Walk through the credential wizard. Use dummy values or real ones.
-
Verify the environment works as expected.
Cleanup:
System Settings → Users & Groups → select testenv → Delete Account
Choose "Delete the home folder" to fully clean up.
Tips:
- You can leave the test user around and re-run
bash scripts/upgrade.shto test upgrades - Use fast user switching to go back and forth without logging out
- Take a screenshot of any failures for debugging
Add a workflow_dispatch trigger to run the full pipeline manually. Including casks. From the GitHub UI.
This gives you a cloud-hosted, recorded acceptance test you can run before any release.
To enable: The release.yml workflow already has validation gates.
For a manual full test, trigger it from the Actions tab:
GitHub → Actions → Validate → Run workflow → select branch → Run
To add a full-Brewfile manual job (including casks), add this to validate.yml:
full-install:
name: Full Install (Manual Only)
runs-on: macos-latest
if: github.event_name == 'workflow_dispatch'
steps:
- uses: actions/checkout@v4
- name: Full brew bundle including casks
run: brew bundle --file=Brewfile --no-upgrade
timeout-minutes: 60What this adds over CI:
- Installs all casks (Cursor, Docker, GoLand, etc.)
- Validates full
Brewfilepackage names are current - Gives you a timestamped, logged record of a clean install
Cost: macOS runner minutes count against your GitHub Actions quota (~10x Linux rate). A full run with casks takes ~30-45 minutes. Run manually before releases, not on every PR.
UTM is a free, open-source VM manager for macOS. A macOS guest VM gives true clean-room testing. Snapshotable and restorable.
Install UTM:
brew install --cask utmSetup:
-
Download a macOS IPSW restore image from mrmacintosh.com or via
softwareupdate --fetch-full-installer -
In UTM: New VM → Virtualize → macOS → select IPSW → configure RAM/disk (16GB RAM, 60GB disk recommended)
-
Complete macOS setup inside the VM. Create a user account, skip Apple ID
-
Take a snapshot before running setup (UTM → VM menu → Snapshot → Save):
Snapshot name: "clean-install" -
Run setup inside the VM:
git clone https://github.com/amcheste/mac-dev-setup \ ~/Repos/amcheste/mac-dev-setup bash ~/Repos/amcheste/mac-dev-setup/setup.sh -
After testing, restore to snapshot to reset to clean state:
UTM → VM menu → Snapshot → Restore → "clean-install"
What this adds:
- True clean-room. No pre-installed tools, fresh Homebrew
- Restorable. Test as many times as you want from the same baseline
- Architecture testing. Create ARM and Intel VMs to test both
- Offline testing. No cloud dependency
Notes:
- Apple Silicon Macs can only run Apple Silicon macOS guests (not Intel macOS)
- Performance is good for CLI work; GUI app installs are slow in the VM
- First-time setup of the VM takes ~30 minutes but snapshots make subsequent tests instant
For a fully automated, hosted clean-room test. Relevant if this becomes a team setup.
GitHub Larger Runners (macOS):
macos-latest-xlarge. Apple Silicon, 6 vCPU, 14GB RAM- Add to a workflow with
runs-on: macos-latest-xlarge - Fully clean environment, billed per minute
- Good for release gates on a team
MacStadium / Orka:
- Dedicated Mac hardware in the cloud
- Persistent VMs you control. Snapshot, restore, automate
- Relevant when this setup is used across a team
Cost estimate for a full manual install run:
- GitHub macOS-latest: ~$0.08/min × 45 min ≈ $3.60 per run
- Run this manually before releases, not on every PR
Use this checklist when running a full acceptance test (Options 2, 4, or 5):
-
setup.shcompletes without errors - Homebrew is installed (or detected if already present)
- All packages in
Brewfileinstall successfully -
~/.zshrcis symlinked todotfiles/zshrc -
~/.vimrcis symlinked todotfiles/vimrc -
~/.secretsis created from template withchmod 600 - vim-plug is installed at
~/.vim/autoload/plug.vim - Vim plugins install without errors (
vim +PlugInstall +qall)
-
go versionworks -
python3 --versionworks (via pyenv) -
node --versionworks (via nvm) -
kubectl version --clientworks -
helm versionworks -
terraform versionworks -
doctl versionworks -
gh --versionworks -
docker --versionworks (if Docker Desktop installed)
-
setup-credentials.shruns and writes~/.secrets -
source ~/.secretsexports expected variables -
setup-mcps.shconfigures all MCP servers -
claude mcp listshows GitHub, filesystem, memory, postgres -
gh auth statusshows authenticated
- Open a new terminal. Prompt appears correctly
-
reposalias navigates to~/Repos -
kalias works (kubectl) -
tfalias works (terraform) - pyenv and nvm load without errors on shell start
-
bash scripts/upgrade.shruns without errors on an already-configured machine - Re-running
setup.shis idempotent. No duplicate installs or errors