#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" PORT="${PREFLIGHT_PORT:-8010}" IMAGE_TAG="${PREFLIGHT_IMAGE_TAG:-safespace:preflight}" CONTAINER_NAME="${PREFLIGHT_CONTAINER_NAME:-safespace-preflight}" SCENARIO_ID="${PREFLIGHT_SCENARIO_ID:-easy_001}" HEALTHCHECK_RETRIES="${PREFLIGHT_HEALTHCHECK_RETRIES:-20}" DOCKERFILE_PATH="${PREFLIGHT_DOCKERFILE_PATH:-Dockerfile}" find_bin() { local name="$1" local candidates=() if [[ -n "${VIRTUAL_ENV:-}" ]]; then candidates+=("$VIRTUAL_ENV/bin/$name") fi candidates+=( "$ROOT_DIR/.venv/bin/$name" "$ROOT_DIR/.review-venv/bin/$name" ) for candidate in "${candidates[@]}"; do if [[ -x "$candidate" ]]; then printf '%s\n' "$candidate" return 0 fi done command -v "$name" 2>/dev/null || return 1 } find_python() { local candidates=() if [[ -n "${PYTHON_BIN:-}" ]]; then printf '%s\n' "$PYTHON_BIN" return 0 fi if [[ -n "${VIRTUAL_ENV:-}" ]]; then candidates+=("$VIRTUAL_ENV/bin/python") fi candidates+=( "$ROOT_DIR/.venv/bin/python" "$ROOT_DIR/.review-venv/bin/python" ) for candidate in "${candidates[@]}"; do if [[ -x "$candidate" ]]; then printf '%s\n' "$candidate" return 0 fi done command -v python3 2>/dev/null || command -v python 2>/dev/null || return 1 } ensure_python_pip() { if "$PYTHON_BIN" -m pip --version >/dev/null 2>&1; then return 0 fi printf '\nBootstrapping pip for %s via ensurepip...\n' "$PYTHON_BIN" "$PYTHON_BIN" -m ensurepip --upgrade --default-pip >/dev/null "$PYTHON_BIN" -m pip --version >/dev/null } cleanup() { if command -v docker >/dev/null 2>&1; then docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true fi } trap cleanup EXIT PYTHON_BIN="$(find_python)" ensure_python_pip PYTEST_BIN="$(find_bin pytest)" OPENENV_BIN="$(find_bin openenv)" DOCKER_BIN="$(find_bin docker || true)" printf '\n[1/9] Running targeted runtime/client tests...\n' "$PYTEST_BIN" tests/test_environment.py tests/test_app_client.py -q printf '\n[2/9] Running full test suite...\n' "$PYTEST_BIN" tests -q printf '\n[3/9] Validating canonical benchmark manifest...\n' "$PYTHON_BIN" - <<'PY' from content_moderation_env.server.scenarios import validate_benchmark_manifest manifest = validate_benchmark_manifest() print( "Canonical benchmark manifest is valid: " f"{manifest['manifest_version']}" ) PY printf '\n[4/9] Running package asset smoke check...\n' "$PYTHON_BIN" scripts/check_package_assets.py printf '\n[5/9] Validating inference config contract...\n' API_BASE_URL="https://example.invalid/v1" \ MODEL_NAME="test-model" \ HF_TOKEN="test-key" \ ENV_BASE_URL="http://127.0.0.1:${PORT}" \ "$PYTHON_BIN" inference.py --validate-config printf '\n[6/9] Validating OpenEnv manifest...\n' "$OPENENV_BIN" validate . if [[ -z "$DOCKER_BIN" ]]; then printf '\n[7/9-9/9] Docker CLI not found on PATH. Skipping container build, health, and typed-client container smoke checks.\n' printf '\nPreflight completed successfully.\n' exit 0 fi printf '\n[7/9] Building Docker image...\n' "$DOCKER_BIN" build -t "$IMAGE_TAG" -f "$DOCKERFILE_PATH" . printf '\n[8/9] Starting container and checking health...\n' "$DOCKER_BIN" run --rm -d -p "${PORT}:8000" --name "$CONTAINER_NAME" "$IMAGE_TAG" >/dev/null for ((attempt = 1; attempt <= HEALTHCHECK_RETRIES; attempt++)); do if curl -fsS "http://127.0.0.1:${PORT}/health" >/dev/null; then break fi if [[ "$attempt" -eq "$HEALTHCHECK_RETRIES" ]]; then printf 'Health check failed after %s attempts.\n' "$HEALTHCHECK_RETRIES" >&2 exit 1 fi sleep 1 done curl -fsS "http://127.0.0.1:${PORT}/health" printf '\n' printf '\n[9/9] Running typed client smoke test...\n' export PREFLIGHT_PORT="$PORT" export PREFLIGHT_SCENARIO_ID="$SCENARIO_ID" "$PYTHON_BIN" - <<'PY' import os from content_moderation_env import ModerationAction, SafeSpaceEnv port = os.environ["PREFLIGHT_PORT"] scenario_id = os.environ["PREFLIGHT_SCENARIO_ID"] with SafeSpaceEnv(base_url=f"http://127.0.0.1:{port}").sync() as env: result = env.reset(scenario_id=scenario_id) assert env.state().scenario_id == scenario_id result = env.step( ModerationAction( action_type="decide", decision="remove", primary_violation="5.1", severity="high", confidence=0.95, key_factors=["spam_commercial"], ) ) assert result.done is True assert result.reward is not None assert result.observation.task_grade is not None print( f"Smoke test passed for {scenario_id}: " f"reward={result.reward:.3f}, task_grade={result.observation.task_grade:.3f}" ) PY printf '\nPreflight completed successfully.\n'