Every CI job except test-fast and mirror-to-codeberg now runs only when explicitly requested via a PR comment, instead of on every push or PR sync. This keeps the Pi runner idle during WIP commits and lets the author decide when to pay each job's cost. Trigger commands: /test-cgal → CGAL test suite (277 tests, ~5 min build + 31 s run) /quality-gates → license/codespell/shellcheck/cgal-conventions (~30 s) /docs → Doxygen build + warning summary (~2 min) /links → Markdown internal link check (~10 s) All comment-triggered jobs check: event is issue_comment AND comment is on a PR (issue.pull_request != null) AND comment body contains the trigger word AND checkout uses refs/pull/N/head (not the default branch) Jobs that stay automatic: test-fast — runs on every push (26 pure-math tests, < 5 s) mirror-to-codeberg — unchanged Jobs that keep additional triggers: markdown-links — weekly cron (Mon 05:00 UTC) + workflow_dispatch doc-build — workflow_dispatch (for manual runs outside a PR) quality-gates drops `needs: test-fast` — it now runs independently when comment-triggered (caller decides whether test-fast passed first). CLAUDE.md CI pipeline table updated with all five jobs and their new trigger descriptions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
199 lines
9.0 KiB
YAML
199 lines
9.0 KiB
YAML
name: C++ Tests
|
||
|
||
on:
|
||
push:
|
||
branches:
|
||
- main
|
||
- dev
|
||
- "claude/**"
|
||
- "feature/**"
|
||
pull_request:
|
||
# /test-cgal comment on any PR triggers the CGAL test suite manually.
|
||
issue_comment:
|
||
types: [created]
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Job 1 — test-fast
|
||
# Pure-math tests (Clausen, ImLi₂, Hyper-ideal geometry).
|
||
# No CGAL, no Boost. Eigen + GTest only. Runs on ALL branches.
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
jobs:
|
||
test-fast:
|
||
runs-on: eulernest
|
||
container:
|
||
image: git.eulernest.eu/conformallab/ci-cpp:latest
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Configure (tests-only)
|
||
run: cmake -S code -B build -DCMAKE_BUILD_TYPE=Release
|
||
|
||
- name: Build
|
||
run: nice -n 19 cmake --build build --target conformallab_tests -j$(nproc)
|
||
|
||
- name: Run tests
|
||
run: >
|
||
ctest --test-dir build
|
||
--output-on-failure
|
||
--output-junit test-results.xml
|
||
|
||
- name: Summary
|
||
if: always()
|
||
run: |
|
||
if [ -f test-results.xml ]; then
|
||
total=$(grep -o 'tests="[0-9]*"' test-results.xml | grep -o '[0-9]*' | head -1)
|
||
failed=$(grep -o 'failures="[0-9]*"' test-results.xml | grep -o '[0-9]*' | head -1)
|
||
skipped=$(grep -o 'skipped="[0-9]*"' test-results.xml | grep -o '[0-9]*' | head -1)
|
||
passed=$(( ${total:-0} - ${failed:-0} - ${skipped:-0} ))
|
||
echo "FAST ▸ TOTAL ${total:-0} | PASSED $passed | FAILED ${failed:-0} | SKIPPED ${skipped:-0}"
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Job 2 — test-cgal
|
||
# Full CGAL test suite (Phase 3–7, 158 tests).
|
||
# Runs ONLY on pull requests (not on direct pushes to dev/main).
|
||
# Starts only after test-fast succeeds.
|
||
#
|
||
# Uses -DWITH_CGAL_TESTS=ON (not -DWITH_CGAL=ON) to avoid building
|
||
# Viewer/GLFW — the CI container has no wayland-scanner.
|
||
#
|
||
# Boost (libboost-dev) is already present in the container since the image rebuild.
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# ─── CGAL tests: manual trigger via PR comment ───────────────────────────
|
||
#
|
||
# Trigger: write "/test-cgal" as a comment on any pull request.
|
||
#
|
||
# Why comment-triggered (not automatic on every PR push):
|
||
# The Pi runner has 3-4 GB RAM total with swap constantly loaded.
|
||
# Even with LOW_MEMORY_BUILD the CGAL build takes ~5 min and stresses
|
||
# the runner. Triggering on every push would queue builds faster than
|
||
# the Pi can drain them. A manual trigger gives full control: run the
|
||
# 277-test suite when a PR is ready for review, not on every WIP commit.
|
||
#
|
||
# How it works:
|
||
# - `issue_comment` fires on all PR + issue comments.
|
||
# - The `if:` condition checks:
|
||
# 1. Event is a comment (not a push or PR sync)
|
||
# 2. The comment is on a PR (issue.pull_request != null)
|
||
# 3. The comment body contains "/test-cgal"
|
||
# - The checkout uses refs/pull/N/head to get the PR branch, because
|
||
# `issue_comment` does not set GITHUB_REF to the PR branch by default.
|
||
#
|
||
# LOW_MEMORY_BUILD applies four RAM-saving measures so the build fits in
|
||
# 2000 MB: -O0, PCH off, unity batch 1, --no-keep-memory linker.
|
||
# See code/tests/cgal/CMakeLists.txt for the full explanation.
|
||
test-cgal:
|
||
needs: test-fast
|
||
if: |
|
||
github.event_name == 'issue_comment' &&
|
||
github.event.issue.pull_request != null &&
|
||
contains(github.event.comment.body, '/test-cgal')
|
||
runs-on: eulernest
|
||
container:
|
||
image: git.eulernest.eu/conformallab/ci-cpp:latest
|
||
# 2000 MB hard limit; 1000 MB swap headroom (memory-swap = RAM + swap).
|
||
# Uses ~half of the Pi's 3-4 GB, leaving margin for OS + runner daemon.
|
||
# With LOW_MEMORY_BUILD peak per TU is ~150-200 MB → well within limit.
|
||
options: "--memory=2000m --memory-swap=3000m"
|
||
|
||
steps:
|
||
# Check out the PR branch, not the default branch.
|
||
# issue_comment events set GITHUB_REF to the default branch; we need
|
||
# refs/pull/N/head to get the actual PR code.
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
ref: refs/pull/${{ github.event.issue.number }}/head
|
||
|
||
- name: Configure (LOW_MEMORY_BUILD — -O0, no PCH, unity batch 1)
|
||
run: |
|
||
cmake -S code -B build \
|
||
-DWITH_CGAL_TESTS=ON \
|
||
-DCMAKE_BUILD_TYPE=Release \
|
||
-DCONFORMALLAB_LOW_MEMORY_BUILD=ON
|
||
|
||
- name: Build CGAL-Tests
|
||
run: nice -n 19 cmake --build build --target conformallab_cgal_tests -j1
|
||
|
||
- name: Run CGAL-Tests
|
||
run: >
|
||
ctest --test-dir build
|
||
-R "^cgal\."
|
||
--output-on-failure
|
||
--output-junit cgal-results.xml
|
||
|
||
- name: Summary
|
||
if: always()
|
||
run: |
|
||
if [ -f cgal-results.xml ]; then
|
||
total=$(grep -o 'tests="[0-9]*"' cgal-results.xml | grep -o '[0-9]*' | head -1)
|
||
failed=$(grep -o 'failures="[0-9]*"' cgal-results.xml | grep -o '[0-9]*' | head -1)
|
||
skipped=$(grep -o 'skipped="[0-9]*"' cgal-results.xml | grep -o '[0-9]*' | head -1)
|
||
passed=$(( ${total:-0} - ${failed:-0} - ${skipped:-0} ))
|
||
echo "CGAL ▸ TOTAL ${total:-0} | PASSED $passed | FAILED ${failed:-0} | SKIPPED ${skipped:-0}"
|
||
fi
|
||
|
||
# ── Structural gate: doc/api/tests.md totals match ctest reality ───
|
||
# Single source of truth for test counts (see doc/release-policy.md).
|
||
# Reuses the already-built ./build dir via BUILD_DIR env var, so this
|
||
# adds ~5 s on top of the existing CGAL job.
|
||
- name: Verify test-count consistency (doc/api/tests.md)
|
||
run: BUILD_DIR=build bash scripts/check-test-counts.sh
|
||
|
||
# ── Structural gate: end-to-end smoke (try_it.sh) ──────────────────
|
||
# The user-facing quick-start script: configure + build + run the
|
||
# full ctest + run the Euclidean example on a bundled mesh. If
|
||
# this regresses, README quick-start instructions are broken.
|
||
# try_it.sh creates its own build-try/ — accept the ~3 min cost as
|
||
# the price of guaranteeing the documented workflow stays working.
|
||
- name: End-to-end smoke test (scripts/try_it.sh)
|
||
run: bash scripts/try_it.sh
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Job 3 — quality-gates (style + convention block)
|
||
#
|
||
# Trigger: write "/quality-gates" as a comment on any pull request.
|
||
#
|
||
# Cheap, deterministic checks (~30 s): license headers, CGAL conventions,
|
||
# codespell, shellcheck. Runs independently of test-fast when comment-
|
||
# triggered (no `needs:` — the caller decides when to invoke it).
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
quality-gates:
|
||
if: |
|
||
github.event_name == 'issue_comment' &&
|
||
github.event.issue.pull_request != null &&
|
||
contains(github.event.comment.body, '/quality-gates')
|
||
runs-on: eulernest
|
||
container:
|
||
image: git.eulernest.eu/conformallab/ci-cpp:latest
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
ref: refs/pull/${{ github.event.issue.number }}/head
|
||
|
||
- name: Install codespell + shellcheck (job-local)
|
||
run: |
|
||
apt-get update -qq
|
||
apt-get install -y --no-install-recommends \
|
||
codespell shellcheck
|
||
|
||
- name: License headers (every C++ source carries MIT SPDX)
|
||
run: bash scripts/quality/license-headers.sh
|
||
|
||
- name: CGAL conventions (6 rules over CGAL public headers)
|
||
run: python3 scripts/quality/cgal-conventions.py
|
||
|
||
- name: codespell (docs + source comments + script messages)
|
||
run: bash scripts/quality/codespell.sh
|
||
|
||
- name: shellcheck (scripts/**/*.sh, severity=warning, strict)
|
||
run: bash scripts/quality/shellcheck.sh --strict
|
||
|
||
- name: Summary
|
||
if: always()
|
||
run: |
|
||
echo "QUALITY ▸ all four gates passed."
|
||
echo " see scripts/quality/README.md for the full catalogue"
|
||
echo " (sanitizers, clang-tidy, coverage, etc. are local-only)"
|