#!/usr/bin/env bash # smoke-test.sh — basic sanity checks for the pi-devbox image # # Usage: ./scripts/smoke-test.sh # # Verifies: # - pi binary present and returns a version # - pi-toolkit cloned at /opt/pi-toolkit # - pi-extensions cloned at /opt/pi-extensions # - entrypoint deploys pi-toolkit keybindings symlink # - entrypoint deploys ≥4 extensions # - mempalace bridge symlink present # - settings.json bootstrapped # - image size within threshold set -euo pipefail IMAGE="${1:?usage: $0 }" PASS=0; FAIL=0 SIZE_THRESHOLD_MB=2200 run() { local label="$1"; local cmd="$2" if docker run --rm --entrypoint="" "$IMAGE" sh -c "$cmd" >/dev/null 2>&1; then printf " ✅ %s\n" "$label"; PASS=$((PASS+1)) else printf " ❌ %s\n" "$label"; FAIL=$((FAIL+1)) fi } echo "=== pi-devbox smoke test: $IMAGE ===" echo "" # ── Basic binary checks ─────────────────────────────────────────────── echo "── Binaries ──" run "pi" "pi --version" run "node" "node --version" run "git" "git --version" run "aws" "aws --version" run "uv" "uv --version" run "nvim" "nvim --version" run "mempalace-mcp" "mempalace-mcp --help" # ── Repo clones ─────────────────────────────────────────────────────── echo "" echo "── Repo clones ──" run "pi-toolkit clone" "test -d /opt/pi-toolkit && git -C /opt/pi-toolkit rev-parse --short HEAD" run "pi-extensions clone" "test -d /opt/pi-extensions && git -C /opt/pi-extensions rev-parse --short HEAD" # ── Runtime deployment (needs entrypoint to run) ────────────────────── echo "" echo "── Runtime deployment ──" # Spin up a long-running container WITHOUT overriding the entrypoint, so # the baked entrypoint chain (entrypoint.sh → entrypoint-user.sh) runs and # deploys pi-toolkit + pi-extensions to ~/.pi/agent/. Override CMD to # tail -f /dev/null so the container stays alive while we docker-exec. CID=$(docker run -d --rm "$IMAGE" tail -f /dev/null) cleanup() { docker rm -f "$CID" >/dev/null 2>&1 || true; } trap cleanup EXIT # Wait for entrypoint-user.sh to finish deploying pi-toolkit + extensions for i in $(seq 1 30); do if docker exec "$CID" test -L /home/developer/.pi/agent/keybindings.json 2>/dev/null; then break fi sleep 1 done exec_test() { local label="$1"; local cmd="$2" if docker exec "$CID" sh -c "$cmd" >/dev/null 2>&1; then printf " ✅ %s\n" "$label"; PASS=$((PASS+1)) else printf " ❌ %s\n" "$label"; FAIL=$((FAIL+1)) fi } exec_test "keybindings.json (pi-toolkit)" 'test -L $HOME/.pi/agent/keybindings.json && echo ok' exec_test "extensions ≥ 4 (pi-extensions)" 'count=$(ls -1 $HOME/.pi/agent/extensions/*.ts 2>/dev/null | wc -l); [ $count -ge 4 ] && echo "$count extensions"' exec_test "mempalace.ts bridge" 'test -L $HOME/.pi/agent/extensions/mempalace.ts && echo ok' exec_test "settings.json bootstrapped" 'test -f $HOME/.pi/agent/settings.json && echo ok' # ── Image size ──────────────────────────────────────────────────────── echo "" echo "── Image size ──" SIZE_MB=$(docker image inspect "$IMAGE" --format='{{.Size}}' | awk '{printf "%d", $1/1048576}') if [ "$SIZE_MB" -le "$SIZE_THRESHOLD_MB" ]; then printf " ✅ size: %d MB (threshold %d MB)\n" "$SIZE_MB" "$SIZE_THRESHOLD_MB"; PASS=$((PASS+1)) else printf " ❌ size: %d MB exceeds threshold %d MB\n" "$SIZE_MB" "$SIZE_THRESHOLD_MB"; FAIL=$((FAIL+1)) fi # ── Summary ─────────────────────────────────────────────────────────── echo "" echo "=== Results: ${PASS} passed, ${FAIL} failed ===" [ "$FAIL" -eq 0 ]