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 |