Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# clang-tidy configuration for the `clang-tidy` CI job (ci.yml).
#
# Why this file exists (issue #80 / cpp-RCP-12): before this file was added,
# ci.yml ran `clang-tidy -p build --warnings-as-errors='*'` with no
# `-checks` argument and no `.clang-tidy` file present. With nothing
# enabling any checks, clang-tidy exits immediately with the fatal usage
# error "Error: no checks enabled" -- and the trailing `|| true` on that
# step was swallowing that failure, meaning the job has never actually
# linted a single file in this repo's history, let alone gated on it.
#
# The check list below is deliberately narrow. It was hand-picked and
# verified (locally, via Homebrew LLVM clang-tidy against this tree) to
# have zero findings against the current include/ + tests/ sources, while
# still being a real, non-trivial "definite bug pattern" gate: use-after-move,
# infinite loops, dangling handles, sizeof/memset/string-compare misuse,
# swapped arguments, virtual near-misses, etc.
#
# Broader categories -- full bugprone-*, cert-*, performance-*,
# portability-*, modernize-*, readability-*, clang-analyzer-*, and even
# clang-diagnostic-* (this project already has pre-existing -Wall/-Wextra
# findings, e.g. -Wmissing-field-initializers in tests/test_legacy_mock.cpp,
# that don't fail the build today because CMakeLists.txt does not pass
# -Werror) -- all currently have real, pre-existing findings across this
# tree. Turning any of those on wholesale would fail this CI job against a
# backlog of findings that are out of scope for a CI/process-only change
# (see the PR that introduced this file for the full accounting). Growing
# this list is left as deliberate follow-up work for whoever owns the C++
# source, once each finding is triaged and fixed or dispositioned.
Checks: >
-*,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase,
bugprone-infinite-loop,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-not-null-terminated-result,
bugprone-signed-char-misuse,
bugprone-sizeof-expression,
bugprone-string-integer-assignment,
bugprone-suspicious-memset-usage,
bugprone-suspicious-string-compare,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-undefined-memory-manipulation,
bugprone-unused-raii,
bugprone-use-after-move,
bugprone-virtual-near-miss

WarningsAsErrors: '*'

# Restrict findings surfaced from #included headers to this project's own
# headers, so third-party FetchContent dependencies (e.g. Catch2, vendored
# under build/_deps/catch2-src) don't get flagged for patterns in their own
# code that this project has no control over.
HeaderFilterRegex: 'include/rcp/'
8 changes: 4 additions & 4 deletions .fusa-hara.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"severity": "S3",
"exposure": "E4",
"controllability": "C2",
"asil": "ASIL-B"
"asil": "ASIL-C"
},
"safetyGoals": [
"SG-001"
Expand Down Expand Up @@ -231,7 +231,7 @@
{
"id": "SG-001",
"description": "Requests to safety-critical endpoints shall be delivered within the configured watchdog period or a fault shall be signalled.",
"asil": "ASIL-B"
"asil": "ASIL-C"
},
{
"id": "SG-002",
Expand All @@ -241,7 +241,7 @@
{
"id": "SG-003",
"description": "A per-stream watchdog kick shall be recorded for every accepted inbound request, regardless of request kind or safety tag.",
"asil": "ASIL-B"
"asil": "ASIL-C"
},
{
"id": "SG-004",
Expand All @@ -261,7 +261,7 @@
{
"id": "SG-007",
"description": "An RC Server that stops responding shall be detected as unreachable within the configured liveness deadline.",
"asil": "ASIL-B"
"asil": "ASIL-C"
},
{
"id": "SG-008",
Expand Down
82 changes: 74 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:

- name: Set up MSVC environment (Windows)
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1

- name: Configure
shell: bash
Expand Down Expand Up @@ -93,10 +93,17 @@ jobs:
- 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: 3344b3d1a6a4d870d26c81c6f3c3ff5ca2930e16 # v2.0.2
path: RELAY

- name: Set up Go
Expand Down Expand Up @@ -149,9 +156,18 @@ jobs:
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 include tests -name '*.hpp' -o -name '*.cpp' | \
xargs clang-tidy -p build --warnings-as-errors='*' || true
find tests -name '*.cpp' | \
xargs clang-tidy -p build --warnings-as-errors='*'

# ── LCOV coverage ─────────────────────────────────────────────────────────────
coverage:
Expand Down Expand Up @@ -187,12 +203,28 @@ jobs:

- 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
Expand Down Expand Up @@ -354,7 +386,11 @@ jobs:
- name: Traceability check (all requirements traced)
run: /tmp/cpfusa/build/cpfusa trace

- name: Traceability gaps report
# 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 AND tested (100%).
Expand Down Expand Up @@ -499,16 +535,46 @@ jobs:
name: coverage-info
path: .

- name: LCOV structural coverage report (DO-178C)
# 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

- name: Full compliance report (JSON)
# 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)
- name: Full compliance report (SARIF, artifact only — non-gating)
run: /tmp/cpfusa/build/cpfusa report --format sarif --output results.sarif || true

- name: Safety metrics snapshot
# 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
Expand Down
Loading
Loading