Files
CASAN/AINative_OKR_CASAN5/.specify/scripts/bash/ci-harness-gate.sh
T
2026-07-06 21:47:38 +09:00

137 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -uo pipefail
# CASAN CI harness gate.
# Runs the reproducible harness suites in a safe order. `run-casan4` must run
# first because it rewrites `.specify/logs`.
#
# Env:
# CASAN_CI_RUN_FRONTEND=0|1 default 1
# CASAN_CI_RUN_INFRA_LAB=0|1 default 0 (Docker Compose lab is optional in CI)
# CASAN_CI_STEP_TIMEOUT_SEC default 600
# CASAN_CI_SUITE_FILTER optional regex; run matching suite names only
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
TESTS="$ROOT/.specify/tests"
PASS=0
FAIL=0
SKIP=0
run() {
local name="$1"
shift
if [[ -n "${CASAN_CI_SUITE_FILTER:-}" && ! "$name" =~ $CASAN_CI_SUITE_FILTER ]]; then
skip "$name (filtered)"
return 0
fi
echo "==> $name"
if run_with_timeout "$@"; then
echo "CI_GATE_PASS $name"
PASS=$((PASS + 1))
else
echo "CI_GATE_FAIL $name" >&2
FAIL=$((FAIL + 1))
fi
}
skip() {
echo "CI_GATE_SKIP $1"
SKIP=$((SKIP + 1))
}
run_with_timeout() {
if ! command -v python3 >/dev/null 2>&1; then
"$@"
return $?
fi
CASAN_CI_STEP_TIMEOUT_SEC="${CASAN_CI_STEP_TIMEOUT_SEC:-600}" python3 - "$@" <<'PY'
import os
import subprocess
import sys
timeout = int(os.environ.get("CASAN_CI_STEP_TIMEOUT_SEC", "600"))
cmd = sys.argv[1:]
try:
raise SystemExit(subprocess.run(cmd, timeout=timeout).returncode)
except subprocess.TimeoutExpired:
print(f"CI_GATE_TIMEOUT seconds={timeout} command={' '.join(cmd)}", file=sys.stderr)
raise SystemExit(124)
PY
}
cd "$ROOT" || exit 1
run "run-casan4-harness" bash "$TESTS/run-casan4-harness-tests.sh"
run "adversarial-harness" bash "$TESTS/adversarial-harness-tests.sh"
run "phase1-track-a" bash "$TESTS/phase1-track-a-tests.sh"
run "phase2-track-c" bash "$TESTS/phase2-track-c-tests.sh"
run "phase2-sourcegen" bash "$TESTS/phase2-sourcegen-tests.sh"
run "phase3-evidence-pack" bash "$TESTS/phase3-evidence-pack-tests.sh"
run "phase3-model-router" bash "$TESTS/phase3-model-router-tests.sh"
run "phase-h5-approval" bash "$TESTS/phase-h5-approval-tests.sh"
run "phase-h5-infra" bash "$TESTS/phase-h5-infra-tests.sh"
run "phase-h6-agentops" bash "$TESTS/phase-h6-agentops-tests.sh"
run "phase-c7-incident" bash "$TESTS/phase-c7-incident-tests.sh"
run "phase-h4-multilingual" bash "$TESTS/phase-h4-multilingual-tests.sh"
run "phase-c6-sandbox" bash "$TESTS/phase-c6-sandbox-tests.sh"
run "phase-h4-split-inject" bash "$TESTS/phase-h4-split-inject-tests.sh"
run "phase10-traceability" bash "$TESTS/phase10-traceability-tests.sh"
run "phase08-compression" bash "$TESTS/phase08-compression-tests.sh"
run "phase-control-plane" bash "$TESTS/phase-control-plane-tests.sh"
run "phase-rbac" bash "$TESTS/phase-rbac-tests.sh"
run "phase-rai" bash "$TESTS/phase-rai-tests.sh"
run "phase-selfimprove" bash "$TESTS/phase-selfimprove-tests.sh"
run "phase-governance-report" bash "$TESTS/phase-governance-report-tests.sh"
run "phase-preflight" bash "$TESTS/phase-preflight-tests.sh"
# Plan-16 security-audit remediation (P0) — each control has a fail-able test.
run "phase-sec01-unsigned-fail" bash "$TESTS/phase-sec01-tests.sh"
run "phase-sec02-no-local-key" bash "$TESTS/phase-sec02-tests.sh"
run "phase-sec03-rollback-rce" bash "$TESTS/phase-sec03-tests.sh"
run "phase-sec04-action-gate" bash "$TESTS/phase-sec04-tests.sh"
run "phase-sec05-json-safe" bash "$TESTS/phase-sec05-tests.sh"
run "phase-sec06-cp-signed" bash "$TESTS/phase-sec06-tests.sh"
run "phase-sec16-bundle" bash "$TESTS/phase-sec16-tests.sh"
run "phase-sec17-prod-profile" bash "$TESTS/phase-sec17-tests.sh"
run "phase-sec18-test-integrity" bash "$TESTS/phase-sec18-tests.sh"
# ARCH-02: coverage cannot silently drop; ARCH-01: harness/policy cannot silently
# drift. Both SKIP cleanly when no manifest is provisioned (non-strict dev/CI).
run "test-integrity" python3 "$SCRIPT_DIR/test-integrity.py" verify
run "bundle-integrity" python3 "$SCRIPT_DIR/bundle-integrity.py" verify
if [[ "${CASAN_CI_RUN_BACKEND:-1}" == "1" ]]; then
if command -v npm >/dev/null 2>&1; then
run "backend-tests" npm test -w backend
else
skip "backend-tests (npm unavailable)"
fi
else
skip "backend-tests (CASAN_CI_RUN_BACKEND=0)"
fi
if [[ "${CASAN_CI_RUN_FRONTEND:-1}" == "1" ]]; then
if command -v npm >/dev/null 2>&1; then
run "frontend-vitest" npm test -w frontend
else
skip "frontend-vitest (npm unavailable)"
fi
else
skip "frontend-vitest (CASAN_CI_RUN_FRONTEND=0)"
fi
if [[ "${CASAN_CI_RUN_INFRA_LAB:-0}" == "1" ]]; then
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
run "local-prod-infra-lab" bash "$TESTS/phase-prod-infra-lab-tests.sh"
else
skip "local-prod-infra-lab (docker compose unavailable)"
fi
else
skip "local-prod-infra-lab (CASAN_CI_RUN_INFRA_LAB=0)"
fi
echo "CI_GATE_SUMMARY PASS=$PASS FAIL=$FAIL SKIP=$SKIP"
[[ "$FAIL" -eq 0 ]] || exit 1