Skip to content

Instantly share code, notes, and snippets.

@martynhaigh
Last active May 12, 2026 06:37
Show Gist options
  • Select an option

  • Save martynhaigh/4441a32fdf9b46d7d4cb911a6323d95d to your computer and use it in GitHub Desktop.

Select an option

Save martynhaigh/4441a32fdf9b46d7d4cb911a6323d95d to your computer and use it in GitHub Desktop.
Mac bootstrap for marty's dotfiles
#!/usr/bin/env bash
#
# Public Mac bootstrap. Live at:
# https://gist.githubusercontent.com/martynhaigh/<GIST_ID>/raw/install.sh
#
# Run on a fresh Mac:
# bash <(curl -fsSL https://gist.githubusercontent.com/.../install.sh)
#
# What it does:
# 1. Installs Xcode Command Line Tools (sudo prompt).
# 2. Installs Homebrew.
# 3. Installs gh, chezmoi, age, and pipx (just enough to bootstrap).
# 4. Logs you into GitHub via browser OAuth (Keeper autofills).
# 5. Generates an SSH key (or reuses existing) and registers it with GitHub.
# 6. Runs `chezmoi init --apply` against the private dotfiles repo.
# 7. chezmoi prompts for machine context (work/personal) and user details.
# 8. chezmoi takes over — installs Brewfiles, applies configs, runs macOS defaults.
#
# Idempotent: re-run any time. Skips steps that already succeeded.
# ---------------------------------------------------------------------------
set -Eeuo pipefail
DOTFILES_REPO="[email protected]:martynhaigh/dotfiles.git"
DOTFILES_REPO_HTTPS="https://github.com/martynhaigh/dotfiles.git"
# Colours
RED=$'\033[0;31m'; GRN=$'\033[0;32m'; YEL=$'\033[0;33m'; BLU=$'\033[0;34m'; RST=$'\033[0m'
info() { printf "%s[INFO]%s %s\n" "$BLU" "$RST" "$*"; }
ok() { printf "%s[ OK ]%s %s\n" "$GRN" "$RST" "$*"; }
warn() { printf "%s[WARN]%s %s\n" "$YEL" "$RST" "$*"; }
err() { printf "%s[ERR ]%s %s\n" "$RED" "$RST" "$*"; }
prompt(){ printf "%s[ ?? ]%s %s " "$YEL" "$RST" "$*"; }
trap 'err "Bootstrap failed at line $LINENO. Re-run when ready."' ERR
# 1. Xcode CLI Tools ---------------------------------------------------------
if xcode-select -p >/dev/null 2>&1; then
ok "Xcode Command Line Tools already installed"
else
info "Installing Xcode Command Line Tools (a GUI prompt will appear)"
xcode-select --install
until xcode-select -p >/dev/null 2>&1; do sleep 5; done
ok "Xcode Command Line Tools installed"
fi
# 2. Homebrew ----------------------------------------------------------------
if command -v brew >/dev/null 2>&1; then
ok "Homebrew already installed"
else
info "Installing Homebrew"
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
# Ensure brew is on PATH for this script
if [[ -x /opt/homebrew/bin/brew ]]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
elif [[ -x /usr/local/bin/brew ]]; then
eval "$(/usr/local/bin/brew shellenv)"
fi
# 3. Bootstrap tools ---------------------------------------------------------
info "Installing bootstrap tools: gh, chezmoi, age, git-delta, jq, neovim, pipx"
brew install gh chezmoi age git-delta jq neovim pipx
pipx ensurepath >/dev/null 2>&1 || true
export PATH="$HOME/.local/bin:$PATH"
# 4. GitHub OAuth ------------------------------------------------------------
if gh auth status >/dev/null 2>&1; then
ok "Already logged into GitHub"
else
info "Logging into GitHub via browser (Keeper will autofill)"
gh auth login --web --git-protocol ssh --scopes "repo,read:org,admin:public_key"
fi
gh auth setup-git
# 5. SSH key -----------------------------------------------------------------
SSH_KEY="$HOME/.ssh/id_ed25519"
if [[ -f "$SSH_KEY" ]]; then
ok "SSH key already present at $SSH_KEY (preserving)"
else
info "Generating SSH key"
EMAIL_GUESS="$(git config --global user.email 2>/dev/null || echo "")"
if [[ -z "$EMAIL_GUESS" ]]; then
prompt "Email for SSH key:"
read -r EMAIL_GUESS
fi
ssh-keygen -t ed25519 -C "$EMAIL_GUESS" -f "$SSH_KEY" -N ""
fi
# Add to ssh-agent + macOS Keychain
mkdir -p ~/.ssh && chmod 700 ~/.ssh
if ! grep -q "UseKeychain yes" ~/.ssh/config 2>/dev/null; then
cat >> ~/.ssh/config <<'EOF'
Host github.com
HostName github.com
User git
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
EOF
fi
ssh-add --apple-use-keychain "$SSH_KEY" 2>/dev/null || ssh-add -K "$SSH_KEY" 2>/dev/null || true
# Register the public key with GitHub if not already there
PUBKEY="$(cat "${SSH_KEY}.pub")"
HOSTNAME_LABEL="$(scutil --get ComputerName 2>/dev/null || hostname)-$(date +%Y%m%d)"
if gh ssh-key list 2>/dev/null | grep -qF "$(awk '{print $2}' <<<"$PUBKEY")"; then
ok "SSH key already registered with GitHub"
else
info "Uploading SSH key to GitHub as '$HOSTNAME_LABEL'"
gh ssh-key add "${SSH_KEY}.pub" --title "$HOSTNAME_LABEL" --type authentication
fi
# Verify SSH works
if ssh -T -o StrictHostKeyChecking=accept-new [email protected] 2>&1 | grep -q "successfully authenticated"; then
ok "GitHub SSH auth working"
else
warn "Initial SSH probe ambiguous; chezmoi clone below will surface any real problem"
fi
# 6. chezmoi init ------------------------------------------------------------
if [[ -d "$HOME/.local/share/chezmoi/.git" ]]; then
info "chezmoi already initialized — running update"
chezmoi update --force
else
info "Cloning dotfiles repo via chezmoi (you'll be asked a few setup questions)"
chezmoi init --apply --ssh "$DOTFILES_REPO" || {
warn "SSH clone failed, falling back to HTTPS"
chezmoi init --apply "$DOTFILES_REPO_HTTPS"
}
fi
ok "Bootstrap complete."
cat <<EOF
Next steps:
• Open a new terminal so PATH + shell config is fresh.
• Read ~/Code/dotfiles/README.md (or wherever chezmoi cloned).
• To make changes:
chezmoi edit ~/.zshrc # opens the source file
chezmoi apply # renders to ~
chezmoi cd # cd into the source repo
• To sync from another machine:
chezmoi update
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment