Operations
Production-minded checks for docs, sources, API health, and runtime validation.
Operations
Operational checks should validate runtime surfaces, docs generation, proxy-list publication, and CI logs independently. Treat each surface as its own gate; do not call a release production-ready from one passing lane.
Production Gate Matrix
| Surface | Command | Expected result |
|---|---|---|
| Python quality | task quality-gates | Ruff, ty, fast tests, and coverage pass with no actionable warnings. |
| Expanded tests | uv run pytest tests/ -q -m "not slow" --ignore=tests/benchmarks --timeout=120 | API, contract, property, unit, and integration coverage pass outside benchmark/slow browser lanes. |
| Docs generation | pnpm --dir web run docs:generate | Generated Fumadocs references and JSON data match source code. |
| Docs lint | pnpm --dir web run lint | ESLint exits with --max-warnings 0. |
| Docs unit tests | pnpm --dir web run test:run | Vitest docs/UI tests pass. |
| Docs build | pnpm --dir web run build | Next.js + Fumadocs build succeeds with no actionable warnings. |
| Strict source health | uv run proxywhirl sources --validate --fail-on-unhealthy --timeout 5 --concurrency 5 | Every enabled built-in source is reachable and healthy at the stable CI concurrency. |
| Package build | .venv/bin/python -m build | Wheel and sdist build with the same interpreter used for dependency install. |
Docs Pipeline
pnpm --dir web run docs:generate
pnpm --dir web run lint
pnpm --dir web run test:run
pnpm --dir web run buildRun pnpm --dir web run test:e2e when browser dependencies are installed and the change affects rendered behavior, routing, or interactive docs components.
CLI Smoke
tmpdir="$(mktemp -d)"
config_path="$tmpdir/.proxywhirl.toml"
uv run python - <<PY
from pathlib import Path
from proxywhirl.config import CLIConfig, save_config
save_config(CLIConfig(encrypt_credentials=False), Path("$config_path"))
PY
uv run proxywhirl --no-lock --config "$config_path" config set max_retries 0
uv run proxywhirl --no-lock --config "$config_path" --format json config get max_retries \
> "$tmpdir/max_retries.json"
rg '"max_retries": 0' "$tmpdir/max_retries.json"
uv run proxywhirl --no-lock --config "$config_path" pool add http://user:pass@proxy.example.com:8080
uv run proxywhirl --no-lock --config "$config_path" pool add http://proxy.example.com:8081
uv run proxywhirl --no-lock --config "$config_path" --format json pool list > "$tmpdir/pool.json"
if rg 'user|pass' "$tmpdir/pool.json"; then
echo "credential leaked in pool list" >&2
exit 1
fi
printf 'http://proxy.example.com:8082\n' > "$tmpdir/proxies.txt"
uv run proxywhirl --no-lock --config "$config_path" --format json import-proxies \
"$tmpdir/proxies.txt" --format text
uv run proxywhirl --no-lock --config "$config_path" pool remove http://proxy.example.com:8081
set +e
uv run proxywhirl --no-lock --config "$config_path" --format json validate-proxy \
http://127.0.0.1:9 --target http://example.com --timeout 0.2 > "$tmpdir/validate.json"
exit_code=$?
set -e
if [ "$exit_code" -eq 0 ]; then
echo "expected closed-port validation to fail" >&2
exit 1
fi
rg '"status": "UNHEALTHY"' "$tmpdir/validate.json"
uv run proxywhirl sources --validate --fail-on-unhealthy --timeout 5 --concurrency 5The closed-port validate-proxy smoke should exit non-zero with structured UNHEALTHY output. The pool smoke should preserve credentials in the config file while keeping userinfo out of CLI output.
API Smoke
Use a temporary storage path and a disposable localhost port:
tmpdir="$(mktemp -d)"
PROXYWHIRL_STORAGE_PATH="$tmpdir/api.db" \
uv run uvicorn proxywhirl.api:app --host 127.0.0.1 --port 8765In another shell, verify health and readiness:
uv run python - <<'PY'
import httpx
for path in ("/api/health", "/api/ready"):
response = httpx.get(f"http://127.0.0.1:8765{path}", timeout=5)
if path == "/api/ready":
response.raise_for_status()
elif response.status_code not in {200, 503}:
response.raise_for_status()
payload = response.json()
assert payload.get("status") == "success", payload
print(path, response.status_code, payload["data"])
PYWith an empty temporary database, /api/health can report 503 with a successful unhealthy response body because no proxies are loaded yet. /api/ready should return 200. Stop the server after the smoke. Use a temporary PROXYWHIRL_API_KEY only when testing authenticated write paths; never commit or print real keys.
GitHub Actions Assurance
After pushing, monitor these runs on the target SHA:
CISecurity ScanValidate Proxy Sources(manualworkflow_dispatchwhen source health is in scope)
Scan completed logs for actionable markers:
gh run view <run-id> --log | rg '##\[warning\]|##\[error\]|::warning::|::error::|DeprecationWarning|RuntimeWarning|ResourceWarning|Traceback|Token required|not valid tokenless upload'Literal environment assignments such as PYTHONWARNINGS="ignore::UserWarning" are not emitted warnings. Codecov upload is optional and skipped when no CODECOV_TOKEN is configured; local coverage threshold enforcement and coverage artifact upload remain required.
Keep documentation generation read-only with respect to proxy runtime behavior and database schema unless a separate implementation task explicitly changes those contracts.