Files
nanoclaw/setup.sh
T
Koshkoshinsk 77fec6c7c3 fix(new-setup): avoid double-bootstrap and corepack EACCES on system Node
Two fixes to the fresh-install path:

1. setup.sh: when `corepack enable` runs as a non-root user against a
   system-wide Node install (apt-installed to /usr/bin), it fails EACCES
   trying to symlink /usr/bin/pnpm, leaving pnpm off PATH. Retry with
   sudo when pnpm is still missing — gated to Linux/WSL so macOS
   Homebrew prefixes aren't polluted with root-owned shims.

2. SKILL.md step 1: if the probe reports STATUS: unavailable (Node not
   installed), install Node BEFORE invoking `bash setup.sh`. The old
   flow ran setup.sh first as a diagnostic, which always failed fast,
   installed Node, then re-ran — two bootstraps for no reason.

Combined: fresh Linux box now goes Node install -> single setup.sh run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:37:03 +00:00

177 lines
4.6 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# setup.sh — Bootstrap script for NanoClaw
# Handles Node.js/pnpm 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"
mkdir -p "$PROJECT_ROOT/logs"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [bootstrap] $*" >> "$LOG_FILE"; }
# --- Platform detection ---
detect_platform() {
local uname_s
uname_s=$(uname -s)
case "$uname_s" in
Darwin*) PLATFORM="macos" ;;
Linux*) PLATFORM="linux" ;;
*) PLATFORM="unknown" ;;
esac
IS_WSL="false"
if [ "$PLATFORM" = "linux" ] && [ -f /proc/version ]; then
if grep -qi 'microsoft\|wsl' /proc/version 2>/dev/null; then
IS_WSL="true"
fi
fi
IS_ROOT="false"
if [ "$(id -u)" -eq 0 ]; then
IS_ROOT="true"
fi
log "Platform: $PLATFORM, WSL: $IS_WSL, Root: $IS_ROOT"
}
# --- 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 20 ] 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
}
# --- pnpm install ---
install_deps() {
DEPS_OK="false"
NATIVE_OK="false"
if [ "$NODE_OK" = "false" ]; then
log "Skipping pnpm install — Node not available"
return
fi
cd "$PROJECT_ROOT"
# Enable corepack so `pnpm` shim lands on PATH.
log "Enabling corepack"
corepack enable >> "$LOG_FILE" 2>&1 || true
# On Linux/WSL with system-wide Node (e.g. apt-installed to /usr/bin),
# corepack needs root to symlink /usr/bin/pnpm. Retry with sudo when pnpm
# isn't on PATH. macOS Homebrew installs land in a user-writable prefix,
# and a sudo retry there would create root-owned shims inside /opt/homebrew
# that later break brew — so the retry is Linux-only.
if ! command -v pnpm >/dev/null 2>&1 && [ "$PLATFORM" = "linux" ] \
&& command -v sudo >/dev/null 2>&1; then
log "pnpm not on PATH after corepack enable — retrying with sudo"
sudo corepack enable >> "$LOG_FILE" 2>&1 || true
fi
log "Running pnpm install --frozen-lockfile"
if pnpm install --frozen-lockfile >> "$LOG_FILE" 2>&1; then
DEPS_OK="true"
log "pnpm install succeeded"
else
log "pnpm install failed"
return
fi
# Verify native module (better-sqlite3)
log "Verifying native modules"
if node -e "require('better-sqlite3')" >> "$LOG_FILE" 2>&1; then
NATIVE_OK="true"
log "better-sqlite3 loads OK"
else
log "better-sqlite3 failed to load"
fi
}
# --- Build tools check ---
check_build_tools() {
HAS_BUILD_TOOLS="false"
if [ "$PLATFORM" = "macos" ]; then
if xcode-select -p >/dev/null 2>&1; then
HAS_BUILD_TOOLS="true"
fi
elif [ "$PLATFORM" = "linux" ]; then
if command -v gcc >/dev/null 2>&1 && command -v make >/dev/null 2>&1; then
HAS_BUILD_TOOLS="true"
fi
fi
log "Build tools: $HAS_BUILD_TOOLS"
}
# --- Main ---
log "=== Bootstrap started ==="
detect_platform
check_node
install_deps
check_build_tools
# Emit status block
STATUS="success"
if [ "$NODE_OK" = "false" ]; then
STATUS="node_missing"
elif [ "$DEPS_OK" = "false" ]; then
STATUS="deps_failed"
elif [ "$NATIVE_OK" = "false" ]; then
STATUS="native_failed"
fi
# Anonymous setup start event (non-blocking, best-effort)
curl -sS --max-time 3 -X POST https://us.i.posthog.com/capture/ \
-H 'Content-Type: application/json' \
-d "{\"api_key\":\"phc_fx1Hhx9ucz8GuaJC8LVZWO8u03yXZZJJ6ObS4yplnaP\",\"event\":\"setup_start\",\"distinct_id\":\"$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo unknown)\",\"properties\":{\"platform\":\"$PLATFORM\",\"is_wsl\":\"$IS_WSL\",\"is_root\":\"$IS_ROOT\",\"node_version\":\"$NODE_VERSION\",\"deps_ok\":\"$DEPS_OK\",\"native_ok\":\"$NATIVE_OK\",\"has_build_tools\":\"$HAS_BUILD_TOOLS\"}}" \
>/dev/null 2>&1 &
cat <<EOF
=== NANOCLAW SETUP: BOOTSTRAP ===
PLATFORM: $PLATFORM
IS_WSL: $IS_WSL
IS_ROOT: $IS_ROOT
NODE_VERSION: $NODE_VERSION
NODE_OK: $NODE_OK
NODE_PATH: ${NODE_PATH_FOUND:-not_found}
DEPS_OK: $DEPS_OK
NATIVE_OK: $NATIVE_OK
HAS_BUILD_TOOLS: $HAS_BUILD_TOOLS
STATUS: $STATUS
LOG: logs/setup.log
=== END ===
EOF
log "=== Bootstrap completed: $STATUS ==="
if [ "$NODE_OK" = "false" ]; then
exit 2
fi
if [ "$DEPS_OK" = "false" ] || [ "$NATIVE_OK" = "false" ]; then
exit 1
fi