Skip to content

Instantly share code, notes, and snippets.

@milichev
Last active February 24, 2026 14:59
Show Gist options
  • Select an option

  • Save milichev/dfb109f78a953493a9df1a8ee25a1fd0 to your computer and use it in GitHub Desktop.

Select an option

Save milichev/dfb109f78a953493a9df1a8ee25a1fd0 to your computer and use it in GitHub Desktop.

config-bootstrap.sh: [almost] unattended MacBook installation

Usage

Run in Terminal:

bash <(curl -fsSL https://gist.githubusercontent.com/milichev/dfb109f78a953493a9df1a8ee25a1fd0/raw/config-bootstrap.sh)

Enter the sudo password when asked.

#!/usr/bin/env bash
# Redirect stdin from the terminal to allow interactivity
exec 0</dev/tty
# Bootstrap script for vadymecum-config
#
# Usage:
# bash <(curl -fsSL https://gist.githubusercontent.com/milichev/dfb109f78a953493a9df1a8ee25a1fd0/raw/config-bootstrap.sh)
set -euo pipefail
REPO_OWNER="milichev"
REPO_NAME="vadymecum-config"
REPO_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}"
TARGET_DIR="$HOME/Dev/github/${REPO_OWNER}"
REPO_DIR="${TARGET_DIR}/${REPO_NAME}"
SCRIPTS_DIR="${REPO_DIR}/Scripts"
# ── helpers ───────────────────────────────────────────────────────────────────
info() { printf '\033[0;34m➜ %s\033[0m\n' "$*"; }
success() { printf '\033[0;32m✅ %s\033[0m\n' "$*"; }
warn() { printf '\033[0;33m⚠️ %s\033[0m\n' "$*"; }
die() { printf '\033[0;31m❌ %s\033[0m\n' "$*" >&2; exit 1; }
confirm() {
local prompt="${1:-Continue?}"
local answer
# When piped from curl, stdin is not a tty — re-open /dev/tty explicitly
read -r -p "$(printf '\033[0;36m%s (Y/n): \033[0m' "$prompt")" answer </dev/tty
[[ -z "$answer" || "$answer" =~ ^[yY]$ ]]
}
# ── prerequisites ─────────────────────────────────────────────────────────────
ensure_xcode_clt() {
# git --version returns non-zero if only the shim exists
if xcode-select -p &>/dev/null && git --version &>/dev/null; then
success "Xcode CLT: $(xcode-select -p)"
return
fi
info "Xcode Command Line Tools not found. Requesting installation..."
# This triggers the GUI popup
xcode-select --install 2>/dev/null || true
sleep 2
# Bring the installer window to the front so it's not missed
osascript -e 'tell application "Install Command Line Developer Tools" to activate' 2>/dev/null || true
local attempts=0
while ! git --version &>/dev/null; do
(( attempts++ ))
if (( attempts > 60 )); then
die "Xcode CLT installation timed out (10 mins). Install manually."
fi
warn "Waiting for GUI installer... (attempt $attempts/60)"
sleep 10
done
success "Xcode CLT installed and functional."
}
ensure_homebrew() {
if command -v brew &>/dev/null; then
success "Homebrew: $(brew --version | head -1)"
else
info "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" < /dev/tty
fi
# If HOMEBREW_PREFIX is not set, we need to configure the environment
if [[ -z "${HOMEBREW_PREFIX:-}" ]]; then
local brew_prefix
brew_prefix="$( [[ -d /opt/homebrew ]] && echo /opt/homebrew || echo /usr/local )"
local brew_exe="$brew_prefix/bin/brew"
local shellenv_cmd="eval \"\$($brew_exe shellenv)\""
for profile in "$HOME/.zshrc" "$HOME/.bash_profile"; do
touch "$profile"
if ! grep -qF "brew shellenv" "$profile"; then
printf '\n# Homebrew initialization\n%s\n' "$shellenv_cmd" >> "$profile"
info "Added brew shellenv to $profile"
else
info "brew shellenv already present in $profile"
fi
done
# Apply to current session
eval "$shellenv_cmd"
eval "$("$brew_exe" shellenv)"
fi
# Finalize: ensure bash is modern and installed
info "Updating Homebrew and installing modern bash..."
brew update
brew install bash
local brew_bash="${HOMEBREW_PREFIX}/bin/bash"
success "Homebrew installed and persistent. bash: $($brew_bash --version | head -1)"
}
# ── main ──────────────────────────────────────────────────────────────────────
main() {
printf '\n\033[1;37m╔══════════════════════════════════════════╗\033[0m\n'
printf '\033[1;37m║ vadymecum-config bootstrap ║\033[0m\n'
printf '\033[1;37m╚══════════════════════════════════════════╝\033[0m\n\n'
ensure_xcode_clt
ensure_homebrew
# Offer Bitwarden vault before git operations that may need GitHub credentials
printf '\n'
if confirm "Open Bitwarden vault for GitHub credentials?"; then
info "Opening Bitwarden vault (Favorites)..."
open "https://vault.bitwarden.com/#/vault?type=favorites"
confirm "Ready to continue?" || die "Aborted by user."
fi
# Clone or update
if [[ -d "$REPO_DIR/.git" ]]; then
warn "Repo already exists at $REPO_DIR"
if confirm "Pull latest changes?"; then
info "Pulling ${REPO_URL}..."
git -C "$REPO_DIR" pull --ff-only
success "Repo updated."
fi
else
info "Creating $TARGET_DIR..."
mkdir -p "$TARGET_DIR"
info "Cloning ${REPO_URL}..."
git clone "$REPO_URL" "$REPO_DIR"
success "Cloned into $REPO_DIR"
fi
cd "$SCRIPTS_DIR" || die "Scripts dir not found: $SCRIPTS_DIR"
success "Working directory: $(pwd)"
printf '\n'
if confirm "Run install-macbook.sh in a new terminal window?"; then
info "Spawning new terminal session..."
osascript <<EOF
tell application "Terminal"
do script "cd '$SCRIPTS_DIR' && ./install-macbook.sh"
activate
set zoomed of window 1 to true
end tell
EOF
success "Installer launched in a new window. You can close this bootstrap window."
else
printf '\n\033[0;33mTo run manually in a new tab:\033[0m\n'
printf ' cd %s\n' "$SCRIPTS_DIR"
printf ' ./install-macbook.sh\n\n'
fi
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment