#!/usr/bin/env bash # # Prove the gates actually RAN. # # Several checks skip themselves when the file they read is absent: # # tests/test_api_inventory_parity.py skipif docs/api-inventory.json missing # tests/test_openapi_spec.py skipif docs/openapi.json missing # tests/test_docs_publishable.py skipif docs/ missing # # That is correct in a published checkout, where docs/ becomes the wiki and # there is genuinely nothing to check. It was silently wrong on GitHub, which # runs against exactly that checkout: the modules skipped, CI reported green, # and an outside branch added nine routes documented nowhere and passed. # # So the list is split. Some gates must run everywhere; the docs-dependent ones # must run HERE, where the files exist, and are allowed to be absent there. # # A gate that stops running is worse than no gate: nobody looks at it again. # This asserts each named module reported at least one PASS and no SKIPS. # # Usage: # bash scripts/check-gates-ran.sh # runs the gates itself # bash scripts/check-gates-ran.sh report.txt # reads an existing -rs report set -uo pipefail cd "$(dirname "$0")/.." # Gates that must run in EVERY checkout, published or internal. A skip here is # always a fault. GATES_EVERYWHERE=( "tests/test_openapi_spec.py" "tests/test_core/test_public_read_surface.py" "tests/test_core/test_authz.py" "tests/test_plugin_migrations.py" ) # Gates that need files the publication export deliberately leaves behind - # docs/ becomes the wiki, so a published checkout genuinely has nothing to check # and skipping is correct there. In THIS repo, skipping means something broke. GATES_INTERNAL_ONLY=( "tests/test_api_inventory_parity.py" "tests/test_docs_publishable.py" ) # A published checkout is one with no docs/ prose. openapi.json alone does not # make it internal: that file ships because the app serves it at runtime. if compgen -G "docs/*.md" > /dev/null; then GATES=("${GATES_EVERYWHERE[@]}" "${GATES_INTERNAL_ONLY[@]}") echo "==> Internal checkout: every gate must run" else GATES=("${GATES_EVERYWHERE[@]}") echo "==> Published checkout: docs-dependent gates are expected to be absent" fi PYTHON="${PYTHON:-python}" [ -x venv/bin/python ] && PYTHON=venv/bin/python report="${1:-}" if [ -z "$report" ]; then report="$(mktemp)" # -rs lists every skip with its reason, which is the thing being checked. "$PYTHON" -m pytest "${GATES[@]}" -q -rs > "$report" 2>&1 || true fi echo "==> Checking that every gate ran" failed=0 for gate in "${GATES[@]}"; do if [ ! -f "$gate" ]; then echo " MISSING $gate - the file is not in this checkout" failed=1 continue fi if grep -q "SKIPPED.*$(basename "$gate")" "$report"; then reason=$(grep "SKIPPED.*$(basename "$gate")" "$report" | head -1 | sed 's/.*-- //') echo " SKIPPED $gate" echo " reason: ${reason:-not given}" failed=1 continue fi echo " ran $gate" done if grep -qE "^[0-9]+ failed|error" "$report"; then echo echo "Gate tests FAILED (not merely skipped):" grep -E "^FAILED|^ERROR" "$report" | sed 's/^/ /' failed=1 fi echo if [ "$failed" -ne 0 ]; then echo "FAIL: a gate did not run. A skipped gate reports green and checks nothing." echo " If a docs-dependent gate skipped in THIS repo, the file it reads" echo " was not generated: run scripts/gen_openapi.py and" echo " scripts/gen_project_map.py. If one skipped in the PUBLISHED repo," echo " the export dropped a file the gate needs - see tools/export-github.sh." exit 1 fi echo "All gates ran."