singularity-forge/scripts/verify-s04.sh

247 lines
9.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# S04 verification — npm pack tarball install smoke test
# Checks: dist integrity, SF_BUNDLED_EXTENSION_PATHS, prepublishOnly,
# npm pack dry-run, tarball install, binary exists, launch (no extension
# errors, "sf" branding), ~/.sf/ untouched, non-TTY warning/no exit 1.
set -uo pipefail
FAIL=0
pass() { echo " PASS: $1"; }
fail() { echo " FAIL: $1"; FAIL=1; }
SMOKE_PREFIX=/tmp/sf-smoke-prefix
TARBALL=""
# Capture ~/.sf/agent/sessions/ count before any smoke runs (for Check 9)
PI_SESSIONS_BEFORE=$(ls ~/.sf/agent/sessions/ 2>/dev/null | wc -l | tr -d ' ')
cleanup() {
rm -rf "$SMOKE_PREFIX"
if [ -n "$TARBALL" ] && [ -f "$TARBALL" ]; then
rm -f "$TARBALL"
fi
}
trap cleanup EXIT
echo "=== S04 Verification ==="
echo ""
# ----------------------------------------------------------------
# Check 1 — dist/loader.js exists and has NODE_PATH block
# ----------------------------------------------------------------
echo "--- Dist integrity ---"
if [ -f "dist/loader.js" ] && grep -q "NODE_PATH" dist/loader.js; then
pass "1 — dist/loader.js exists and contains NODE_PATH block"
else
fail "1 — dist/loader.js missing or NODE_PATH block absent"
fi
# ----------------------------------------------------------------
# Check 2 — SF_BUNDLED_EXTENSION_PATHS does NOT reference src/resources
# ----------------------------------------------------------------
# The variable must be present and must use agentDir-based paths only.
paths_line=$(grep "SF_BUNDLED_EXTENSION_PATHS" dist/loader.js | grep -v "src/resources" | head -1)
if [ -n "$paths_line" ]; then
# Double-check: none of the actual join() lines (not comments) reference src/resources.
# We look only at lines containing join( to avoid matching comment lines like "NOT src/resources".
if grep -A 15 "SF_BUNDLED_EXTENSION_PATHS" dist/loader.js | grep "join(" | grep -q "src/resources"; then
fail "2 — SF_BUNDLED_EXTENSION_PATHS still references src/resources path(s)"
else
pass "2 — SF_BUNDLED_EXTENSION_PATHS uses agentDir-based paths (no src/resources)"
fi
else
fail "2 — SF_BUNDLED_EXTENSION_PATHS line not found or still references src/resources"
fi
echo ""
echo "--- package.json hooks ---"
# ----------------------------------------------------------------
# Check 3 — prepublishOnly present in package.json
# ----------------------------------------------------------------
if node -e "const p=JSON.parse(require('fs').readFileSync('package.json','utf8')); process.exit(p.scripts?.prepublishOnly ? 0 : 1)" 2>/dev/null; then
pass "3 — prepublishOnly hook present in package.json"
else
fail "3 — prepublishOnly hook missing from package.json"
fi
echo ""
echo "--- npm pack dry-run ---"
# ----------------------------------------------------------------
# Check 4 — npm pack --dry-run lists expected files
# ----------------------------------------------------------------
dry_out=$(npm pack --dry-run 2>&1)
file_count=$(echo "$dry_out" | grep -c "npm notice" || true)
has_src=$(echo "$dry_out" | grep -q "src/resources" && echo "yes" || echo "no")
has_dist=$(echo "$dry_out" | grep -q "dist/" && echo "yes" || echo "no")
has_pkg=$(echo "$dry_out" | grep -q "pkg/" && echo "yes" || echo "no")
# Count actual files listed (lines with a path, not summary lines)
file_lines=$(echo "$dry_out" | grep "npm notice" | grep -v "=== Tarball" | grep -v "filename\|package size\|unpacked size\|shasum\|integrity\|total files" | wc -l | tr -d ' ')
if [ "$file_lines" -ge 100 ] && [ "$has_dist" = "yes" ] && [ "$has_pkg" = "yes" ]; then
# src/resources check — warn but don't fail if absent (it's in "files" array but may not produce 100+ files on its own)
if [ "$has_src" = "yes" ]; then
pass "4 — dry-run: ${file_lines} files listed, dist/ present, pkg/ present, src/resources present"
else
fail "4 — dry-run: ${file_lines} files listed but src/resources NOT in pack output"
echo " (dry-run output tail:)"
echo "$dry_out" | tail -10 | sed 's/^/ /'
fi
elif [ "$file_lines" -lt 100 ]; then
fail "4 — dry-run: only ${file_lines} files listed (expected >=100)"
echo "$dry_out" | tail -10 | sed 's/^/ /'
else
fail "4 — dry-run: dist/=${has_dist} pkg/=${has_pkg}"
echo "$dry_out" | tail -10 | sed 's/^/ /'
fi
echo ""
echo "--- tarball pack ---"
# ----------------------------------------------------------------
# Check 5 — npm pack produces a tarball
# ----------------------------------------------------------------
# Note: prepublishOnly triggers a build here (expected).
npm pack --silent 2>/dev/null || npm pack 2>&1 | tail -5
TARBALL=$(ls glittercowboy-sf-*.tgz 2>/dev/null | head -1 || true)
if [ -n "$TARBALL" ] && [ -f "$TARBALL" ]; then
pass "5 — tarball produced: $TARBALL"
else
fail "5 — npm pack did not produce a tarball"
echo " Aborting remaining checks — no tarball available."
echo ""
echo "=== Results ==="
echo "One or more checks FAILED."
exit 1
fi
echo ""
echo "--- tarball install ---"
# ----------------------------------------------------------------
# Check 6 — tarball installs cleanly to temp prefix
# ----------------------------------------------------------------
rm -rf "$SMOKE_PREFIX"
if npm install -g --prefix "$SMOKE_PREFIX" "./$TARBALL" 2>&1 | tail -5; then
pass "6 — tarball installed to $SMOKE_PREFIX (exit 0)"
else
fail "6 — tarball install failed"
fi
# ----------------------------------------------------------------
# Check 7 — binary exists at expected path after install
# ----------------------------------------------------------------
if [ -f "$SMOKE_PREFIX/bin/sf" ] || [ -L "$SMOKE_PREFIX/bin/sf" ]; then
pass "7 — $SMOKE_PREFIX/bin/sf exists after install"
else
fail "7 — $SMOKE_PREFIX/bin/sf not found after install"
ls -la "$SMOKE_PREFIX/bin/" 2>/dev/null || echo " (bin/ dir does not exist)"
fi
echo ""
echo "--- launch smoke ---"
# ----------------------------------------------------------------
# Check 8 — launch: "sf" branding + zero extension load errors
# Use background kill pattern (macOS has no GNU timeout).
# Allow 8s for extensions to load.
# ----------------------------------------------------------------
smoke_out=$(mktemp)
(
env -i HOME="$HOME" PATH="$PATH" \
"$SMOKE_PREFIX/bin/sf" < /dev/null > "$smoke_out" 2>&1
) &
smoke_pid=$!
sleep 8
kill "$smoke_pid" 2>/dev/null || true
wait "$smoke_pid" 2>/dev/null || true
ext_errors=$(grep "Extension load error" "$smoke_out" 2>/dev/null | wc -l | tr -d ' ')
# Strip ANSI escape codes for branding check
plain_out=$(sed 's/\x1b\[[0-9;]*m//g' "$smoke_out" 2>/dev/null || cat "$smoke_out")
has_sf=$(echo "$plain_out" | grep -qi "sf\|get shit done" && echo "yes" || echo "no")
if [ "$ext_errors" -eq 0 ]; then
pass "8a — zero Extension load errors on launch"
else
fail "8a — ${ext_errors} Extension load error(s) on launch"
grep "Extension load error" "$smoke_out" | head -5 | sed 's/^/ /'
fi
if [ "$has_sf" = "yes" ]; then
pass "8b — \"sf\" / \"get shit done\" branding found in launch output"
else
# Fallback: check if binary self-identifies differently (not "pi")
has_pi_only=$(echo "$plain_out" | grep -qi "^pi\b" && echo "yes" || echo "no")
if [ "$has_pi_only" = "no" ]; then
pass "8b — output does not show \"pi\" branding (sf branding likely in ANSI sequences)"
else
fail "8b — output shows \"pi\" branding instead of \"sf\""
head -5 "$smoke_out" | sed 's/^/ /'
fi
fi
rm -f "$smoke_out"
echo ""
echo "--- ~/.sf/ isolation ---"
# ----------------------------------------------------------------
# Check 9 — ~/.sf/ session count unchanged before/after smoke run
# PI_SESSIONS_BEFORE captured at script start (before any binary invocation).
# ----------------------------------------------------------------
pi_after=$(ls ~/.sf/agent/sessions/ 2>/dev/null | wc -l | tr -d ' ')
if [ "$PI_SESSIONS_BEFORE" = "$pi_after" ]; then
pass "9 — ~/.sf/agent/sessions/ count unchanged (${pi_after} sessions before and after)"
else
fail "9 — ~/.sf/agent/sessions/ count changed: was ${PI_SESSIONS_BEFORE}, now ${pi_after}"
fi
echo ""
echo "--- non-TTY warning path ---"
# ----------------------------------------------------------------
# Check 10 — non-TTY missing optional keys → warning, no exit 1
# Run installed binary with minimal env (HOME + PATH only), piped from /dev/null.
# ----------------------------------------------------------------
tmp10=$(mktemp)
exit10_tmp=$(mktemp)
echo "" > "$exit10_tmp"
(
env -i HOME="$HOME" PATH="$PATH" \
"$SMOKE_PREFIX/bin/sf" < /dev/null > "$tmp10" 2>&1
echo "$?" > "$exit10_tmp"
) &
pid10=$!
sleep 5
kill "$pid10" 2>/dev/null || true
wait "$pid10" 2>/dev/null || true
if grep -qi "warning\|optional" "$tmp10" 2>/dev/null; then
pass "10a — non-TTY missing optional keys → warning emitted"
else
fail "10a — non-TTY missing optional keys → no warning found in output"
echo " Output (first 5 lines):"
head -5 "$tmp10" | sed 's/^/ /'
fi
exit10_code=$(cat "$exit10_tmp")
if [ "$exit10_code" = "1" ]; then
fail "10b — non-TTY missing optional keys → exited with code 1 (should not)"
echo " Output: $(head -3 "$tmp10")"
else
pass "10b — non-TTY missing optional keys → did NOT exit 1 (code: ${exit10_code:-killed})"
fi
rm -f "$tmp10" "$exit10_tmp"
echo ""
echo "=== Results ==="
if [ "$FAIL" -eq 0 ]; then
echo "All checks passed."
exit 0
else
echo "One or more checks FAILED."
exit 1
fi