#!/usr/bin/env bash # Prepare a machine to run rig, and say plainly what worked, what was already # done, and what is left for a human. # # This is the grouped entry point: `make setup`. Every step is idempotent and # independently checked, so running it twice is safe and running it on a # half-configured machine finishes the job rather than starting over. # # It deliberately does NOT abort on the first failure. A setup script that dies # at step 2 hides the fact that steps 4 and 5 were also going to fail — and on # an unfamiliar machine, the full picture is the whole point. Failures are # collected and reported together, and the exit code reflects the worst outcome. # # The same script runs inside a fresh throwaway distro (newbox), so the # provisioning path and the everyday path cannot drift apart. # # Usage: # setup.sh # host checks + the dev toolchain # setup.sh core # kubectl and jq only — no cluster tooling # setup.sh --share-docker # ...and offer this distro's Docker to others # setup.sh --cluster # ...and bring the cluster up set -euo pipefail cd "$(dirname "$0")" source ./lib/config.sh load_config WITH_SHARE=0 WITH_CLUSTER=0 # Cluster tooling is not wanted everywhere: a managed or corporate-issued # machine may legitimately want kubectl and nothing that builds clusters. TIER=dev for a in "$@"; do case "$a" in core|dev) TIER="$a" ;; --share-docker) WITH_SHARE=1 ;; --cluster) WITH_CLUSTER=1 ;; *) echo "unknown option: $a" >&2; exit 1 ;; esac done if [ "$TIER" = "core" ] && [ "$WITH_CLUSTER" -eq 1 ]; then echo "core tier installs no cluster tooling, so --cluster cannot work" >&2 exit 1 fi # ── step framework ───────────────────────────────────────────────────────── # Statuses are deliberately distinct: "already" and "done" both mean success but # tell you very different things about the machine you are on. STEP_NAMES=() STEP_STATUS=() STEP_NOTE=() WORST=0 record() { STEP_NAMES+=("$1"); STEP_STATUS+=("$2"); STEP_NOTE+=("${3:-}") # Only a genuine failure is a non-zero exit. "manual" means the machine is # fine and you have something to do — reporting that as an error makes the # whole run look broken and trains people to ignore the output. [ "$2" = "fail" ] && WORST=1 || true local mark case "$2" in already) mark=" ok " ;; done) mark=" done " ;; skip) mark=" skip " ;; manual) mark="MANUAL" ;; fail) mark=" FAIL " ;; esac printf "[%s] %-22s %s\n" "$mark" "$1" "${3:-}" } # ── steps ────────────────────────────────────────────────────────────────── step_host() { local out if ! out=$(bash ./deps.sh detect 2>&1); then record host fail "detection failed" return fi # Anything flagged with '!' needs a human; surface the count here # and the detail below rather than burying it. local warns; warns=$(echo "$out" | grep -c '^\s*!' || true) HOST_DETAIL="$out" if [ "$warns" -gt 0 ]; then record host manual "$warns item(s) need attention — see below" else record host already "no problems detected" fi } step_toolchain() { local want="kubectl jq" [ "$TIER" = "dev" ] && want="$want kind tilt" local missing="" for b in $want; do command -v "$b" >/dev/null 2>&1 || missing="$missing $b" done if [ -z "$missing" ]; then record toolchain already "$TIER: $want" return fi if bash ./deps.sh install "$TIER" >/tmp/rig-deps.$$ 2>&1; then local still="" for b in $want; do [ -x "${OUT_BIN:-$HOME/.local/bin}/$b" ] || still="$still $b" done if [ -n "$still" ]; then record toolchain fail "still missing:$still (see /tmp/rig-deps.$$)" else record toolchain done "$TIER, installed:$missing" rm -f "/tmp/rig-deps.$$" fi else record toolchain fail "install failed — see /tmp/rig-deps.$$" fi } step_path() { local bin="${OUT_BIN:-$HOME/.local/bin}" case ":$PATH:" in *":$bin:"*) ;; *) record path manual "add to ~/.bashrc: export PATH=\"$bin:\$PATH\""; return ;; esac if grep -qs "$bin" "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null; then record path already "$bin on PATH and persisted" else record path manual "on PATH now, but not persisted in ~/.bashrc" fi } step_docker() { if ! command -v docker >/dev/null 2>&1; then record docker fail "no docker cli — this is the one prerequisite rig cannot install" return fi if docker info >/dev/null 2>&1; then record docker already "$(docker version --format '{{.Server.Version}}' 2>/dev/null)" else record docker fail "daemon unreachable (in the docker group? logged out and back in?)" fi } step_share_docker() { if [ "$WITH_SHARE" -ne 1 ]; then record docker-share skip "not requested (--share-docker)" return fi if ! grep -qi microsoft /proc/version 2>/dev/null; then record docker-share skip "not WSL — sharing only applies between WSL distros" return fi if [ -f /etc/systemd/system/docker.service.d/10-rig-shared-socket.conf ]; then record docker-share already "this distro is offering its Docker to others" return fi # Needs root, and asking mid-script is worse than telling the user the # single command to run. if [ "$(id -u)" -ne 0 ] && ! sudo -n true 2>/dev/null; then record docker-share manual "run: sudo bash ctrl/dockerhost.sh share" return fi if sudo bash ./dockerhost.sh share >/tmp/rig-share.$$ 2>&1; then record docker-share done "this distro now owns the shared Docker" rm -f "/tmp/rig-share.$$" else record docker-share fail "see /tmp/rig-share.$$" fi } step_ports() { local busy="" for entry in "HTTP:$HTTP_PORT" "HTTPS:$HTTPS_PORT" "TILT:$TILT_PORT" "REGISTRY:$REGISTRY_PORT"; do local p="${entry#*:}" if command -v ss >/dev/null 2>&1 && ss -ltn "sport = :$p" 2>/dev/null | grep -q LISTEN; then busy="$busy ${entry%%:*}($p)" fi done if [ -n "$busy" ]; then record ports fail "in use:$busy — override in ctrl/.env or rename the directory" else record ports already "$HTTP_PORT-$REGISTRY_PORT free" fi } step_cluster() { if [ "$TIER" = "core" ]; then record cluster skip "core tier — no cluster tooling on this machine" return fi if [ "$WITH_CLUSTER" -ne 1 ]; then record cluster skip "not requested (--cluster)" return fi if kind get clusters 2>/dev/null | grep -qx "$CLUSTER"; then record cluster already "'$CLUSTER' exists" return fi if bash ./cluster.sh up >/tmp/rig-cluster.$$ 2>&1; then record cluster done "'$CLUSTER' created" rm -f "/tmp/rig-cluster.$$" else record cluster fail "see /tmp/rig-cluster.$$" fi } # ── run ──────────────────────────────────────────────────────────────────── echo "setting up '$CLUSTER'" echo HOST_DETAIL="" step_host step_toolchain step_path step_docker step_share_docker step_ports step_cluster echo if [ -n "$HOST_DETAIL" ]; then echo "host detail" echo "$HOST_DETAIL" | sed 's/^/ /' echo fi # Repeat only what still needs action, so the tail of the output is a to-do list # rather than a transcript. outstanding=0 for i in "${!STEP_NAMES[@]}"; do case "${STEP_STATUS[$i]}" in fail|manual) [ "$outstanding" -eq 0 ] && echo "outstanding:" outstanding=1 printf " %-8s %-16s %s\n" "${STEP_STATUS[$i]}" "${STEP_NAMES[$i]}" "${STEP_NOTE[$i]}" ;; esac done if [ "$outstanding" -eq 0 ]; then echo "ready. next: make cluster up && make docs" else echo echo "(nothing was aborted — every step ran so the list above is complete)" fi exit "$WORST"