fix: fusa-asil-b CI gate — pin cpp-FuSa v0.17.1, stop wiping requirements, enforce real gap thresholds #23
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] | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| 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@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 \ | |
| -G Ninja | |
| - name: Build | |
| run: cmake --build build --parallel | |
| - name: Test (single-threaded to avoid TempDir races) | |
| run: ctest --test-dir build --output-on-failure -j1 | |
| coverage: | |
| name: Coverage (LCOV) | |
| 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 lcov | |
| - name: Configure (coverage build) | |
| 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: | | |
| lcov --capture \ | |
| --directory build-cov \ | |
| --output-file coverage.info | |
| lcov --remove coverage.info \ | |
| '*/tests/*' '*/catch2/*' '*/FetchContent/*' '*/_deps/*' \ | |
| '/usr/include/*' '/usr/lib/*' \ | |
| --output-file coverage.info | |
| - name: Enforce 70% line coverage gate | |
| run: | | |
| COVERAGE=$(lcov --summary coverage.info 2>&1 | awk '/lines/ {gsub(/%/,""); print $2}') | |
| echo "Line coverage: ${COVERAGE}%" | |
| if ! awk "BEGIN {exit (${COVERAGE} + 0 >= 70) ? 0 : 1}"; then | |
| echo "::error::Line coverage ${COVERAGE}% is below the required 70%" | |
| exit 1 | |
| fi | |
| - name: Upload coverage report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report | |
| path: coverage.info | |
| relay-conform: | |
| name: RELAY conformance (relay conform) | |
| 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: Build CLI | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_CXX_STANDARD=17 \ | |
| -G Ninja | |
| cmake --build build --parallel --target cpp-lin-cli | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: 'stable' | |
| - name: Install relay tool | |
| run: go install github.com/SoundMatt/RELAY/cmd/relay@latest | |
| - name: RELAY conformance gate | |
| run: relay conform --strict ./build/cli/cpp-lin-cli | |
| - name: RELAY interop gate (§20 Continuous Conformance) | |
| run: relay interop --protocol LIN ./build/cli/cpp-lin-cli | |
| sanitizers: | |
| name: ASan + UBSan (IEC 61508 SIL-2 dynamic analysis) | |
| 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 gcc-12 g++-12 | |
| - name: Configure (ASan + UBSan) | |
| env: | |
| CC: gcc-12 | |
| CXX: g++-12 | |
| run: | | |
| cmake -B build-san \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -O1" \ | |
| -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" \ | |
| -G Ninja | |
| - name: Build | |
| run: cmake --build build-san --parallel | |
| - name: Test with sanitizers | |
| env: | |
| ASAN_OPTIONS: "halt_on_error=1:detect_stack_use_after_return=1" | |
| UBSAN_OPTIONS: "halt_on_error=1:print_stacktrace=1" | |
| run: ctest --test-dir build-san --output-on-failure -j1 | |
| tsan: | |
| name: ThreadSanitizer (REQ-VIRT-018 concurrent access) | |
| 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 gcc-12 g++-12 | |
| - name: Configure (ThreadSanitizer) | |
| env: | |
| CC: gcc-12 | |
| CXX: g++-12 | |
| run: | | |
| cmake -B build-tsan \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer -O1" \ | |
| -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=thread" \ | |
| -G Ninja | |
| - name: Build | |
| run: cmake --build build-tsan --parallel | |
| - name: Test with ThreadSanitizer | |
| env: | |
| TSAN_OPTIONS: "halt_on_error=1:second_deadlock_stack=1" | |
| run: ctest --test-dir build-tsan --output-on-failure -j1 | |
| fusa-asil-b: | |
| name: cpp-FuSa ASIL-B qualification | |
| runs-on: ubuntu-22.04 | |
| needs: build-and-test | |
| steps: | |
| - name: Check out cpp-LIN | |
| uses: actions/checkout@v4 | |
| with: | |
| path: cpp-LIN | |
| - name: Check out cpp-FuSa | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: SoundMatt/cpp-FuSa | |
| path: cpp-FuSa | |
| ref: v0.17.1 | |
| - name: Install tools | |
| run: sudo apt-get update -qq && sudo apt-get install -y cmake ninja-build zip | |
| - name: Build cpfusa | |
| run: | | |
| cmake -B cpp-FuSa/build \ | |
| -S cpp-FuSa \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -G Ninja | |
| cmake --build cpp-FuSa/build --parallel | |
| # No `cpfusa init` step: .fusa.json and .fusa-reqs.json are real, | |
| # populated, checked-in files (the latter is 50+ KB of hand-maintained | |
| # REQ-* entries). `init --force` unconditionally overwrote both with | |
| # an empty scaffold before the traceability step ever ran, so CI's | |
| # requirements coverage was always computed against zero requirements | |
| # regardless of the repo's real state (cpp-LIN#19). `init` is only | |
| # needed for first-time local setup, where these files don't exist yet. | |
| - name: cpfusa check | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa check --dir . | |
| - name: cpfusa lint | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa lint --dir . | |
| - name: cpfusa trace (requirements traceability) | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa trace --dir . | |
| - name: cpfusa cyber | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa cyber --write --dir . | |
| - name: cpfusa qualify (ASIL-B gate) | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa qualify --dir . | |
| - name: cpfusa hara init | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa hara init --project cpp-LIN --dir . || true | |
| # boundary/tara/fmea/safety-case/sas/sci run BEFORE the iso26262/ | |
| # iec61508 gap analyses below: several ISO 26262/IEC 61508 objectives | |
| # (e.g. §9-2.1 Safety case, §10.4 SCI) are graded by cpfusa on whether | |
| # safety-case.json/sci.json etc. already exist on disk. Gap-checking | |
| # before generating them undercounted objectives this repo actually | |
| # addresses. | |
| - name: cpfusa boundary | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa boundary --dir . | |
| - name: cpfusa tara | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa tara --dir . | |
| - name: cpfusa fmea | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa fmea --dir . | |
| - name: cpfusa safety-case | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa safety-case --dir . | |
| - name: cpfusa sas | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa sas --dir . | |
| - name: cpfusa sci | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa sci --dir . | |
| # These gates enforce a documented, currently-achieved baseline gap | |
| # count rather than requiring zero gaps outright: cpfusa v0.17.1 has | |
| # no evidence-detection logic at all for several objectives (e.g. §6.1 | |
| # Software architectural design, §8-6.2 Safety manual — see cpp-FuSa | |
| # src/iso26262/iso26262.cpp's detect_status(), which falls through to | |
| # Status::Gap unconditionally for those IDs regardless of what evidence | |
| # exists), so a "0 gaps" target is not achievable with this tool | |
| # version no matter how complete this repo's safety documentation is. | |
| # The gate below still turns this into a real, enforced check: it | |
| # fails the build if the gap count *regresses* past the current | |
| # baseline, rather than being structurally unable to fail at all | |
| # (cpp-LIN#20). See SAFETY_PLAN.md for this repo's ASIL-B *target* | |
| # framing (SEooC, not a completed-certification claim). | |
| - name: cpfusa iso26262 (ASIL-B) | |
| working-directory: cpp-LIN | |
| run: | | |
| ../cpp-FuSa/build/cpfusa iso26262 \ | |
| --asil ASIL-B \ | |
| --output iso26262-gap-report.json \ | |
| --dir . | |
| GAPS=$(jq '.summary.gaps' iso26262-gap-report.json) | |
| echo "ISO 26262 gaps: ${GAPS} (baseline: 13)" | |
| if [ "${GAPS}" -gt 13 ]; then | |
| echo "::error::ISO 26262 gap count ${GAPS} exceeds baseline of 13 — see iso26262-gap-report.json" | |
| exit 1 | |
| fi | |
| - name: cpfusa iec61508 (SIL-2) | |
| working-directory: cpp-LIN | |
| run: | | |
| ../cpp-FuSa/build/cpfusa iec61508 \ | |
| --sil SIL-2 \ | |
| --output iec61508-gap-report.json \ | |
| --dir . | |
| GAPS=$(jq '.summary.gaps' iec61508-gap-report.json) | |
| echo "IEC 61508 gaps: ${GAPS} (baseline: 11)" | |
| if [ "${GAPS}" -gt 11 ]; then | |
| echo "::error::IEC 61508 gap count ${GAPS} exceeds baseline of 11 — see iec61508-gap-report.json" | |
| exit 1 | |
| fi | |
| - name: cpfusa badge | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa badge --dir . | |
| - name: cpfusa vuln | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa vuln --dir . | |
| - name: cpfusa metrics record | |
| working-directory: cpp-LIN | |
| run: ../cpp-FuSa/build/cpfusa metrics record --dir . | |
| - name: cpfusa report (JSON) | |
| working-directory: cpp-LIN | |
| run: | | |
| ../cpp-FuSa/build/cpfusa report \ | |
| --format json \ | |
| --output check-report.json \ | |
| --dir . | |
| - name: Upload ASIL-B evidence artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: asil-b-evidence | |
| path: | | |
| cpp-LIN/check-report.json | |
| cpp-LIN/qualify-report.json | |
| cpp-LIN/cyber-report.json | |
| cpp-LIN/tara.json | |
| cpp-LIN/tara.md | |
| cpp-LIN/fmea.json | |
| cpp-LIN/fmea.csv | |
| cpp-LIN/safety-case.json | |
| cpp-LIN/safety-case.mermaid | |
| cpp-LIN/safety-case.md | |
| cpp-LIN/sbom.json | |
| cpp-LIN/provenance.json | |
| cpp-LIN/artifact-manifest.json | |
| cpp-LIN/fusa-badge.svg | |
| cpp-LIN/iso26262-gap-report.json | |
| cpp-LIN/iec61508-gap-report.json | |
| cpp-LIN/sas.json | |
| cpp-LIN/sas.md | |
| cpp-LIN/sci.json | |
| cpp-LIN/vuln.json | |
| cpp-LIN/boundary.mermaid | |
| cpp-LIN/boundary.dot | |
| cpp-LIN/.fusa-hara.json | |
| cpp-LIN/.fusa-metrics.json | |
| cpp-LIN/TARA.md | |
| cpp-LIN/SAFETY_MANUAL.md | |
| cpp-LIN/INCIDENT-RESPONSE.md | |
| cpp-LIN/SECURITY.md | |
| static-analysis: | |
| name: Static analysis (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-14 clang-tidy-14 | |
| - name: Configure (generate compile_commands.json) | |
| env: | |
| CC: clang-14 | |
| CXX: clang++-14 | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_CXX_STANDARD=17 \ | |
| -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ | |
| -G Ninja | |
| - name: clang-tidy | |
| run: | | |
| find src include -name '*.cpp' -o -name '*.hpp' | \ | |
| xargs clang-tidy-14 \ | |
| -p build \ | |
| --checks='-*,clang-analyzer-*,bugprone-*,modernize-use-override,modernize-use-nullptr,cppcoreguidelines-no-malloc' \ | |
| 2>&1 | tee clang-tidy.log || true | |
| if grep -q "error:" clang-tidy.log; then | |
| echo "::error::clang-tidy reported errors" | |
| grep "error:" clang-tidy.log | |
| exit 1 | |
| fi | |
| docker-build: | |
| name: Docker build (smoke test) | |
| runs-on: ubuntu-22.04 | |
| needs: build-and-test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build Docker image | |
| run: docker build -f docker/Dockerfile --target test -t cpp-lin-test . | |
| - name: Smoke test - run all tests in container | |
| run: docker run --rm cpp-lin-test | |
| sarif: | |
| name: SARIF upload | |
| runs-on: ubuntu-22.04 | |
| needs: build-and-test | |
| permissions: | |
| security-events: write | |
| steps: | |
| - name: Check out cpp-LIN | |
| uses: actions/checkout@v4 | |
| with: | |
| path: cpp-LIN | |
| - name: Check out cpp-FuSa | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: SoundMatt/cpp-FuSa | |
| path: cpp-FuSa | |
| ref: v0.17.1 | |
| - name: Install tools | |
| run: sudo apt-get update -qq && sudo apt-get install -y cmake ninja-build | |
| - name: Build cpfusa | |
| run: | | |
| cmake -B cpp-FuSa/build \ | |
| -S cpp-FuSa \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -G Ninja | |
| cmake --build cpp-FuSa/build --parallel | |
| # No `cpfusa init` step here either — see the fusa-asil-b job's note | |
| # (cpp-LIN#19): .fusa.json/.fusa-reqs.json are already checked in. | |
| - name: Generate SARIF report | |
| working-directory: cpp-LIN | |
| run: | | |
| ../cpp-FuSa/build/cpfusa check \ | |
| --format sarif \ | |
| --output cpfusa.sarif \ | |
| --dir . || true | |
| - name: Upload SARIF to GitHub Security tab | |
| if: hashFiles('cpp-LIN/cpfusa.sarif') != '' | |
| uses: github/codeql-action/upload-sarif@v4 | |
| with: | |
| sarif_file: cpp-LIN/cpfusa.sarif |