name: CI on: push: branches: [main] pull_request: schedule: - cron: '17 3 * * *' # nightly-integration only; every other job stays PR/push-triggered workflow_dispatch: # lets a human fire nightly-integration on demand 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: eslint (no-download-logic gate + general rules) if: steps.check.outputs.present == 'true' working-directory: extension run: | npm ci npx eslint . - name: web-ext lint if: steps.check.outputs.present == 'true' working-directory: extension run: | 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 nightly-integration: # Real veloxd + tools/testserver, 50 concurrent downloads mixing hostile modes, # every completed file's SHA-256 checked against testserver's own /sha256/ route, # veloxd's open-FD count checked flat across the run. Nightly, not per-PR: it's # ~2 minutes of real network I/O against a local server, not a schema check. # See tests/integration/README.md#nightly-integration-run for what each assertion # catches and the forced-failure transcript proving it isn't vacuous. if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Bootstrap toolchain run: sudo ./tools/bootstrap.sh - name: Configure run: cmake --preset dev - name: Build veloxd run: cmake --build --preset dev --target veloxd - name: Nightly integration run run: python3 tests/integration/nightly_run.py --veloxd build/dev/bin/veloxd --tasks 50 --timeout 180