data17 path and postgresql17 package refs were never updated when PG was upgraded to 18. Fixes setup scripts, skills, docs, tests, and archived playbooks to match the running system (PG 18.3, /var/db/postgres/data). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
280 lines
7.1 KiB
Bash
Executable file
280 lines
7.1 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# setup.sh — Bootstrap script for Clawdie AI
|
|
# Handles Node.js/npm setup, then hands off to the Node.js setup modules.
|
|
# This is the only bash script in the setup flow.
|
|
|
|
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
LOG_FILE="$PROJECT_ROOT/logs/setup.log"
|
|
|
|
# shellcheck source=scripts/date-format.sh
|
|
. "$PROJECT_ROOT/scripts/date-format.sh"
|
|
|
|
mkdir -p "$PROJECT_ROOT/logs"
|
|
|
|
log() { echo "[$(format_display_timestamp_now)] [bootstrap] $*" >> "$LOG_FILE"; }
|
|
|
|
# --- Platform detection ---
|
|
|
|
detect_platform() {
|
|
local uname_s
|
|
uname_s=$(uname -s)
|
|
case "$uname_s" in
|
|
FreeBSD*) PLATFORM="freebsd" ;;
|
|
*)
|
|
echo "ERROR: Clawdie AI requires FreeBSD. Detected: $uname_s" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
IS_ROOT="false"
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
IS_ROOT="true"
|
|
fi
|
|
|
|
log "Platform: $PLATFORM, Root: $IS_ROOT"
|
|
}
|
|
|
|
# --- Host package baseline ---
|
|
|
|
install_host_pkg_baseline() {
|
|
HOST_PKG_BASELINE_STATUS="skipped"
|
|
HOST_PKG_BASELINE_MISSING=""
|
|
HOST_PKG_BASELINE_INSTALL_CMD="none"
|
|
|
|
[ "$PLATFORM" = "freebsd" ] || return
|
|
|
|
local -a missing_pkgs=()
|
|
local package_file="$PROJECT_ROOT/infra/packages/host-baseline.txt"
|
|
local pkg
|
|
|
|
while IFS= read -r pkg; do
|
|
[ -n "$pkg" ] || continue
|
|
case "$pkg" in
|
|
\#*) continue ;;
|
|
node24)
|
|
command -v node >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
npm)
|
|
command -v npm >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
bsddialog)
|
|
command -v bsddialog >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
bastille)
|
|
command -v bastille >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
git)
|
|
command -v git >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
tmux)
|
|
command -v tmux >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
python311)
|
|
command -v python3 >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
uv)
|
|
command -v uv >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
ripgrep)
|
|
command -v rg >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
fd)
|
|
command -v fd >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
rsync)
|
|
command -v rsync >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
postgresql18-client)
|
|
command -v psql >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
rust)
|
|
command -v rustc >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
py311-pillow)
|
|
python3 -c "import PIL" >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
dejavu)
|
|
[ -f /usr/local/share/fonts/dejavu/DejaVuSansMono.ttf ] || missing_pkgs+=("$pkg")
|
|
;;
|
|
*)
|
|
command -v "$pkg" >/dev/null 2>&1 || missing_pkgs+=("$pkg")
|
|
;;
|
|
esac
|
|
done < "$package_file"
|
|
|
|
if [ "${#missing_pkgs[@]}" -eq 0 ]; then
|
|
HOST_PKG_BASELINE_STATUS="already_present"
|
|
log "Host package baseline already present"
|
|
return
|
|
fi
|
|
|
|
HOST_PKG_BASELINE_MISSING="${missing_pkgs[*]}"
|
|
|
|
if [ "$IS_ROOT" = "true" ]; then
|
|
HOST_PKG_BASELINE_INSTALL_CMD="pkg install -y ${HOST_PKG_BASELINE_MISSING}"
|
|
elif command -v sudo >/dev/null 2>&1; then
|
|
HOST_PKG_BASELINE_INSTALL_CMD="sudo pkg install -y ${HOST_PKG_BASELINE_MISSING}"
|
|
else
|
|
HOST_PKG_BASELINE_STATUS="missing_no_sudo"
|
|
log "Host package baseline missing and sudo unavailable: ${HOST_PKG_BASELINE_MISSING}"
|
|
return
|
|
fi
|
|
|
|
log "Installing missing host package baseline: ${HOST_PKG_BASELINE_MISSING}"
|
|
if $HOST_PKG_BASELINE_INSTALL_CMD >> "$LOG_FILE" 2>&1; then
|
|
HOST_PKG_BASELINE_STATUS="installed"
|
|
log "Host package baseline installed successfully"
|
|
else
|
|
HOST_PKG_BASELINE_STATUS="install_failed"
|
|
log "Host package baseline installation failed"
|
|
fi
|
|
}
|
|
|
|
# --- Node.js check ---
|
|
|
|
check_node() {
|
|
NODE_OK="false"
|
|
NODE_VERSION="not_found"
|
|
NODE_PATH_FOUND=""
|
|
|
|
if command -v node >/dev/null 2>&1; then
|
|
NODE_VERSION=$(node --version 2>/dev/null | sed 's/^v//')
|
|
NODE_PATH_FOUND=$(command -v node)
|
|
local major
|
|
major=$(echo "$NODE_VERSION" | cut -d. -f1)
|
|
if [ "$major" -ge 24 ] 2>/dev/null; then
|
|
NODE_OK="true"
|
|
fi
|
|
log "Node $NODE_VERSION at $NODE_PATH_FOUND (major=$major, ok=$NODE_OK)"
|
|
else
|
|
log "Node not found"
|
|
fi
|
|
}
|
|
|
|
# --- bsddialog check ---
|
|
|
|
check_bsddialog() {
|
|
BSDDIALOG_OK="false"
|
|
BSDDIALOG_PATH="not_found"
|
|
|
|
if command -v bsddialog >/dev/null 2>&1; then
|
|
BSDDIALOG_OK="true"
|
|
BSDDIALOG_PATH=$(command -v bsddialog)
|
|
fi
|
|
|
|
log "bsddialog: $BSDDIALOG_OK (${BSDDIALOG_PATH:-not_found})"
|
|
}
|
|
|
|
# --- npm install ---
|
|
|
|
install_deps() {
|
|
DEPS_OK="false"
|
|
|
|
if [ "$NODE_OK" = "false" ]; then
|
|
log "Skipping npm install — Node not available"
|
|
return
|
|
fi
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
# npm install with --unsafe-perm if root (needed for native modules)
|
|
local npm_flags=""
|
|
if [ "$IS_ROOT" = "true" ]; then
|
|
npm_flags="--unsafe-perm"
|
|
log "Running as root, using --unsafe-perm"
|
|
fi
|
|
|
|
log "Running npm install $npm_flags"
|
|
if npm install $npm_flags >> "$LOG_FILE" 2>&1; then
|
|
DEPS_OK="true"
|
|
log "npm install succeeded"
|
|
else
|
|
log "npm install failed"
|
|
return
|
|
fi
|
|
|
|
}
|
|
|
|
# --- Build tools check ---
|
|
|
|
check_build_tools() {
|
|
HAS_BUILD_TOOLS="false"
|
|
|
|
if command -v cc >/dev/null 2>&1 && command -v make >/dev/null 2>&1; then
|
|
HAS_BUILD_TOOLS="true"
|
|
fi
|
|
|
|
log "Build tools: $HAS_BUILD_TOOLS"
|
|
}
|
|
|
|
# --- Main ---
|
|
|
|
log "=== Bootstrap started ==="
|
|
|
|
detect_platform
|
|
install_host_pkg_baseline
|
|
check_node
|
|
check_bsddialog
|
|
install_deps
|
|
check_build_tools
|
|
|
|
# Emit status block
|
|
STATUS="success"
|
|
if [ "$HOST_PKG_BASELINE_STATUS" = "missing_no_sudo" ] || [ "$HOST_PKG_BASELINE_STATUS" = "install_failed" ]; then
|
|
STATUS="host_packages_failed"
|
|
elif [ "$NODE_OK" = "false" ]; then
|
|
STATUS="node_missing"
|
|
elif [ "$DEPS_OK" = "false" ]; then
|
|
STATUS="deps_failed"
|
|
fi
|
|
|
|
cat <<EOF
|
|
=== CLAWDIE SETUP: BOOTSTRAP ===
|
|
PLATFORM: $PLATFORM
|
|
IS_ROOT: $IS_ROOT
|
|
HOST_PKG_BASELINE_STATUS: $HOST_PKG_BASELINE_STATUS
|
|
HOST_PKG_BASELINE_MISSING: ${HOST_PKG_BASELINE_MISSING:-none}
|
|
HOST_PKG_BASELINE_INSTALL_CMD: ${HOST_PKG_BASELINE_INSTALL_CMD:-none}
|
|
NODE_VERSION: $NODE_VERSION
|
|
NODE_OK: $NODE_OK
|
|
NODE_PATH: ${NODE_PATH_FOUND:-not_found}
|
|
DEPS_OK: $DEPS_OK
|
|
HAS_BUILD_TOOLS: $HAS_BUILD_TOOLS
|
|
BSDDIALOG_OK: $BSDDIALOG_OK
|
|
BSDDIALOG_PATH: ${BSDDIALOG_PATH:-not_found}
|
|
STATUS: $STATUS
|
|
LOG: logs/setup.log
|
|
=== END ===
|
|
EOF
|
|
|
|
log "=== Bootstrap completed: $STATUS ==="
|
|
|
|
if [ "$HOST_PKG_BASELINE_STATUS" = "missing_no_sudo" ] || [ "$HOST_PKG_BASELINE_STATUS" = "install_failed" ]; then
|
|
exit 1
|
|
fi
|
|
if [ "$NODE_OK" = "false" ]; then
|
|
exit 2
|
|
fi
|
|
if [ "$DEPS_OK" = "false" ]; then
|
|
exit 1
|
|
fi
|
|
|
|
# Wire tracked git hooks (idempotent)
|
|
git config core.hooksPath hooks/ 2>/dev/null || true
|
|
log "Git hooks path set to hooks/"
|
|
|
|
# Detect host locale silently — reads $LANG / locale / ~/.login_conf from the FreeBSD install
|
|
# Falls back to en-US on neutral/cloud environments. No prompt needed.
|
|
log "Detecting host locale"
|
|
npx tsx setup/index.ts --step profile --accept-detected >> "$LOG_FILE" 2>&1 || true
|
|
|
|
if [ "${CLAWDIE_SKIP_ONBOARDING:-0}" != "1" ] \
|
|
&& [ -t 0 ] \
|
|
&& [ -t 1 ]; then
|
|
log "Launching interactive onboarding"
|
|
echo
|
|
echo "Launching interactive onboarding..."
|
|
npm run wizard
|
|
fi
|