#!/usr/bin/env bash set -euo pipefail # When run via curl-pipe-to-bash, stdin is the script stream, not the terminal. # Reconnect stdin to the terminal so interactive prompts work. if [ ! -t 0 ]; then exec < /dev/tty fi MACHINE_TYPE="${1:-personal}" COMPUTER_NAME="${2:-$(hostname -s)}" DOTFILES_DIR="" # Validate machine type if [[ "$MACHINE_TYPE" != "personal" && "$MACHINE_TYPE" != "work" ]]; then echo "ERROR: Machine type must be 'personal' or 'work', got '$MACHINE_TYPE'" >&2 exit 1 fi echo "==> Setting up $MACHINE_TYPE machine: $COMPUTER_NAME" # 1. Enable Touch ID for sudo early (so the rest of the script can use it) if [ ! -f /etc/pam.d/sudo_local ]; then echo "==> Enabling Touch ID for sudo..." sudo sh -c 'echo "auth sufficient pam_tid.so" > /etc/pam.d/sudo_local' fi # Keep sudo alive for the duration of the script sudo -v while true; do sudo -n true; sleep 55; kill -0 "$$" || exit; done 2>/dev/null & # 2. Create Developer directory mkdir -p ~/Developer # 3. Install Homebrew if not present if ! command -v brew &>/dev/null; then echo "==> Installing Homebrew..." NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" eval "$(/opt/homebrew/bin/brew shellenv)" fi # 3. Install Rosetta (for Apple Silicon) if [[ "$(uname -m)" == "arm64" ]]; then sudo softwareupdate --install-rosetta --agree-to-license 2>/dev/null || true fi # 4. Ensure GitHub auth (needed for private repo clone) if ! command -v gh &>/dev/null; then brew install gh fi if ! gh auth status &>/dev/null; then echo "==> GitHub authentication required to clone dotfiles..." gh auth login fi gh auth setup-git # 5. Install chezmoi and apply dotfiles echo "==> Installing chezmoi and applying dotfiles..." if ! command -v chezmoi &>/dev/null; then brew install chezmoi fi if [ ! -d "$(chezmoi source-path 2>/dev/null)" ] || [ -z "$(ls -A "$(chezmoi source-path)" 2>/dev/null)" ]; then # First run: prompt for identity and pre-populate chezmoi data read -rp "Full name [Darlin Alberto]: " FULL_NAME FULL_NAME="${FULL_NAME:-Darlin Alberto}" read -rp "Git email: " GIT_EMAIL if [ -z "$GIT_EMAIL" ]; then echo "ERROR: Git email is required." >&2 exit 1 fi mkdir -p ~/.config/chezmoi cat > ~/.config/chezmoi/chezmoi.yaml << CHEZEOF data: name: "$FULL_NAME" email: "$GIT_EMAIL" machine_type: "$MACHINE_TYPE" CHEZEOF chezmoi init --apply dalberto else # Re-run: pull latest and apply chezmoi update --apply fi DOTFILES_DIR="$(chezmoi source-path)" if [ ! -d "$DOTFILES_DIR" ]; then echo "ERROR: chezmoi source directory not found at $DOTFILES_DIR" >&2 exit 1 fi # Symlink to ~/Developer/dotfiles for convenience if [ ! -e "$HOME/Developer/dotfiles" ]; then mkdir -p "$HOME/Developer" ln -sf "$DOTFILES_DIR" "$HOME/Developer/dotfiles" fi # 6. Install packages via Homebrew cd "$DOTFILES_DIR" echo "==> Installing shared packages (Brewfile)..." brew bundle --no-upgrade --file=Brewfile if [ -f "Brewfile.$MACHINE_TYPE" ]; then echo "==> Installing $MACHINE_TYPE packages (Brewfile.$MACHINE_TYPE)..." brew bundle --no-upgrade --file="Brewfile.$MACHINE_TYPE" fi # 7. Install mise-managed tools (Node, Claude Code, etc.) if command -v mise &>/dev/null; then echo "==> Installing mise-managed tools..." mise install --yes 2>/dev/null || true fi # 8. Set Fish as default shell FISH_PATH="$(command -v fish || echo /opt/homebrew/bin/fish)" if ! grep -q "$FISH_PATH" /etc/shells; then echo "$FISH_PATH" | sudo tee -a /etc/shells fi if [ "$SHELL" != "$FISH_PATH" ]; then chsh -s "$FISH_PATH" fi # 8. Install Fisher and fish plugins echo "==> Installing Fisher and fish plugins..." fish -c "curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher" 2>/dev/null || true fish -c "fisher update" 2>/dev/null || true # 9. Install fishmarks if [ ! -d ~/.fishmarks ]; then echo "==> Installing fishmarks..." git clone https://github.com/techwizrd/fishmarks.git ~/.fishmarks fi # 10. Set up Espanso scripts venv if command -v uv &>/dev/null && [ -f ~/.config/espanso/scripts/pyproject.toml ]; then echo "==> Setting up Espanso scripts venv..." (cd ~/.config/espanso/scripts && uv venv && uv sync 2>/dev/null || true) fi # 11. Build bat cache if command -v bat &>/dev/null; then bat cache --build 2>/dev/null || true fi # 12. Disable Spotlight indexing on ~/Developer echo "==> Disabling Spotlight indexing on ~/Developer..." sudo mdutil -i off ~/Developer 2>/dev/null || true # Mark setup as done (for idempotency checks) touch ~/.dotfiles-setup-done echo "" echo "==> Setup complete!" echo "" echo " Remaining manual steps:" STEPS_FILE="$DOTFILES_DIR/MANUAL_STEPS.md" if [ -f "$STEPS_FILE" ]; then sed -n '/^## After bootstrap/,$ { /^## /d; /^$/d; s/^/ /; p; }' "$STEPS_FILE" else echo " See MANUAL_STEPS.md in the dotfiles repo." fi # 13. Run macOS defaults LAST (kills Terminal/Finder/Dock — must be final step) if [[ "${3:-}" == "--defaults" ]] || [[ ! -f ~/.dotfiles-defaults-done ]]; then echo "" echo "==> Applying macOS defaults (this will restart Finder, Dock, and Terminal)..." bash "$DOTFILES_DIR/.macos" "$COMPUTER_NAME" touch ~/.dotfiles-defaults-done else echo "==> Skipping macOS defaults (already applied). Pass --defaults as 3rd arg to re-run." fi