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, dotfiles, Git identity, login shell, 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
│ │ ├── lib/
│ │ │ └── dotfiles-install.sh # XDG symlinks and manifest file copies
│ │ ├── 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
│ │ ├── dotfiles.sh # Symlink config/ trees; copy home files from manifest
│ │ ├── dotfiles.manifest # Home-directory file copies (VS Code, .zshrc, etc.)
│ │ ├── dev.sh # Dotfiles, global git config, colima start
│ │ ├── shell.sh # Default login shell (chsh)
│ │ └── 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— dotfiles (XDG symlinks + manifest copies), 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— default login shell (viazsh)config/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–11 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, ffmpeg-full, fzf, git, htop, imagemagick-full, jq, lazygit, poppler, resvg, ripgrep, sevenzip, vim, wget, yazi, zoxide — fd and ripgrep also support Neovim Telescope — then brew link ffmpeg-full imagemagick-full -f --overwrite so the -full kegs’ binaries are linked
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, Ruby, Go, Colima, Docker, Docker Compose, GitHub CLI (
gh), Neovim, Podman, Semgrep, ShellCheck, Tree-sitter, Tree-sitter CLI, 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 - Neovim / Mason host runtimes (also used outside the editor): Node and Python 3.12 for npm- and pip-based Mason tools; Ruby (keg-only Homebrew Ruby for
gem install, e.g. Solargraph); Go for Go-based Mason tools (gopls,shfmt, etc.); Tree-sitter CLI to compile Treesitter parsers from source (distinct from thetree-sitterlibrary formula). fd and ripgrep for Telescope are installed ininstall/cli.sh.
- 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, Symbols Only Nerd Font (for terminal icons such as Yazi)
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
Symlinks all src/dotfiles/config/ trees into ~/.config/, then copies home files from config/dotfiles.manifest when the destination is missing
Runs config/dotfiles.sh, seeds global Git settings when ~/.gitconfig is absent, colima start
- Sets default login shell to Zsh (
chsh)
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.
config/dotfiles.sh: Symlinks everysrc/dotfiles/config/<app>/tree to~/.config/<app>/, then copies home-directory files listed inconfig/dotfiles.manifest(.zshrc,.vimrc, VS Code settings). Existing real directories at a target path are moved aside with a timestamped backup before linking.- OS-specific zsh: Snippets under
config/zsh/(for exampledarwin.zsh) are picked up automatically once~/.config/zshis symlinked; no repo-path cache file is required. - tmux: Config lives at
~/.config/tmux/tmux.confvia the XDG symlink pass (no separate~/.tmux.confcopy). - 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.