publish the runtime spec; gate guard
This commit is contained in:
28
.github/workflows/ci.yml
vendored
28
.github/workflows/ci.yml
vendored
@@ -1,9 +1,11 @@
|
|||||||
# CI for shopdb-flask on GitHub Actions.
|
# CI for shopdb-flask on GitHub Actions.
|
||||||
#
|
#
|
||||||
# Mirrors the internal CI pipeline. Four jobs on push + pull_request:
|
# Mirrors the internal CI pipeline. Jobs on push + pull_request:
|
||||||
# backend - pytest (SQLite via TestingConfig, no DB service needed)
|
# backend - pytest (SQLite via TestingConfig, no DB service needed)
|
||||||
# naming - the CONTRIBUTING.md naming/style gate
|
# naming - the CONTRIBUTING.md naming/style gate
|
||||||
|
# gates - proves those checks actually RAN rather than skipping
|
||||||
# frontend - vitest + Vue build
|
# frontend - vitest + Vue build
|
||||||
|
# lean-build - ADR-013: a per-site build carries only its plugins
|
||||||
# migrations-mysql - the REAL multi-site deploy path: fresh flask db upgrade
|
# migrations-mysql - the REAL multi-site deploy path: fresh flask db upgrade
|
||||||
# + every plugin's chain on utf8mb4 MySQL 8, idempotent on
|
# + every plugin's chain on utf8mb4 MySQL 8, idempotent on
|
||||||
# a second run. The pytest suite only exercises SQLite
|
# a second run. The pytest suite only exercises SQLite
|
||||||
@@ -22,6 +24,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
# A second push supersedes the first: the older run answers a question nobody is
|
||||||
|
# asking any more, and it holds a self-hosted runner while doing it.
|
||||||
|
concurrency:
|
||||||
|
group: ci-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
backend:
|
backend:
|
||||||
runs-on: arc-runner-set
|
runs-on: arc-runner-set
|
||||||
@@ -40,6 +48,24 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: bash scripts/check-naming-and-style.sh
|
- run: bash scripts/check-naming-and-style.sh
|
||||||
|
|
||||||
|
gates:
|
||||||
|
# Prove the gates RAN. Several skip themselves when the file they read is
|
||||||
|
# absent, which is correct in this checkout (docs/ becomes the wiki) and was
|
||||||
|
# silently wrong here for months: the modules skipped, CI went green, and an
|
||||||
|
# outside branch added nine routes documented nowhere and passed.
|
||||||
|
#
|
||||||
|
# A gate that stops running is worse than no gate, because nobody looks at
|
||||||
|
# it again.
|
||||||
|
runs-on: arc-runner-set
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.14'
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -r requirements-dev.txt
|
||||||
|
- run: bash scripts/check-gates-ran.sh
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
runs-on: arc-runner-set
|
runs-on: arc-runner-set
|
||||||
defaults:
|
defaults:
|
||||||
|
|||||||
20974
docs/openapi.json
Normal file
20974
docs/openapi.json
Normal file
File diff suppressed because it is too large
Load Diff
101
scripts/check-gates-ran.sh
Executable file
101
scripts/check-gates-ran.sh
Executable file
@@ -0,0 +1,101 @@
|
|||||||
|
#!/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."
|
||||||
Reference in New Issue
Block a user