Skip to content

Instantly share code, notes, and snippets.

@jbnunn
Last active March 7, 2026 21:04
Show Gist options
  • Select an option

  • Save jbnunn/035ed0f8e38c8ace26a249ad7a497cc4 to your computer and use it in GitHub Desktop.

Select an option

Save jbnunn/035ed0f8e38c8ace26a249ad7a497cc4 to your computer and use it in GitHub Desktop.
Setup new Mac - The Basics (2026)
#!/usr/bin/env bash
# mac-setup.sh — Repeatable macOS bootstrap script
# Usage: bash mac-setup.sh
# Run this in a fresh terminal after a clean macOS install.
set -e
BREW="/opt/homebrew/bin/brew" # Apple Silicon path
# ─── Helpers ──────────────────────────────────────────────────────────────────
log() { echo ""; echo "==> $*"; }
warn() { echo " [!] $*"; }
# ─── 1. Homebrew ──────────────────────────────────────────────────────────────
log "Installing Homebrew"
if ! command -v brew &>/dev/null; then
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add brew to PATH for the rest of this script
eval "$($BREW shellenv)"
else
warn "Homebrew already installed, updating..."
brew update
fi
# ─── 2. Shell integration ─────────────────────────────────────────────────────
log "Adding Homebrew to shell profile"
SHELL_PROFILE="$HOME/.zprofile"
if ! grep -q "brew shellenv" "$SHELL_PROFILE" 2>/dev/null; then
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$SHELL_PROFILE"
fi
eval "$($BREW shellenv)"
# ─── 3. CLI tools ─────────────────────────────────────────────────────────────
log "Installing CLI tools via Homebrew"
brew install uv node dockutil starship neovim stow
# ─── 4. GUI apps (Casks) ──────────────────────────────────────────────────────
log "Installing GUI apps via Homebrew Cask"
brew install --cask \
google-chrome \
iterm2 \
ghostty \
orbstack
# ─── 5. Dotfiles ──────────────────────────────────────────────────────────────
log "Cloning and stowing dotfiles"
if [ ! -d "$HOME/dotfiles" ]; then
git clone https://github.com/jbnunn/dotfiles.git "$HOME/dotfiles"
else
warn "~/dotfiles already exists, skipping clone"
fi
# Ensure ~/.config is a real directory (not a symlink)
if [ -L "$HOME/.config" ]; then
unlink "$HOME/.config"
fi
mkdir -p "$HOME/.config"
cd "$HOME/dotfiles"
for pkg in bat btop eza ghostty ncspot neofetch nvim skhd starship tmux yazi; do
stow --target="$HOME" "$pkg" && echo " stowed: $pkg" || warn "failed: $pkg"
done
cd "$HOME"
# ─── 6. Shell config ──────────────────────────────────────────────────────────
log "Configuring shell"
if ! grep -q "starship init" "$HOME/.zshrc" 2>/dev/null; then
echo 'eval "$(starship init zsh)"' >> "$HOME/.zshrc"
fi
if ! grep -q "case-insensitive" "$HOME/.zshrc" 2>/dev/null; then
echo '' >> "$HOME/.zshrc"
echo '# Case-insensitive tab completion' >> "$HOME/.zshrc"
echo "zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'" >> "$HOME/.zshrc"
echo 'autoload -Uz compinit && compinit' >> "$HOME/.zshrc"
fi
# ─── 7. Keyboard settings ─────────────────────────────────────────────────────
log "Configuring keyboard repeat rate"
defaults write NSGlobalDomain KeyRepeat -int 2
defaults write NSGlobalDomain InitialKeyRepeat -int 15
defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false
# ─── 8. Clean up Dock ─────────────────────────────────────────────────────────
log "Clearing Dock (keeping only Finder)"
# dockutil --remove all leaves Finder since it can't be removed from Dock
dockutil --remove all --no-restart
# Restart Dock to apply changes
killall Dock
# ─── 9. Done ──────────────────────────────────────────────────────────────────
log "Done! A few things need manual attention:"
echo ""
echo " [ ] Log out and back in (or reboot) for all settings to take effect."
echo ""
echo " [ ] OrbStack launches like Docker — open it once to finish setup."
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment