#!/usr/bin/env sh set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) PLATFORM_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/../.." && pwd) INFRA_DIR="$PLATFORM_ROOT/infra" ASSISTANT_DIR="$PLATFORM_ROOT/services/ai-workspace-assistant" HUB_DIR="$PLATFORM_ROOT/services/ai-workspace-hub" ONTOLOGY_DIR="$PLATFORM_ROOT/services/ontology-core" COMPOSE_ENV="${COMPOSE_ENV:-$INFRA_DIR/.env}" if [ ! -f "$COMPOSE_ENV" ]; then COMPOSE_ENV="$INFRA_DIR/.env.example" fi REQUIRE_RUNTIME="${REQUIRE_RUNTIME:-0}" REQUIRE_REMOTE_WORKER_RELAY="${REQUIRE_REMOTE_WORKER_RELAY:-0}" passed=0 failed=0 skipped=0 section() { printf '\n== %s ==\n' "$1" } pass() { passed=$((passed + 1)) printf 'PASS %s\n' "$1" } fail() { failed=$((failed + 1)) printf 'FAIL %s\n' "$1" >&2 } skip() { skipped=$((skipped + 1)) printf 'SKIP %s\n' "$1" } run_cmd() { label="$1" shift printf '\n$ %s\n' "$label" set +e "$@" status=$? set -e if [ "$status" -eq 0 ]; then pass "$label" else fail "$label (exit $status)" fi } run_sh() { label="$1" dir="$2" command="$3" printf '\n$ %s\n' "$label" set +e (cd "$dir" && sh -lc "$command") status=$? set -e if [ "$status" -eq 0 ]; then pass "$label" else fail "$label (exit $status)" fi } run_sh_quiet() { label="$1" dir="$2" command="$3" output_file=$(mktemp "${TMPDIR:-/tmp}/nodedc-check.XXXXXX") printf '\n$ %s\n' "$label" set +e (cd "$dir" && sh -lc "$command") > "$output_file" 2>&1 status=$? set -e if [ "$status" -eq 0 ]; then pass "$label" rm -f "$output_file" else cat "$output_file" rm -f "$output_file" fail "$label (exit $status)" fi } section "Static syntax" run_cmd "Hub server syntax" node --check "$HUB_DIR/src/server.mjs" run_cmd "Assistant server syntax" node --check "$ASSISTANT_DIR/src/server.mjs" run_cmd "Ontology MCP server syntax" node --check "$ONTOLOGY_DIR/src/mcp-server.mjs" run_cmd "Local environment safety checker syntax" node --check "$INFRA_DIR/scripts/check-local-environment-safety.mjs" run_sh "Shell scripts syntax" "$PLATFORM_ROOT" \ "sh -n infra/scripts/init-dev-env.sh infra/scripts/check-local-test-system.sh infra/scripts/check-ai-workspace-topology.sh infra/scripts/check-ai-workspace-config-contract.sh infra/scripts/check-ai-workspace-release-gates.sh" if command -v bash >/dev/null 2>&1; then run_sh "Synology hub-only deploy scripts syntax" "$PLATFORM_ROOT" \ "bash -n infra/synology/sync-ai-hub-relay.sh infra/synology/apply-ai-hub-relay.sh" else skip "Synology hub-only deploy scripts syntax (bash not installed)" fi section "Environment contracts" run_cmd "Local environment safety" node "$INFRA_DIR/scripts/check-local-environment-safety.mjs" run_cmd "AI Workspace config contract" "$INFRA_DIR/scripts/check-ai-workspace-config-contract.sh" section "Compose boundary" if command -v docker >/dev/null 2>&1; then compose_tmp=$(mktemp "${TMPDIR:-/tmp}/nodedc-local-compose.XXXXXX") if docker compose --env-file "$COMPOSE_ENV" -f "$INFRA_DIR/docker-compose.dev.yml" config > "$compose_tmp"; then pass "Local compose config renders" else fail "Local compose config renders" fi rm -f "$compose_tmp" if grep -Fq "env_file:" "$INFRA_DIR/docker-compose.dev.yml"; then fail "Local compose must not inject the full .env into services" else pass "Local compose uses explicit service environment only" fi else skip "Docker compose config render (docker not installed)" fi section "AI Workspace smokes" run_sh_quiet "Assistant run-profile smoke" "$ASSISTANT_DIR" "npm run smoke:run-profile" run_sh_quiet "AI Hub Ontology MCP proxy smoke" "$HUB_DIR" "npm run smoke:ontology-mcp-proxy" if [ -d "$ONTOLOGY_DIR" ]; then run_sh_quiet "Ontology assistant caller smoke" "$ONTOLOGY_DIR" "npm run smoke:assistant-caller" run_sh_quiet "Ontology MCP smoke" "$ONTOLOGY_DIR" "npm run smoke:mcp" else skip "Ontology assistant caller smoke (missing $ONTOLOGY_DIR)" fi section "Runtime classification" topology_tmp=$(mktemp "${TMPDIR:-/tmp}/nodedc-topology.XXXXXX") set +e (cd "$PLATFORM_ROOT" && infra/scripts/check-ai-workspace-topology.sh) > "$topology_tmp" topology_status=$? set -e cat "$topology_tmp" classification=$(awk -F= '/^classification=/{print $2; exit}' "$topology_tmp") rm -f "$topology_tmp" if [ "$topology_status" -eq 0 ]; then pass "Runtime topology classified as ${classification:-unknown}" else fail "Runtime topology classification failed (exit $topology_status)" fi if [ "$REQUIRE_RUNTIME" = "1" ]; then case "$classification" in true-local-e2e|tunnel-local-e2e|local-remote-worker-relay) pass "Runtime requirement satisfied" ;; *) fail "Runtime requirement not satisfied (classification=${classification:-unknown})" ;; esac fi if [ "$REQUIRE_REMOTE_WORKER_RELAY" = "1" ]; then case "$classification" in local-remote-worker-relay) pass "Remote worker relay requirement satisfied" ;; *) fail "Remote worker relay requirement not satisfied (classification=${classification:-unknown})" ;; esac fi section "Git hygiene" run_sh "Platform diff hygiene" "$PLATFORM_ROOT" "git diff --check" section "Summary" printf 'passed=%s failed=%s skipped=%s\n' "$passed" "$failed" "$skipped" if [ "$failed" -ne 0 ]; then exit 1 fi