Repository navigation
rewrite(phase2): port watchdog + build allocation fault-injection seam, ported from c-RCP #165
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, rewrite/v3-from-c-rcp] | |
| pull_request: | |
| branches: [main, rewrite/v3-from-c-rcp] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ── Build and test (cross-platform matrix) ─────────────────────────────────── | |
| build-and-test: | |
| name: ${{ matrix.os }} / ${{ matrix.compiler }} / C++${{ matrix.std }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-22.04 | |
| compiler: clang-14 | |
| std: 17 | |
| cc: clang-14 | |
| cxx: clang++-14 | |
| - os: ubuntu-22.04 | |
| compiler: gcc-12 | |
| std: 17 | |
| cc: gcc-12 | |
| cxx: g++-12 | |
| - os: ubuntu-22.04 | |
| compiler: gcc-12 | |
| std: 20 | |
| cc: gcc-12 | |
| cxx: g++-12 | |
| - os: macos-14 | |
| compiler: clang | |
| std: 17 | |
| cc: clang | |
| cxx: clang++ | |
| - os: windows-2022 | |
| compiler: msvc | |
| std: 17 | |
| cc: cl | |
| cxx: cl | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install tools (Ubuntu) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y cmake ninja-build | |
| - name: Set up MSVC environment (Windows) | |
| if: runner.os == 'Windows' | |
| uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1 | |
| - name: Configure | |
| shell: bash | |
| env: | |
| CC: ${{ matrix.cc }} | |
| CXX: ${{ matrix.cxx }} | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_CXX_STANDARD=${{ matrix.std }} \ | |
| -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ | |
| -DRELAY_BUILD_CLI=ON \ | |
| -G Ninja | |
| - name: Build | |
| run: cmake --build build --parallel | |
| - name: Test | |
| run: ctest --test-dir build --output-on-failure -j1 | |
| - name: Upload compile_commands.json (Linux clang-14 C++17 only) | |
| if: matrix.os == 'ubuntu-22.04' && matrix.compiler == 'clang-14' && matrix.std == 17 | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: compile-commands | |
| path: build/compile_commands.json | |
| # ── rcp/l2.hpp real-socket round trip (Linux, needs CAP_NET_RAW/root) ──────── | |
| # Exercises rcp::l2::Server/Client over a real veth pair — real AF_PACKET | |
| # sockets, a real Linux network interface pair, real Ethernet frames. This | |
| # is deliberately separate from build-and-test above: it needs elevated | |
| # privileges (CAP_NET_RAW, satisfied here via `sudo`) that job intentionally | |
| # does not run with, and tests/test_l2.cpp's own header comment documents | |
| # why its pure codec tests must not require them either. | |
| l2-veth: | |
| name: l2 veth round trip (rcp::l2::Server/Client, real frames) | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y cmake ninja-build | |
| - name: Configure | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_CXX_STANDARD=17 \ | |
| -G Ninja | |
| - name: Build l2_veth_roundtrip | |
| run: cmake --build build --target l2_veth_roundtrip --parallel | |
| - name: Create veth pair | |
| run: | | |
| sudo ip link add veth0 type veth peer name veth1 | |
| sudo ip link set veth0 up | |
| sudo ip link set veth1 up | |
| - name: Run real Server/Client round trip over the veth pair | |
| run: | | |
| SERVER_MAC=$(cat /sys/class/net/veth1/address) | |
| echo "veth1 MAC: ${SERVER_MAC}" | |
| sudo ./build/tests/l2_veth_roundtrip veth1 veth0 "${SERVER_MAC}" | |
| # ── RELAY conformance gate (spec §12) ──────────────────────────────────────── | |
| relay-conform: | |
| name: relay conform (RELAY §12 conformance) | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - name: Checkout cpp-RCP | |
| uses: actions/checkout@v4 | |
| # Pinned to the latest tagged RELAY release rather than floating on the | |
| # default branch (issue #83 / cpp-RCP-15): an unpinned checkout means | |
| # this job's pass/fail can change with no cpp-RCP-side change at all | |
| # whenever RELAY's main branch moves, which is not reproducible CI. | |
| # Bump this SHA/tag deliberately (in its own commit) when cpp-RCP is | |
| # ready to validate against a newer RELAY conformance ruleset. | |
| - name: Checkout RELAY (authoritative conformance validator) | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: SoundMatt/RELAY | |
| ref: 810d1877362145a407b865426bb007657e42d00f # v2.0.4 | |
| path: RELAY | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.25' | |
| - name: Build relay conformance tool | |
| working-directory: RELAY | |
| run: go build -o "$RUNNER_TEMP/relay" ./cmd/relay | |
| - name: Install build tools | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y cmake ninja-build | |
| - name: Build cpp-rcp CLI | |
| run: | | |
| cmake -B build -DCMAKE_BUILD_TYPE=Release -DRELAY_BUILD_CLI=ON -G Ninja | |
| cmake --build build --target cpp-rcp --parallel | |
| - name: Run relay conform (strict) | |
| run: '"$RUNNER_TEMP/relay" conform --strict build/cpp-rcp' | |
| # ── clang-tidy ─────────────────────────────────────────────────────────────── | |
| clang-tidy: | |
| name: clang-tidy | |
| runs-on: ubuntu-22.04 | |
| needs: build-and-test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y cmake ninja-build clang-tidy | |
| - name: Configure | |
| env: | |
| CC: clang-14 | |
| CXX: clang++-14 | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_CXX_STANDARD=17 \ | |
| -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ | |
| -G Ninja | |
| - name: Build (generate compile_commands.json) | |
| run: cmake --build build --parallel | |
| - name: Run clang-tidy | |
| # Only pass tests/*.cpp as translation units: this project's | |
| # include/rcp/*.hpp headers are header-only (no standalone compile | |
| # command in compile_commands.json for a .hpp on its own), so | |
| # invoking clang-tidy directly on a header fails with a | |
| # clang-diagnostic-error "file not found" on its own #includes | |
| # (no -I flags to resolve them) rather than a real lint finding. | |
| # .clang-tidy's HeaderFilterRegex ('include/rcp/') still surfaces | |
| # findings from those headers as they're transitively included by | |
| # the .cpp translation units below. | |
| run: | | |
| find tests -name '*.cpp' | \ | |
| xargs clang-tidy -p build --warnings-as-errors='*' | |
| # ── ASan/UBSan (regmap + faultinject) ───────────────────────────────────────── | |
| # Scoped to the two suites whose headers hand out raw references into | |
| # containers they also mutate: | |
| # | |
| # - rcp/regmap.hpp's Ep0 does raw std::vector<T> indexing across | |
| # per-endpoint access-control checks and the config vectors those checks | |
| # gate (cpp-RCP-N2-01/N2-02, issues #64/#65). | |
| # - rcp/faultinject.hpp's Interceptor picks a rule out of a std::vector it | |
| # erases from in the same pass (cpp-RCP-D6). That defect was live and | |
| # the *existing* test_faultinject cases already reproduced it under | |
| # ASan — this job simply wasn't running them, which is why CI stayed | |
| # green. Keeping it in scope makes that class of defect gated rather | |
| # than fixed once. | |
| # | |
| # This is additive to build-and-test above, not a replacement for it. | |
| asan-ubsan-regmap: | |
| name: ASan/UBSan (regmap + faultinject) | |
| runs-on: ubuntu-22.04 | |
| needs: build-and-test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y cmake ninja-build | |
| - name: Configure (ASan + UBSan) | |
| env: | |
| CC: clang-14 | |
| CXX: clang++-14 | |
| run: | | |
| cmake -B build-asan \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_CXX_STANDARD=17 \ | |
| -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" \ | |
| -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" \ | |
| -G Ninja | |
| - name: Build test_regmap and test_faultinject | |
| run: cmake --build build-asan --target test_regmap test_faultinject --parallel | |
| - name: Run test_regmap under ASan/UBSan | |
| env: | |
| ASAN_OPTIONS: halt_on_error=1 | |
| UBSAN_OPTIONS: halt_on_error=1,print_stacktrace=1 | |
| run: ./build-asan/tests/test_regmap | |
| - name: Run test_faultinject under ASan/UBSan | |
| env: | |
| ASAN_OPTIONS: halt_on_error=1 | |
| UBSAN_OPTIONS: halt_on_error=1,print_stacktrace=1 | |
| run: ./build-asan/tests/test_faultinject | |
| # ── LCOV coverage ───────────────────────────────────────────────────────────── | |
| coverage: | |
| name: Coverage (LCOV) | |
| runs-on: ubuntu-22.04 | |
| needs: build-and-test | |
| outputs: | |
| coverage-info: coverage.info | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y cmake ninja-build lcov | |
| - name: Configure (coverage build) | |
| env: | |
| CC: gcc-12 | |
| CXX: g++-12 | |
| run: | | |
| cmake -B build-cov \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_CXX_FLAGS="--coverage -O0" \ | |
| -DCMAKE_EXE_LINKER_FLAGS="--coverage" \ | |
| -G Ninja | |
| - name: Build | |
| run: cmake --build build-cov --parallel | |
| - name: Test | |
| run: ctest --test-dir build-cov --output-on-failure -j1 | |
| - name: Collect LCOV data | |
| run: | | |
| # --rc lcov_branch_coverage=1 must be passed to BOTH the --capture | |
| # and --remove invocations below (lcov re-reads/rewrites the .info | |
| # file's branch records on every invocation, so omitting it on | |
| # either step silently drops branch data even if the other step | |
| # collected it). Without it, totalBranches/hitBranches downstream | |
| # in coverage-report.json are always 0/0 -- a vacuous "nothing | |
| # failed" rather than a genuine 100%-branch-coverage result against | |
| # this project's declared DAL-B target (see issues #67/#68). | |
| lcov --capture \ | |
| --directory build-cov \ | |
| --gcov-tool gcov-12 \ | |
| --rc lcov_branch_coverage=1 \ | |
| --output-file coverage.info | |
| # /usr/include/* excludes STL/toolchain system headers, which | |
| # otherwise dominate the file-entry count (confirmed: 101/153 file | |
| # entries and ~5979/9034 lines were system headers, dragging | |
| # blended line coverage down to ~68.8% vs. ~91.5% for actual | |
| # project files alone -- see issue #67/#68). | |
| lcov --remove coverage.info \ | |
| '*/tests/*' '*/catch2/*' '*/FetchContent/*' '*/cmake-build*/*' \ | |
| '/usr/include/*' \ | |
| --rc lcov_branch_coverage=1 \ | |
| --output-file coverage.info | |
| - name: Upload coverage report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-info | |
| path: coverage.info | |
| # ── Build cpp-FuSa binary (shared by all cpfusa-* jobs) ────────────────────── | |
| cpfusa-build: | |
| name: Build cpp-FuSa | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - name: Install tools | |
| run: sudo apt-get update -qq && sudo apt-get install -y cmake ninja-build | |
| - name: Build cpp-FuSa from source | |
| run: | | |
| git clone --depth=1 --branch v0.18.0 https://github.com/SoundMatt/cpp-FuSa /tmp/cpfusa | |
| cmake -B /tmp/cpfusa/build -S /tmp/cpfusa \ | |
| -DCMAKE_BUILD_TYPE=Release -G Ninja | |
| cmake --build /tmp/cpfusa/build --parallel | |
| - name: Upload cpfusa binary | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build/cpfusa | |
| # ── Helper: download cpfusa binary ─────────────────────────────────────────── | |
| # (Not a real job — these steps are inlined in each cpfusa-* job below.) | |
| # ── cpp-FuSa: built-in safety check (FUSA001–005) ──────────────────────────── | |
| cpfusa-check: | |
| name: cpfusa check --strict | |
| runs-on: ubuntu-22.04 | |
| needs: [build-and-test, cpfusa-build] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Safety check (0 errors required) | |
| run: /tmp/cpfusa/build/cpfusa check | |
| - name: Verify required safety artifacts are present | |
| run: | | |
| missing=0 | |
| for f in \ | |
| .fusa.json \ | |
| .fusa-reqs.json \ | |
| .fusa-hara.json \ | |
| .fusa-problems.json \ | |
| .fusa-iec62443.json \ | |
| SECURITY.md \ | |
| INCIDENT-RESPONSE.md \ | |
| SAFETY_PLAN.md \ | |
| .github/CODEOWNERS; do | |
| if [ ! -f "$f" ]; then | |
| echo "::error::Required safety artifact missing: $f" | |
| missing=$((missing + 1)) | |
| fi | |
| done | |
| [ "$missing" -eq 0 ] || exit 1 | |
| # ── cpp-FuSa: MISRA/AUTOSAR/JSF++ lint (LINT001–010) ───────────────────────── | |
| cpfusa-lint: | |
| name: cpfusa lint (MISRA/AUTOSAR) | |
| runs-on: ubuntu-22.04 | |
| needs: [build-and-test, cpfusa-build] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: MISRA/AUTOSAR/JSF++ lint | |
| run: /tmp/cpfusa/build/cpfusa lint | |
| # ── cpp-FuSa: static analysis (clang-tidy + cppcheck + own passes) ─────────── | |
| cpfusa-analyze: | |
| name: cpfusa analyze | |
| runs-on: ubuntu-22.04 | |
| needs: [build-and-test, cpfusa-build] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install analysis tools | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y cmake ninja-build clang-tidy cppcheck | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Configure (generate compile_commands.json) | |
| env: | |
| CC: clang-14 | |
| CXX: clang++-14 | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_CXX_STANDARD=17 \ | |
| -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ | |
| -G Ninja | |
| - name: Build | |
| run: cmake --build build --parallel | |
| - name: Static analysis | |
| run: /tmp/cpfusa/build/cpfusa analyze || true | |
| # ── cpp-FuSa: 20 CWE-mapped cybersecurity rules (ISO 21434) ───────────────── | |
| cpfusa-cyber: | |
| name: cpfusa cyber --strict | |
| runs-on: ubuntu-22.04 | |
| needs: [build-and-test, cpfusa-build] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Cybersecurity rules (ISO 21434) | |
| run: /tmp/cpfusa/build/cpfusa cyber | |
| # ── cpp-FuSa: requirements traceability (≥80% coverage gate) ───────────────── | |
| cpfusa-trace: | |
| name: cpfusa trace (100% coverage) | |
| runs-on: ubuntu-22.04 | |
| needs: [build-and-test, cpfusa-build] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Traceability check (all requirements traced) | |
| run: /tmp/cpfusa/build/cpfusa trace | |
| # Informational only, not a gate: this dumps the gap detail for human | |
| # review. The actual gate for this job is the "Enforce 100% requirement | |
| # coverage" step below, which runs without `|| true` and fails the job | |
| # for real. | |
| - name: Traceability gaps report (informational, non-gating) | |
| run: /tmp/cpfusa/build/cpfusa trace --gaps || true | |
| # RELAY §20.1: every requirement MUST be traced (100%). NOTE: despite | |
| # this job's own prior "traced AND tested" comment, --req-coverage | |
| # only gates cpfusa's annotation_coverage (traced) metric -- confirmed | |
| # by direct read of cpp-FuSa v0.18.0's trace.cpp, which wires | |
| # --req-coverage to annotation_coverage alone, never to `tested`. The | |
| # "AND tested" half was previously unenforced (this repo's own | |
| # `tested`/`total` has happened to be 100% regardless, so nothing was | |
| # ever observed to regress, but nothing was actually checking it). | |
| - name: Enforce 100% requirement coverage (traced) | |
| run: /tmp/cpfusa/build/cpfusa trace --req-coverage 100 | |
| # RELAY §20.1's other half: every requirement MUST also be tested | |
| # (100%). cpp-FuSa v0.18.0 has no CLI flag for this metric alone (its | |
| # --sec-tested flag is a differently-named, differently-scoped gate -- | |
| # see the next step's own header -- and is NOT a substitute here: it | |
| # currently has a bug, filed as SoundMatt/cpp-FuSa#64, that makes it | |
| # silently check general test coverage instead of security-test | |
| # coverage, so relying on it for "tested" would work today by | |
| # coincidence and break the moment that bug is fixed). `cpfusa trace | |
| # --format json`'s `coverage` object exposes `testedRequirements`/ | |
| # `totalRequirements` directly and correctly -- parsed here via jq, | |
| # matching the same JSON-over-text-scraping choice SoundMatt/c-RCP#577 | |
| # made for the identical problem in that sibling repo's own CI. | |
| - name: Enforce 100% requirement coverage (tested) | |
| run: | | |
| cov="$(/tmp/cpfusa/build/cpfusa trace --format json | jq '.coverage')" | |
| echo "$cov" | |
| total=$(echo "$cov" | jq '.totalRequirements') | |
| tested=$(echo "$cov" | jq '.testedRequirements') | |
| if [ "$tested" != "$total" ]; then | |
| echo "::error::'tested' metric gate failed: tested=$tested total=$total (must be equal for a 100% gate)" | |
| exit 1 | |
| fi | |
| echo "OK: $tested/$total tested" | |
| # Genuine security-test coverage: CYBERSECURITY.md's own six security | |
| # layers cite exactly 64 requirement ids as security-relevant | |
| # (REQ-TLS-001..010, REQ-AUTH-001..008, REQ-E2E-001..014, | |
| # REQ-RL-001..008, REQ-DISC-001..009, REQ-REGMAP-001..015), each now | |
| # marked `"severity": "cybersecurity"` in `.fusa-reqs.json` -- the | |
| # field cpp-FuSa's own trace.cpp already reads to compute | |
| # `secTestedRequirements` (a test annotation on a cybersecurity-marked | |
| # requirement), previously never populated so this metric was always | |
| # zero. Deliberately NOT using cpfusa's own `--sec-tested` CLI flag: | |
| # confirmed by direct read of v0.18.0's trace.cpp/cli.cpp that it | |
| # gates on the wrong variable (general test_coverage, not | |
| # sec_tested/total) -- the exact same bug class SoundMatt/c-FuSa#147 | |
| # fixed in c-FuSa, filed upstream as SoundMatt/cpp-FuSa#64, not yet | |
| # fixed. Parses `--format json`'s already-correct | |
| # `secTestedRequirements` field directly instead, at a 17% floor | |
| # (64/376 = 17.02%) -- NOT a claim that only 17% of this codebase is | |
| # security-relevant, a regression floor against the exact, | |
| # CYBERSECURITY.md-enumerated subset. Once cpp-FuSa#64 is fixed | |
| # upstream, this step can switch to `cpfusa trace --sec-tested 17` | |
| # directly. | |
| - name: Enforce security-test coverage (17% floor, real CYBERSECURITY.md subset) | |
| run: | | |
| cov="$(/tmp/cpfusa/build/cpfusa trace --format json | jq '.coverage')" | |
| echo "$cov" | |
| total=$(echo "$cov" | jq '.totalRequirements') | |
| sec=$(echo "$cov" | jq '.secTestedRequirements') | |
| pct=$(( sec * 100 / total )) | |
| if [ "$pct" -lt 17 ]; then | |
| echo "::error::sec-tested gate failed: ${pct}% ($sec/$total) < required 17%" | |
| exit 1 | |
| fi | |
| echo "OK: ${pct}% ($sec/$total) security-tested" | |
| # ── cpp-FuSa: ctest integration → .fusa-evidence.json ──────────────────────── | |
| cpfusa-verify: | |
| name: cpfusa verify (ctest evidence) | |
| runs-on: ubuntu-22.04 | |
| needs: [build-and-test, cpfusa-build] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install tools | |
| run: sudo apt-get update -qq && sudo apt-get install -y cmake ninja-build | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Configure | |
| env: | |
| CC: gcc-12 | |
| CXX: g++-12 | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_CXX_STANDARD=17 \ | |
| -G Ninja | |
| - name: Build | |
| run: cmake --build build --parallel | |
| - name: Run tests + generate evidence | |
| run: /tmp/cpfusa/build/cpfusa verify | |
| - name: Upload test evidence | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fusa-evidence | |
| path: .fusa-evidence.json | |
| # ── cpp-FuSa: tool qualification suite ─────────────────────────────────────── | |
| cpfusa-qualify: | |
| name: cpfusa qualify | |
| runs-on: ubuntu-22.04 | |
| needs: cpfusa-build | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Tool qualification (8 positive/negative test cases) | |
| run: /tmp/cpfusa/build/cpfusa qualify | |
| - name: Upload qualification report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: qualify-report | |
| path: qualify-report.json | |
| # ── cpp-FuSa: CMake dependency CVE scan ─────────────────────────────────────── | |
| cpfusa-vuln: | |
| name: cpfusa vuln (CVE scan) | |
| runs-on: ubuntu-22.04 | |
| needs: cpfusa-build | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Dependency vulnerability scan | |
| run: /tmp/cpfusa/build/cpfusa vuln | |
| - name: Upload vulnerability report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: vuln-report | |
| path: vuln.json | |
| # ── cpp-FuSa: impact analysis on PRs ───────────────────────────────────────── | |
| cpfusa-impact: | |
| name: cpfusa impact (change analysis) | |
| runs-on: ubuntu-22.04 | |
| if: github.event_name == 'pull_request' | |
| needs: [cpfusa-build, cpfusa-trace] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Change impact analysis | |
| run: /tmp/cpfusa/build/cpfusa impact | |
| # ── cpp-FuSa: full compliance report (aggregated) ──────────────────────────── | |
| cpfusa-report: | |
| name: cpfusa report (compliance) | |
| runs-on: ubuntu-22.04 | |
| needs: | |
| - build-and-test | |
| - coverage | |
| - cpfusa-check | |
| - cpfusa-lint | |
| - cpfusa-analyze | |
| - cpfusa-cyber | |
| - cpfusa-trace | |
| - cpfusa-verify | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: cpfusa-binary | |
| path: /tmp/cpfusa/build | |
| - run: chmod +x /tmp/cpfusa/build/cpfusa | |
| - name: Download coverage info | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: coverage-info | |
| path: . | |
| # Artifact generation only, not a gate: this project's stub bridge | |
| # implementations cannot reach the 100%-branch DAL-B threshold this | |
| # command grades against, so `cpfusa coverage` exits non-zero by | |
| # design today (same reasoning as release.yml's gap-report steps). | |
| # `|| true` is intentional here so the job can still produce and | |
| # upload coverage-report.json for inspection; it does not mean "DAL-B | |
| # coverage is enforced". The step immediately below is what actually | |
| # gates on this job's data (issues #67/#68). | |
| - name: LCOV structural coverage report (DO-178C, artifact only — non-gating) | |
| run: /tmp/cpfusa/build/cpfusa coverage --profile coverage.info --dal DAL-B --output coverage-report.json || true | |
| # Real gate (issues #67/#68): a coverage report with totalBranches==0 | |
| # is not "100% branch coverage met", it's "no branch data was | |
| # collected at all" -- a 0/0 ratio that would otherwise vacuously | |
| # satisfy any branch-coverage threshold check. Fail hard if that | |
| # happens, independent of whether the DAL-B step above is gating. | |
| - name: Assert branch coverage data is non-vacuous | |
| run: | | |
| total_branches=$(jq '.summary.totalBranches' coverage-report.json) | |
| echo "coverage-report.json summary.totalBranches=${total_branches}" | |
| if [ "$total_branches" -le 0 ]; then | |
| echo "::error::coverage-report.json reports totalBranches=${total_branches} — no branch coverage data was collected, so any branch-coverage threshold is being met vacuously (0/0), not genuinely. See issues #67/#68." | |
| exit 1 | |
| fi | |
| # Artifact generation only, not a gate: these convert the aggregated | |
| # findings (already gated individually by the cpfusa-check / -lint / | |
| # -cyber / -trace / -verify jobs this job `needs:`) into downloadable | |
| # JSON/SARIF report formats. `|| true` reflects that intent, not a | |
| # bug — there is nothing pass/fail-shaped about a format conversion. | |
| - name: Full compliance report (JSON, artifact only — non-gating) | |
| run: /tmp/cpfusa/build/cpfusa report --format json --output report.json || true | |
| - name: Full compliance report (SARIF, artifact only — non-gating) | |
| run: /tmp/cpfusa/build/cpfusa report --format sarif --output results.sarif || true | |
| # Artifact generation only, not a gate: records a point-in-time safety | |
| # metrics snapshot for trend tracking. There is no pass/fail threshold | |
| # associated with recording a snapshot. | |
| - name: Safety metrics snapshot (artifact only — non-gating) | |
| run: /tmp/cpfusa/build/cpfusa metrics record || true | |
| - name: Upload compliance report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: compliance-report | |
| path: | | |
| report.json | |
| results.sarif | |
| coverage-report.json |