CI gates (active on every PR via .gitea/workflows/)
───────────────────────────────────────────────────
1. test-count consistency
cpp-tests.yml gains a step after test-cgal that runs
`scripts/check-test-counts.sh` against the just-built ./build dir
(reuse via new BUILD_DIR env var, ~5 s overhead). Drift between
`doc/api/tests.md` and ctest reality now fails the PR.
2. End-to-end smoke
`scripts/try_it.sh` (the documented user quick-start) is now part of
the CGAL job, so README quick-start regressions fail the PR rather
than silently breaking when users land.
3. Internal markdown link checker
New `.gitea/workflows/markdown-links.yml` + `scripts/check-markdown
-links.py`. PRs that touch any *.md file run the check; main pushes
trigger it too; a weekly cron catches external link rot. Pure
Python, no third-party action. Validated against the current tree:
122 internal links across 37 *.md files, 0 broken.
Local quality scripts (`scripts/quality/`, not in CI)
─────────────────────────────────────────────────────
* `license-headers.sh` — `SPDX-License-Identifier: MIT` audit over
code/{include,src,tests}/. Currently
reports 60/66 files missing it — that's
a follow-up; the script captures the
structural gap.
* `sanitizers.sh` — ASan + UBSan over the fast test suite.
* `coverage.sh` — gcov/lcov line + branch coverage of
code/include/, HTML report under
build-coverage/lcov-html/.
* `clang-tidy.sh` — runs the curated `.clang-tidy` policy over
every public header.
* `multi-compiler.sh` — sequential build + test against every
detected g++/clang++ (auto-discovery or
explicit list).
* `cgal-version-matrix.sh`— sequential build + CGAL test suite against
every CGAL tree under `~/cgal/<ver>/` (or
via `CGAL_ROOTS=...` env var).
* `reproducible-build.sh`— two `Release -j1` builds, fail if any test
executable byte-differs.
* `run-all.sh` — driver: `--fast` for the ~5-min subset,
no arg for the ~25–40 min full sweep;
captures per-gate logs to
build-quality-logs/.
+ `.clang-tidy` — curated, deliberately-small policy (only
checks that fire on OUR code, never on
transitive CGAL/Eigen/Boost headers).
+ `scripts/quality/README.md` — explains the structure, lists each
gate's wall-time + prereqs, and codifies
the promotion path: a gate moves into CI
only when it's green on the dev machine
AND has a recovery-instructions paragraph
in `doc/release-policy.md`.
Doc updates
───────────
`doc/architecture/locked-vs-flexible.md` (reviewer-facing) gains 4
"closed" rows in the limitations table — the 3 CI gates above and the
local quality-script suite.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
101 lines
3.3 KiB
Bash
Executable File
101 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# scripts/quality/run-all.sh
|
||
#
|
||
# Run every local quality gate in sequence. Each gate is independent;
|
||
# a failure does not stop the rest (we collect failures and report at
|
||
# the end). Use this before tagging a release or before showing the
|
||
# repo to an external reviewer.
|
||
#
|
||
# Wall-time budget on a typical dev laptop (M-series Mac):
|
||
# license-headers.sh ~1 s
|
||
# check-markdown-links.py ~2 s
|
||
# sanitizers.sh ~3 min
|
||
# coverage.sh ~2 min
|
||
# clang-tidy.sh ~2 min (depends on header count)
|
||
# multi-compiler.sh ~5 min (per compiler)
|
||
# reproducible-build.sh ~6 min
|
||
# cgal-version-matrix.sh ~5 min per CGAL version
|
||
# ─────────────────────────────
|
||
# TOTAL ~25–40 min
|
||
#
|
||
# Usage:
|
||
# bash scripts/quality/run-all.sh # everything
|
||
# bash scripts/quality/run-all.sh --fast # skip the slow gates
|
||
# (cgal-matrix, multi-compiler,
|
||
# coverage, reproducible)
|
||
#
|
||
# Exit code: number of failed gates (so 0 = green).
|
||
|
||
set -uo pipefail
|
||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||
cd "$ROOT"
|
||
|
||
FAST=0
|
||
[ "${1:-}" = "--fast" ] && FAST=1
|
||
|
||
GATES_FAST=(
|
||
"License headers | bash scripts/quality/license-headers.sh"
|
||
"Markdown links | python3 scripts/check-markdown-links.py"
|
||
"Sanitizers | bash scripts/quality/sanitizers.sh"
|
||
"clang-tidy | bash scripts/quality/clang-tidy.sh"
|
||
)
|
||
GATES_SLOW=(
|
||
"Coverage | bash scripts/quality/coverage.sh"
|
||
"Multi-compiler | bash scripts/quality/multi-compiler.sh"
|
||
"Reproducible build | bash scripts/quality/reproducible-build.sh"
|
||
"CGAL version matrix | bash scripts/quality/cgal-version-matrix.sh"
|
||
)
|
||
|
||
if [ "$FAST" -eq 1 ]; then
|
||
GATES=("${GATES_FAST[@]}")
|
||
else
|
||
GATES=("${GATES_FAST[@]}" "${GATES_SLOW[@]}")
|
||
fi
|
||
|
||
LOG_DIR="build-quality-logs"
|
||
mkdir -p "$LOG_DIR"
|
||
|
||
echo "============================================================"
|
||
echo " conformallab++ local quality gates"
|
||
echo " mode: $([ $FAST -eq 1 ] && echo 'FAST (4 gates)' || echo "FULL (${#GATES[@]} gates)")"
|
||
echo " logs: $LOG_DIR/"
|
||
echo "============================================================"
|
||
|
||
results=""
|
||
failed=0
|
||
i=0
|
||
for entry in "${GATES[@]}"; do
|
||
i=$((i + 1))
|
||
name="${entry%%|*}"
|
||
name="${name%%[[:space:]]*([[:space:]])}" # trim trailing space
|
||
cmd="${entry##*|}"
|
||
cmd="${cmd##[[:space:]]}"
|
||
# Slug-safe filename
|
||
slug=$(echo "$name" | tr ' /[:upper:]' '_-[:lower:]' | tr -cd 'a-z0-9_-')
|
||
log="$LOG_DIR/$slug.log"
|
||
echo
|
||
echo "──── [$i/${#GATES[@]}] $name ────"
|
||
if eval "$cmd" >"$log" 2>&1; then
|
||
echo " OK ($log)"
|
||
results="${results} PASS $name
|
||
"
|
||
else
|
||
rc=$?
|
||
echo " FAIL (rc=$rc) — see $log"
|
||
echo " last 20 lines:"
|
||
tail -20 "$log" | sed 's/^/ /'
|
||
results="${results} FAIL $name ($log)
|
||
"
|
||
failed=$((failed + 1))
|
||
fi
|
||
done
|
||
|
||
echo
|
||
echo "============================================================"
|
||
echo " Summary"
|
||
echo "============================================================"
|
||
printf "%s" "$results"
|
||
echo
|
||
echo " failed: $failed / ${#GATES[@]}"
|
||
exit $failed
|