ci(lint): add clang-format and MISRA static-analysis checks #39

Merged
hoogv merged 0 commits from refs/pull/39/head into dev 2026-07-22 13:01:58 +00:00
hoogv commented 2026-07-22 12:55:25 +00:00 (Migrated from codeberg.org)

Description

Adds .forgejo/workflows/lint.yml with two CI jobs: a .clang-format conformance check on changed files, and a MISRA C static-analysis pass using tools/misra/run_misra.sh's cppcheck-based tooling.

Why

Change justification per ISO 9001 section 7.5.2: .forgejo/workflows/tests.yml currently only runs the host-native Catch2 test suite — no static-quality gate enforces .clang-format conformance or MISRA C compliance on every push/PR, both of which are QMS-relevant for this MISRA-governed firmware project.

Closes #38

Type of Change

  • Bug fix / nonconformity resolution
  • New feature

Impact Assessment

  • Functional impact: None — CI/build-tooling only, no product/driver code changes.
  • Regulatory impact: None.
  • QMS impact: Potential — adds a new automated static-analysis gate; may be relevant to QMS tooling records.
  • Risk impact: No — existing risk coverage sufficient.

Testing

  • CI pipeline passes (compile, unit tests, integration tests, static analysis)
  • Manually tested: run_misra.sh runs clean (0 findings) against hal/util with a valid synthetic rule_texts.txt; confirmed it still catches a real violation (injected a goto statement, an unsuppressed rule); confirmed the crash-detection fix actually fires on a malformed rule-texts file; confirmed the clang-format changed-files diff logic correctly picks up .c/.h files from a real past commit and that clang-format --dry-run --Werror correctly flags non-conformant code.
  • No regressions observed in related areas

PR Size

  • XL (> 300 lines) — must split unless exemption declared

Size justification / exemption:

Raw diff is 459 lines across 4 new files.

Exemption

This PR restores previously-existing tooling from a local backup rather than authoring new business logic from scratch: tools/misra/run_misra.sh (134 lines) and tools/misra/generate_rule_texts.py (107 lines) are infrastructure scripts, and tools/misra/suppressions.txt (144 lines) is a mostly-mechanical list of pre-vetted rule suppressions. Only .forgejo/workflows/lint.yml (74 lines) is genuinely new CI wiring. Not split further since these four files are one cohesive, interdependent unit (the workflow invokes the script, which reads the suppressions file and is generated by the Python script) that doesn't functively separate into smaller independently-reviewable PRs.

Checklist

  • Commit messages follow type(scope): description convention with Relates to #<issue> footer
  • PR template filled in completely
  • No unverified external binaries introduced (see QMS-GITFLOW-001 Binary Security section)
  • All commits leave the codebase in a compilable, passing-tests state (Commit Integrity Rule)

Summary

  • clang-format job: checks only files changed in the current push/PR against the already-committed .clang-format config, not the whole tree — the existing register headers predate that config and aren't conformant yet, so a whole-tree gate would fail immediately on landing.
  • misra job: runs tools/misra/run_misra.sh's cppcheck-based MISRA C pass. MISRA's guideline text is copyrighted, so rule_texts.txt is populated from the MISRA_RULES repo secret at run time instead of a locally-purchased PDF; rule_texts.txt/misra.json/report.txt stay gitignored, never committed.
  • Lands tools/misra/{run_misra.sh,suppressions.txt,generate_rule_texts.py} (previously only in a local backup, never committed).
  • Two real bugs found and fixed while bringing this up locally: run_misra.sh hardcoded hal/mss/hal/dss as check targets, but neither is landed yet — changed to check whatever subset of {mss,dss,util} actually exists on disk. A crashed misra.py addon makes cppcheck bail out with an [internalError], which the report-generation grep didn't match — silently producing a false "0 findings" clean pass instead of a failure; added a check that fails loudly on that bailout message.
  • One genuine false positive found and suppressed: misra-c2012-17.3 (implicit function declaration) fires on mmwhal_types.h's GCC inline-asm critical-section macros (__asm__ __volatile__(...)) because misra.py doesn't recognize __asm__ as a keyword.
## Description Adds `.forgejo/workflows/lint.yml` with two CI jobs: a `.clang-format` conformance check on changed files, and a MISRA C static-analysis pass using `tools/misra/run_misra.sh`'s cppcheck-based tooling. ## Why Change justification per ISO 9001 section 7.5.2: `.forgejo/workflows/tests.yml` currently only runs the host-native Catch2 test suite — no static-quality gate enforces `.clang-format` conformance or MISRA C compliance on every push/PR, both of which are QMS-relevant for this MISRA-governed firmware project. ## Related Issue Closes #38 ## Type of Change - [x] Bug fix / nonconformity resolution - [x] New feature ## Impact Assessment - **Functional impact:** None — CI/build-tooling only, no product/driver code changes. - **Regulatory impact:** None. - **QMS impact:** Potential — adds a new automated static-analysis gate; may be relevant to QMS tooling records. - **Risk impact:** No — existing risk coverage sufficient. ## Testing - [x] CI pipeline passes (compile, unit tests, integration tests, static analysis) - [x] Manually tested: `run_misra.sh` runs clean (0 findings) against `hal/util` with a valid synthetic `rule_texts.txt`; confirmed it still catches a real violation (injected a `goto` statement, an unsuppressed rule); confirmed the crash-detection fix actually fires on a malformed rule-texts file; confirmed the clang-format changed-files diff logic correctly picks up `.c`/`.h` files from a real past commit and that `clang-format --dry-run --Werror` correctly flags non-conformant code. - [x] No regressions observed in related areas ## PR Size - [x] XL (> 300 lines) — must split unless exemption declared **Size justification / exemption:** Raw diff is 459 lines across 4 new files. ## Exemption This PR restores previously-existing tooling from a local backup rather than authoring new business logic from scratch: `tools/misra/run_misra.sh` (134 lines) and `tools/misra/generate_rule_texts.py` (107 lines) are infrastructure scripts, and `tools/misra/suppressions.txt` (144 lines) is a mostly-mechanical list of pre-vetted rule suppressions. Only `.forgejo/workflows/lint.yml` (74 lines) is genuinely new CI wiring. Not split further since these four files are one cohesive, interdependent unit (the workflow invokes the script, which reads the suppressions file and is generated by the Python script) that doesn't functively separate into smaller independently-reviewable PRs. ## Checklist - [x] Commit messages follow `type(scope): description` convention with `Relates to #<issue>` footer - [x] PR template filled in completely - [x] No unverified external binaries introduced (see QMS-GITFLOW-001 Binary Security section) - [x] All commits leave the codebase in a compilable, passing-tests state (Commit Integrity Rule) ## Summary - **clang-format job**: checks only files changed in the current push/PR against the already-committed `.clang-format` config, not the whole tree — the existing register headers predate that config and aren't conformant yet, so a whole-tree gate would fail immediately on landing. - **misra job**: runs `tools/misra/run_misra.sh`'s cppcheck-based MISRA C pass. MISRA's guideline text is copyrighted, so `rule_texts.txt` is populated from the `MISRA_RULES` repo secret at run time instead of a locally-purchased PDF; `rule_texts.txt`/`misra.json`/`report.txt` stay gitignored, never committed. - Lands `tools/misra/{run_misra.sh,suppressions.txt,generate_rule_texts.py}` (previously only in a local backup, never committed). - **Two real bugs found and fixed while bringing this up locally**: `run_misra.sh` hardcoded `hal/mss`/`hal/dss` as check targets, but neither is landed yet — changed to check whatever subset of `{mss,dss,util}` actually exists on disk. A crashed `misra.py` addon makes cppcheck bail out with an `[internalError]`, which the report-generation `grep` didn't match — silently producing a false "0 findings" clean pass instead of a failure; added a check that fails loudly on that bailout message. - **One genuine false positive found and suppressed**: `misra-c2012-17.3` (implicit function declaration) fires on `mmwhal_types.h`'s GCC inline-asm critical-section macros (`__asm__ __volatile__(...)`) because `misra.py` doesn't recognize `__asm__` as a keyword.
hoogv self-assigned this 2026-07-23 13:35:04 +00:00
hoogv added this to the Development project 2026-07-23 13:38:51 +00:00
Sign in to join this conversation.
No reviewers
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
opendutchsolutions.public/hal_awr6843!39
No description provided.