#!/usr/bin/env bash # # get.fabickcat.ai/ansible — stage -1 bootstrap for a fresh macOS machine. # # curl -fsSL https://get.fabickcat.ai/ansible | bash # curl -fsSL https://get.fabickcat.ai/ansible | bash -s -- --check --diff # (fallback while DNS verifies: https://fabick-get.pages.dev/ansible) # # Gets a fresh mac to where the repo's own bootstrap.sh can take over: # Homebrew -> git-credential-manager -> clone/update ~/ansible -> exec # ./bootstrap.sh (extra args pass through to ansible-playbook). Idempotent; # safe to re-run. Nothing here needs sudo except the installers' own prompts. # # Wrapped in main() called on the LAST line: bash parses the whole function # before running it, so a truncated download executes nothing, and child # processes can't slurp unread script bytes off the stdin pipe. set -euo pipefail REPO_URL="https://fabickcat.visualstudio.com/FabickCatOpenClaw/_git/ansible" REPO_DIR="${HOME}/ansible" say() { printf '==> %s\n' "$*"; } fail() { printf 'error: %s\n' "$*" >&2; exit 1; } # Find brew even when the current shell lacks its shellenv (fresh machine, # fresh install): check PATH, then both standard prefixes. load_brew() { command -v brew >/dev/null 2>&1 && return 0 local b for b in /opt/homebrew/bin/brew /usr/local/bin/brew; do if [[ -x "$b" ]]; then eval "$("$b" shellenv)" return 0 fi done return 1 } # 1 when the process can actually OPEN the controlling terminal. A real open() # attempt — permission bits ([[ -r /dev/tty ]]) false-pass in cron/CI/agent # shells that have no controlling terminal. HAVE_TTY=0 # Run a child with the real terminal on stdin when we have one (sudo and # sign-in prompts read it); headless, run it plainly — the prompt-needing # steps were already refused up front in main(). run_tty() { if [[ "$HAVE_TTY" -eq 1 ]]; then "$@" /dev/null; then HAVE_TTY=1; fi # Under `curl | bash` stdin is the pipe, so prompts must read /dev/tty. Fail # up front — not mid-flight — when a step that WILL prompt lies ahead and no # terminal is available. Headless bootstrap of a FRESH machine is out of # scope (see docs/cloudflare-access.md in the repo); headless re-runs on an # already-provisioned machine prompt for nothing and are fine. if [[ "$HAVE_TTY" -ne 1 ]]; then load_brew \ || fail "no usable /dev/tty and Homebrew is not installed — its installer prompts for your password. Run from an interactive terminal." command -v git-credential-manager >/dev/null 2>&1 \ || fail "no usable /dev/tty and git-credential-manager is not installed — its .pkg prompts for your password. Run from an interactive terminal." [[ -e "$REPO_DIR" ]] \ || fail "no usable /dev/tty and ${REPO_DIR} does not exist — the first clone opens an Entra sign-in. Run from an interactive terminal." fi if ! load_brew; then say "Installing Homebrew (you will be prompted for your macOS password)..." run_tty /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" load_brew || fail "Homebrew installation did not complete." fi # git-credential-manager must exist BEFORE the private clone; its .pkg # postinstall runs `git-credential-manager configure`. git itself ships with # the Xcode CLT that the Homebrew installer ensures. if ! command -v git-credential-manager >/dev/null 2>&1; then say "Installing git-credential-manager (the .pkg may prompt for your password)..." run_tty brew install --cask git-credential-manager fi if [[ ! -e "$REPO_DIR" ]]; then say "Cloning ${REPO_URL} -> ${REPO_DIR} (a browser sign-in may open)..." run_tty git clone "$REPO_URL" "$REPO_DIR" elif [[ -d "${REPO_DIR}/.git" ]]; then case "$(git -C "$REPO_DIR" remote get-url origin 2>/dev/null)" in *FabickCatOpenClaw*ansible) # matches both the HTTPS and SSH remote forms say "Updating existing ${REPO_DIR}..." run_tty git -C "$REPO_DIR" pull --ff-only \ || fail "could not fast-forward ${REPO_DIR} — resolve local changes there (or re-authenticate), then re-run." ;; *) fail "${REPO_DIR} exists but is not this ansible repo — move it aside, then re-run." ;; esac else fail "${REPO_DIR} exists but is not a git repo — move it aside, then re-run." fi say "Handing off to ${REPO_DIR}/bootstrap.sh ${*:-(no args)}" cd "$REPO_DIR" if [[ "$HAVE_TTY" -eq 1 ]]; then exec ./bootstrap.sh "$@"