PROTO wired the suite into ctest (label "conformance": the end-to-end `conformance` test that shells to run.sh, plus the native `conformance_cpp`); the CI job called run.sh directly. Two entry points, and the required M0 gate exercised only one of them, so the ctest registration could rot. The conformance job now configures, builds velox_conformance_cpp, and runs `ctest --preset dev -L conformance`. The dev test preset's noTestsAction: error is the rot guard: an empty label match exits non-zero instead of the old silent pass. build/sanitizers exclude the heavy e2e test with -E '^conformance$' (the dedicated job owns that run; conformance_cpp still runs under every sanitizer). ADR 0014 records the decision. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0143aKiohmDiyefJBwHDJJqw
223 lines
9.2 KiB
YAML
223 lines
9.2 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
# GitHub-hosted runners are Ubuntu 24.04; the project targets 26.04. bootstrap.sh warns
|
|
# but proceeds. Revisit when 26.04 runners exist.
|
|
DEBIAN_FRONTEND: noninteractive
|
|
|
|
jobs:
|
|
# --- fast lint jobs: no compiler, no heavy deps -------------------------------------
|
|
clang-format:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Install clang-format
|
|
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends clang-format
|
|
- name: Check formatting
|
|
run: |
|
|
shopt -s globstar nullglob
|
|
files=(core/**/*.{cpp,hpp} daemon/**/*.{cpp,hpp} cli/**/*.{cpp,hpp} nmhost/**/*.{cpp,hpp})
|
|
if [ ${#files[@]} -eq 0 ]; then echo "no C++ sources yet — skipping"; exit 0; fi
|
|
printf '%s\n' "${files[@]}"
|
|
clang-format --dry-run --Werror "${files[@]}"
|
|
|
|
testserver:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.12'
|
|
- name: testserver self-test
|
|
run: python3 tools/testserver/selftest.py
|
|
|
|
bootstrap-script:
|
|
# Keeps tools/bootstrap.sh honest on the runner image: it must run clean and its
|
|
# --check must pass. ubuntu-latest is 24.04; the project ships on 26.04, so this
|
|
# exercises the 24.04 archive only. bootstrap-script-2604 below is what validates
|
|
# the package names against the release the project actually targets.
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- run: sudo ./tools/bootstrap.sh --with-clang
|
|
- run: ./tools/bootstrap.sh --check --with-clang
|
|
# Cheap: apt-cache only. Validates the M6 packaging names now so they can't rot
|
|
# unnoticed until M6.
|
|
- run: ./tools/bootstrap.sh --check --with-clang --packaging
|
|
|
|
bootstrap-script-2604:
|
|
# The project targets 26.04 and GitHub has no 26.04 runner image yet, so the one
|
|
# automated place bootstrap.sh runs is on the wrong release to catch a name that is
|
|
# valid on 24.04 and gone on 26.04 — which is exactly how libqt6svg6-dev reached a
|
|
# contributor's VM (gui/docs/pkg-qa-requests-m1.md R1/R2). Run the real install in a
|
|
# 26.04 container, with --with-clang: the fuzz toolchain had never been exercised
|
|
# anywhere (CORE ran clang++-21 directly because it can't sudo).
|
|
runs-on: ubuntu-latest
|
|
container: ubuntu:26.04
|
|
steps:
|
|
- name: Base tools for checkout
|
|
run: |
|
|
apt-get update -qq
|
|
apt-get install -y --no-install-recommends ca-certificates git sudo
|
|
- uses: actions/checkout@v4
|
|
- name: Full bootstrap on 26.04 (--with-clang)
|
|
run: ./tools/bootstrap.sh --with-clang
|
|
- name: Re-verify
|
|
run: ./tools/bootstrap.sh --check --with-clang
|
|
|
|
extension-lint:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- id: check
|
|
# "Has EXT landed?" is answered by a manifest, not by extension/package.json:
|
|
# a guard keyed to one filename passes vacuously the day EXT ships the lane
|
|
# under any other name. Skip only when the lane genuinely is not here; once a
|
|
# manifest exists, a missing lint entrypoint is a hard failure, not a skip.
|
|
run: |
|
|
manifest=""
|
|
for m in extension/manifest.json extension/src/manifest.json extension/public/manifest.json; do
|
|
if [ -f "$m" ]; then manifest="$m"; break; fi
|
|
done
|
|
if [ -z "$manifest" ]; then
|
|
echo "extension/ has not landed yet (no manifest.json) — skipping web-ext lint."
|
|
echo "present=false" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
echo "EXT has landed: $manifest"
|
|
echo "present=true" >> "$GITHUB_OUTPUT"
|
|
if [ ! -f extension/package.json ]; then
|
|
echo "::error::$manifest exists but extension/package.json does not — this job" \
|
|
"cannot lint the extension. Wire web-ext lint in here; do not let the check" \
|
|
"pass green over an unlinted lane."
|
|
exit 1
|
|
fi
|
|
- uses: actions/setup-node@v4
|
|
if: steps.check.outputs.present == 'true'
|
|
with:
|
|
node-version: '22'
|
|
- name: web-ext lint
|
|
if: steps.check.outputs.present == 'true'
|
|
working-directory: extension
|
|
run: |
|
|
npm ci
|
|
npx web-ext lint --source-dir .
|
|
|
|
# --- build + test matrix ----------------------------------------------------------
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
compiler: [gcc, clang]
|
|
env:
|
|
CC: ${{ matrix.compiler == 'gcc' && 'gcc' || 'clang' }}
|
|
CXX: ${{ matrix.compiler == 'gcc' && 'g++' || 'clang++' }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Bootstrap toolchain
|
|
run: sudo ./tools/bootstrap.sh --with-clang
|
|
- name: Configure
|
|
run: cmake --preset ci
|
|
- name: Build
|
|
run: cmake --build --preset ci
|
|
- name: Test
|
|
# -E '^conformance$' drops the end-to-end run.sh test (npm installs, its own
|
|
# mockd, ~24 s); the dedicated `conformance` job owns that one run. The native
|
|
# `conformance_cpp` test is not excluded and still runs on every matrix leg.
|
|
run: ctest --preset ci --output-on-failure -E '^conformance$'
|
|
|
|
sanitizers:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
preset: [dev, tsan] # dev = ASan + UBSan
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Bootstrap toolchain
|
|
run: sudo ./tools/bootstrap.sh --with-clang
|
|
- name: Configure
|
|
run: cmake --preset ${{ matrix.preset }}
|
|
- name: Build
|
|
run: cmake --build --preset ${{ matrix.preset }}
|
|
- name: Test
|
|
# See the build job: the end-to-end run.sh test is the dedicated `conformance`
|
|
# job's; sanitizing a suite that shells out to its own unsanitized g++ build and
|
|
# a node process buys nothing. `conformance_cpp` still runs here under the sanitizer.
|
|
run: ctest --preset ${{ matrix.preset }} --output-on-failure -E '^conformance$'
|
|
env:
|
|
ASAN_OPTIONS: detect_leaks=1:halt_on_error=1
|
|
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
|
TSAN_OPTIONS: halt_on_error=1
|
|
|
|
clang-tidy:
|
|
# Advisory through M1 (see .clang-tidy WarningsAsErrors: ''); becomes required at M2.
|
|
runs-on: ubuntu-latest
|
|
continue-on-error: true
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- id: check
|
|
run: |
|
|
if ls core/CMakeLists.txt daemon/CMakeLists.txt >/dev/null 2>&1; then
|
|
echo "present=true" >> "$GITHUB_OUTPUT"
|
|
else echo "present=false" >> "$GITHUB_OUTPUT"; fi
|
|
- name: Bootstrap toolchain
|
|
if: steps.check.outputs.present == 'true'
|
|
run: sudo ./tools/bootstrap.sh
|
|
- name: Configure (for compile_commands.json)
|
|
if: steps.check.outputs.present == 'true'
|
|
run: cmake --preset dev
|
|
- name: Run clang-tidy on changed files
|
|
if: steps.check.outputs.present == 'true'
|
|
run: |
|
|
mapfile -t files < <(git diff --name-only --diff-filter=ACM \
|
|
"${{ github.event.pull_request.base.sha || 'HEAD~1' }}" HEAD \
|
|
-- '*.cpp' '*.hpp' || true)
|
|
[ ${#files[@]} -eq 0 ] && { echo "no C++ changes"; exit 0; }
|
|
printf '%s\n' "${files[@]}"
|
|
clang-tidy -p build/dev "${files[@]}"
|
|
- name: skipped
|
|
if: steps.check.outputs.present == 'false'
|
|
run: echo "no C++ lane has landed a CMakeLists yet — skipping clang-tidy"
|
|
|
|
conformance:
|
|
# The M0 exit gate. Proves the generated C++ daemon surface and the generated TS
|
|
# extension surface agree with contracts/fixtures without either side having run
|
|
# against the other. Required on every PR — branch protection is a repo setting,
|
|
# recorded in .github/BRANCH_PROTECTION.md.
|
|
#
|
|
# Canonical entry point is `ctest -L conformance`. tests/conformance/CMakeLists.txt
|
|
# (owned by PROTO) registers two tests under that label: `conformance`, which shells
|
|
# out to run.sh end to end, and `conformance_cpp`, the finer-grained native runner.
|
|
# CI drives it exactly as a developer does — one definition of "the suite passed",
|
|
# and PROTO's registration is on the exercised path so it cannot rot. See
|
|
# docs/adr/0014-conformance-runs-through-ctest.md.
|
|
#
|
|
# `noTestsAction: error` in the dev test preset is the rot guard: if the label ever
|
|
# matches nothing (registration deleted, typo), ctest exits non-zero instead of
|
|
# passing vacuously.
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Bootstrap toolchain
|
|
run: sudo ./tools/bootstrap.sh
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '22' # apt ships < 20; run.sh's TS replay runner needs >= 20
|
|
- name: Configure
|
|
run: cmake --preset dev
|
|
- name: Build the native conformance runner
|
|
run: cmake --build --preset dev --target velox_conformance_cpp
|
|
- name: Run conformance (ctest -L conformance)
|
|
run: ctest --preset dev -L conformance --output-on-failure
|