#!/usr/bin/env bash # Bootstrap a new Mac: Homebrew + gh, authenticate, clone the private dotfiles # repo to ~/.config, then hand over to its install.sh. # # bash -c "$(curl -fsSL dot.peterpolman.nl)" # # Idempotent: safe to re-run. set -euo pipefail REPO="peterpolman/dotfiles" CONFIG="$HOME/.config" log() { printf '\033[35m==>\033[0m %s\n' "$1"; } [ "$(uname -s)" = "Darwin" ] || { echo "macOS only"; exit 1; } log "Xcode command line tools" if ! xcode-select -p >/dev/null 2>&1; then # Ask softwareupdate for the CLT package by name, no GUI, no second run. touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress label=$(softwareupdate -l 2>/dev/null \ | grep -oE 'Command Line Tools for Xcode-[0-9.]+' | sort -V | tail -1) if [ -n "$label" ]; then echo " installing $label (a few minutes, needs your password)" sudo softwareupdate -i "$label" else echo " falling back to the GUI installer, click through it" xcode-select --install 2>/dev/null || true fi rm -f /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress # Either path is asynchronous; wait it out rather than failing. waited=0 until xcode-select -p >/dev/null 2>&1; do [ "$waited" -ge 1800 ] && { echo "timed out waiting for the CLT install"; exit 1; } sleep 10 waited=$((waited + 10)) done echo " installed" fi log "Homebrew" if ! command -v brew >/dev/null; then # NONINTERACTIVE makes the installer probe with `sudo -n`, which fails outright # when no credential is cached, so cache one first and keep it warm. sudo -v while true; do sudo -n true; sleep 50; done 2>/dev/null & sudo_keepalive=$! trap 'kill "$sudo_keepalive" 2>/dev/null || true' EXIT NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" kill "$sudo_keepalive" 2>/dev/null || true trap - EXIT fi eval "$(/opt/homebrew/bin/brew shellenv)" log "GitHub CLI" command -v gh >/dev/null || brew install gh # --hostname/--git-protocol/--skip-ssh-key remove every prompt except the # one-time browser code. The extra scopes let us upload the SSH key below. gh auth status >/dev/null 2>&1 || \ gh auth login --hostname github.com --git-protocol ssh --skip-ssh-key --web \ --scopes "admin:public_key,admin:ssh_signing_key" log "SSH key" KEY="$HOME/.ssh/id_ed25519" if [ ! -f "$KEY" ]; then cat <<'MSG' No ~/.ssh/id_ed25519 on this machine. Nothing is generated for you. Bring your key over (AirDrop ~/.ssh/id_ed25519 and .pub from the other Mac, then chmod 600 the private half), or create one: ssh-keygen -t ed25519 -C "$(hostname -s)" gh ssh-key add ~/.ssh/id_ed25519.pub gh ssh-key add ~/.ssh/id_ed25519.pub --type signing git/config signs commits with that same key, so it is needed either way. Re-run this script afterwards. MSG exit 1 fi ssh-keyscan -t rsa,ecdsa,ed25519 github.com 2>/dev/null >> "$HOME/.ssh/known_hosts" sort -u -o "$HOME/.ssh/known_hosts" "$HOME/.ssh/known_hosts" log "dotfiles -> $CONFIG" if [ -d "$CONFIG/.git" ]; then git -C "$CONFIG" pull --ff-only else # ~/.config is never empty by now (gh just wrote its own config there), so # clone bare-ish elsewhere, move the .git in, and check out over the top. tmp="$(mktemp -d)/dotfiles" git clone --no-checkout "git@github.com:$REPO.git" "$tmp" mkdir -p "$CONFIG" mv "$tmp/.git" "$CONFIG/.git" rm -rf "$tmp" git -C "$CONFIG" checkout -f main fi log "handing over to install.sh" exec "$CONFIG/install.sh"