Bash automation for a development-ready Mac.
Homebrew installs, shell and Terminal setup, optional dotfiles, and opinionated
defaults write tuning for Apple Silicon and Intel.
Structured automation for provisioning a polished, development-focused macOS environment: Homebrew formulas and casks, system-wide defaults, and repeatability you can trust in CI.
- Install vs configuration: Category scripts are split under
src/scripts/install/(Homebrew, installers, clones) andsrc/scripts/config/(defaults write, dotfiles, Git identity, login shell). Orchestrators run one bundle or both. - npm entrypoints: After
npm ci, usenpm run installs,npm run config, ornpm run all(see npm scripts). - Shared helpers:
utils.shcentralizes repo paths,log_error, safe copy/download helpers, and a single error log path. - System defaults:
config/system-config.shapplies appearance, input, Finder, Dock, Spotlight, menu bar (clock/battery), Night Shift, security-related settings, and Apple Silicon–friendlypmsettuning (see below). - CI: GitHub Actions runs
master.sh --cionmacos-latest(skipsinstall/pre-install.shfor speed).
- macOS (recent versions; scripts assume Darwin)
- Network access for Homebrew and downloads
- Administrator privileges when
sudois required (for example firewall, guest account, system Software Update plist, andpmsetinconfig/system-config.sh)
-
Clone the repository
git clone https://github.com/garretpatten/macOS-setup-scripts cd macOS-setup-scripts -
Update submodules (for dotfiles)
git submodule update --init --remote --recursive src/dotfiles/
-
Install Node dev deps (needed for
npm run)npm ci
-
Make scripts executable
chmod +x src/scripts/*.sh \ src/scripts/install/*.sh \ src/scripts/config/*.sh
-
Run setup
npm run all # or: bash src/scripts/master.sh
| Command | Runs |
|---|---|
npm run all |
Full provisioning (master.sh): interleaved installs and configuration (same order as the historical single master.sh; see Execution flow). |
npm run installs |
Install bundle only (run-install.sh): Homebrew, external installers, and security repo clones—no defaults write or dotfiles. |
npm run config |
Configuration bundle only (run-config.sh): macOS defaults, home layout, dev/shell dotfiles, Proton Pass PATH hook, completion banner. |
Use npm run config on machines that already have packages but should pick up the latest defaults, dotfiles copies, or shell tweaks from this repo.
Bash equivalents:
bash src/scripts/run-install.sh
bash src/scripts/run-config.sh
bash src/scripts/master.shRun a single category (paths are relative to the repo root):
bash src/scripts/install/cli.sh
bash src/scripts/config/system-config.sh # macOS defaults only
bash src/scripts/config/organizeHome.shconfig/shell.sh is normally invoked by run-config.sh / master.sh with zsh (login-shell parity with the prior setup).
macOS-setup-scripts/
├── .github/workflows/
│ └── test-runner.yaml # CI: master.sh --ci on macOS runners
├── src/
│ ├── scripts/
│ │ ├── utils.sh # Paths, logging, safe copy/download helpers
│ │ ├── master.sh # Full run: installs + config (interleaved; see below)
│ │ ├── run-install.sh # Install bundle only (--ci skips pre-install)
│ │ ├── run-config.sh # Configuration bundle only
│ │ ├── install/
│ │ │ ├── pre-install.sh # Homebrew bootstrap, Xcode CLT, softwareupdate
│ │ │ ├── cli.sh # CLI formulas
│ │ │ ├── media.sh # Media casks
│ │ │ ├── productivity.sh
│ │ │ ├── dev.sh # Dev formulas/casks, NVM, packer.nvim
│ │ │ ├── security.sh # Security packages, Proton Pass installer, clones
│ │ │ ├── shell.sh # Terminal fonts, Ghostty, tmux, Oh My Posh, etc.
│ │ │ └── post-install.sh# brew update / upgrade / cleanup
│ │ └── config/
│ │ ├── system-config.sh # defaults write, firewall, pmset; restarts services
│ │ ├── organizeHome.sh # Home directory layout
│ │ ├── dev.sh # Dotfiles (editors), global git config, colima start
│ │ ├── shell.sh # Shell/tmux dotfiles, ~/.dotfiles_path, chsh
│ │ ├── security.sh # Proton Pass PATH snippet in ~/.zshrc
│ │ └── completion.sh # Banner + next steps
│ ├── dotfiles/ # Submodule
│ └── assets/ # Completion banner (e.g. apple.txt)
├── setup_errors.log # Created at repo root when scripts run (gitignored)
└── LICENSE
master.sh keeps the historical interleaved ordering so macOS preferences run before long Homebrew work, development dotfiles apply immediately after dev packages, and shell dotfiles run after install/post-install.sh:
install/pre-install.sh— Homebrew bootstrap, Xcode Command Line Tools,softwareupdate(skipped withmaster.sh --ci)config/system-config.sh— macOS preferences and security-related system settingsconfig/organizeHome.sh— home directory layoutinstall/cli.sh,install/media.sh,install/productivity.sh— category installsinstall/dev.sh— development Homebrew stack, NVM,packer.nvimconfig/dev.sh— editor/Neovim dotfiles, global Git settings,colima startinstall/security.sh— security packages, Proton Pass installer,~/Hackingclonesinstall/shell.sh— shell/terminal Homebrew packagesinstall/post-install.sh—brewmaintenanceconfig/shell.sh— shell/dotfiles and default login shell (viazsh)config/security.sh— append Proton PassPATHto~/.zshrcif missingconfig/completion.sh— completion banner
run-install.sh runs steps 1 (optional) and 4–9 only (no defaults or dotfiles). run-config.sh runs 2, 3, 6, 10–12 in order (full configuration pass without installing packages).
This script writes user and system preferences and ends by restarting Dock, Finder, ControlCenter, SystemUIServer, and mds so changes take effect. Highlights:
- Appearance & UI: Dark mode, small sidebar icons, reduced window animation for snappier feedback
- Input: Classic scrolling, fast key repeat, full keyboard access (Tab through all controls), three-finger drag
- Security (single
sudosession): Application Firewall on, stealth mode, guest account off, automatic macOS updates via/Library/Preferences/com.apple.SoftwareUpdate, pluspmsetoptions suited to Apple Silicon (lid wake, TCP keepalive, Power Nap) - Hardening & updates:
.DS_Storesuppression on network/USB volumes, security-related Software Update toggles, Launch Services quarantine prompt off, Crash Reporter dialog off, disk “not ejected properly” notification off, screen-lock password settings - Finder & screenshots: Show extensions and hidden files, path bar, column view, search scoped to the current folder, POSIX path in the title bar, spring-loading for folders, screenshots under
~/Pictures/Screenshots(directory created before setting the path) - Dock & Spotlight: Autohide, minimize into app icon, no recent apps, faster Dock animations; Spotlight category ordering
- Menu bar: Custom clock format; battery percentage hidden (
com.apple.menuextra.batteryandcom.apple.controlcenterfor newer Control Center behavior) - Night Shift: Enabled with sunset–sunrise-style schedule (strength and schedule keys as in the script)
Adjust config/system-config.sh if you prefer stricter security (for example keeping quarantine prompts) or different power-management values.
log_error— stderr plus append tosetup_errors.logensure_directory—mkdir -pwith errors loggedcopy_file_safe/copy_directory_safe— copy only when source exists and destination is missingdownload_file_safe—curlwith timeouts and validation
Scripts append command errors with 2>>"$ERROR_LOG_FILE" || true where appropriate so a single failure does not stop the whole run.
- Error log:
setup_errors.logat the repository root (seeERROR_LOG_FILEinutils.sh). The file is gitignored (*.log).
On push/PR to master, Test Runner (.github/workflows/test-runner.yaml) runs bash src/scripts/master.sh --ci on a GitHub-hosted macOS runner. That skips install/pre-install.sh to avoid long Xcode/OS update steps; the workflow checks setup_errors.log for real failures.
Illustrative list; see each script under src/scripts/install/ and src/scripts/config/ for exact commands.
- Homebrew (install if missing),
brew update,brew upgrade,brew cleanup, analytics off - Xcode Command Line Tools and system software updates (
softwareupdate)
bat, btop, curl, eza, fastfetch, fd, git, htop, jq, lazygit, ripgrep, vim, wget
Brave Browser, DuckDuckGo, Spotify, VLC
- Homebrew casks: Balena Etcher, Google Gemini (desktop), Notion, Proton Drive, Proton Mail, Standard Notes, Zoom
- Homebrew formula: Raycast
- Homebrew formulas: Node, Python 3.12, Colima, Docker, Docker Compose, GitHub CLI (
gh), Neovim, Podman, Semgrep, ShellCheck, Tree-sitter, Angular CLI - Homebrew casks: Postman, Visual Studio Code
- Other Homebrew: Sourcegraph app (from
sourcegraph/apptap), Sourcegraph CLI (src-cli) - Also: NVM (official install script),
packer.nvimclone for Neovim
- Homebrew casks: 1Password, 1Password CLI, Proton VPN, Signal, Burp Suite, OWASP ZAP
- Homebrew formulas: OpenVPN, ExifTool, Nmap
- Also: Proton Pass CLI (install script), clones PayloadsAllTheThings and SecLists into
~/Hacking/(directory created if needed)
- Homebrew formulas: Oh My Posh (
jandedobbeleer/oh-my-posh/oh-my-posh), Ghostty, Zsh, tmux, zsh-autosuggestions, zsh-syntax-highlighting - Homebrew casks: Font Awesome Terminal Fonts, Fira Code, Meslo LG Nerd Font, Powerline Symbols
brew update, upgrade, and cleanup
- Firewall, stealth mode, guest account, Software Update and
pmsetbehavior (no Homebrew packages)
Creates ~/Books, ~/Games, ~/Hacking, ~/Projects; removes empty ~/Templates if present
Optional Neovim / Vim / VS Code / terminal-app config from src/dotfiles/, global Git user settings and credential helper, colima start
- Also: Ghostty and Oh My Posh config trees,
~/.config/tmux/(modular tmux includes/themes),.zshrc,.tmux.conffromsrc/dotfiles/; writes~/.dotfiles_pathwhen missing or stale; default login shell set to Zsh
Appends export PATH="/Users/garret/.local/bin:$PATH" to ~/.zshrc when Proton Pass CLI’s bin is not already referenced (runs after config/shell.sh so the main ~/.zshrc is copied first).
Prints completion notes and src/assets/apple.txt when present (Fastfetch macOS ASCII logo, with color placeholders removed for plain cat output)
Dotfiles and assets under src/dotfiles/ and src/assets/ as copied or referenced by the configuration scripts.
- Permissions: Some steps need
sudo; you may be prompted once persudoinvocation (grouped inconfig/system-config.shwhere possible). - Homebrew:
install/pre-install.shexpects to install or use Homebrew if missing. - Logs: Inspect
setup_errors.logat the repo root after a run.
tail -n 50 setup_errors.log- New Homebrew items: Add formulas or casks to the matching file under
src/scripts/install/, then runnpm run installs(orbash src/scripts/run-install.sh) or the single category script. - macOS defaults or dotfiles: Develop changes in the dotfiles repository when editing the submodule; bump this repo’s
src/dotfilessubmodule to that commit. Editsrc/scripts/config/for provisioning tweaks. Runnpm run config/bash src/scripts/run-config.sh(ormaster.sh) to apply configuration without reinstalling packages.
~/.dotfiles_path:config/shell.shseeds or refreshes this file sohome/.zshrccan find the checkout (for example…/macOS-setup-scripts/src/dotfiles).- tmux: The vendored
home/.tmux.confexpects~/.config/tmux/(includes, themes).config/shell.shcopiessrc/dotfiles/config/tmux/when that destination is not already present. - Full XDG symlink mirror: To link every
config/<app>/tree under~/.config/<app>/, run./setup.sh --link-xdg-configfrom the submodule directory (see the dotfiles README). Parentconfig/scripts still copy a subset for the apps this repo provisions (config/dev.sh,config/shell.sh). - Upstream workflow: After updating the submodule, run
npm run config(ormaster.sh) to consume changes on this machine.
For questions, bug reports, or feature requests, open an issue on this repository.
This project is licensed under the MIT License.