ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
check_inclusive_terminology_commits.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# SPDX-License-Identifier: MIT
3# Copyright (c) 2026 Brighton Sikarskie
4"""Inclusive-terminology gate for commit message text read from stdin.
5
6Called by the inclusive-terminology CI workflow to verify that no commit
7message on a push or PR contains banned legacy SPI/I2C terminology.
8
9Usage:
10 git log BASE..HEAD --format=%B | python3 scripts/checks/check_inclusive_terminology_commits.py
11 python3 scripts/checks/check_inclusive_terminology_commits.py --selftest
12
13Exit code:
14 0 -- clean
15 1 -- violations found
16
17Per-paragraph opt-out: include LEGACY-OK: <reason> anywhere in the same
18blank-line-delimited paragraph as the offending word. Commit message prose is
19word-wrapped by editors and git itself, so an opt-out placed at the end of a
20paragraph must cover every physical line of that paragraph, not only the one
21it sits on -- a same-line-only rule rejects annotations on ordinary wrapped
22text.
23
24@copyright Copyright (c) 2026 Brighton Sikarskie
25SPDX-License-Identifier: MIT
26"""
27
28from __future__ import annotations
29
30import re
31import sys
32
33BANNED: tuple[tuple[re.Pattern[str], str], ...] = (
34 (re.compile(r"\bmaster(s|ed|ing|ship)?\b", re.IGNORECASE), "master -- use Primary/Controller"),
35 (re.compile(r"\bslave(s|d)?\b", re.IGNORECASE), "slave -- use Peripheral"),
36 (re.compile(r"\bMOSI\b"), "MOSI -- use COPI"),
37 (re.compile(r"\bMISO\b"), "MISO -- use CIPO"),
38 (re.compile(r"\bSlave[ _-]Select\b", re.IGNORECASE), "Slave Select -- use CS"),
39 # Note: bare '\bSS\b' is intentionally omitted here -- it produces too many
40 # false positives in prose commit messages (e.g. "ban SS -> use CS" documenting
41 # the rule itself). The abbreviation is caught at the source-file level by
42 # check_inclusive_terminology.py instead.
43)
44
45LEGACY_OK: re.Pattern[str] = re.compile(r"LEGACY-OK\s*:", re.IGNORECASE)
46
47
48def find_violations(text: str) -> list[str]:
49 """Scan commit-message text for banned terms, honoring paragraph-scoped opt-outs."""
50 violations: list[str] = []
51 lines = text.splitlines()
52 para_start = 0
53 for idx in range(len(lines) + 1):
54 at_boundary = idx == len(lines) or lines[idx].strip() == ""
55 if not at_boundary:
56 continue
57 para_lines = lines[para_start:idx]
58 if para_lines and not any(LEGACY_OK.search(pl) for pl in para_lines):
59 for offset, line in enumerate(para_lines):
60 lineno = para_start + offset + 1
61 for pattern, msg in BANNED:
62 if pattern.search(line):
63 violations.append(f" line {lineno}: {msg}\n > {line.strip()}")
64 break
65 para_start = idx + 1
66 return violations
67
68
69def _selftest() -> int:
70 """Assert both directions: a must-fire case and a must-stay-quiet case."""
71 fired = find_violations("fix(spi): rework the MOSI/MISO pin mux\n")
72 if not fired:
73 print("[SELFTEST FAIL] an un-annotated MOSI in a commit message was not flagged.")
74 return 1
75
76 # A wrapped paragraph: the banned word is on one physical line, the
77 # opt-out on another, inside the SAME paragraph. This is exactly the
78 # shape ordinary git line-wrapping produces and must stay quiet.
79 quiet = find_violations(
80 "ci(gates): widen scope\n\n"
81 "Widening surfaced only verbatim upstream terms (IEEE 1588 PTP\n"
82 "master/slave, datasheet MOSI/MISO pin labels), LEGACY-OK: upstream\n"
83 "domain terminology quoted verbatim, not our naming.\n"
84 )
85 if quiet:
86 print("[SELFTEST FAIL] a paragraph-scoped LEGACY-OK opt-out did not cover its")
87 print(" whole paragraph:")
88 for v in quiet:
89 print(v)
90 return 1
91
92 # A LEGACY-OK in a DIFFERENT paragraph must NOT reach across the blank
93 # line -- the opt-out is paragraph-scoped, not whole-message-scoped.
94 cross_paragraph = find_violations(
95 "fix(spi): rework the MOSI pin mux\n\nLEGACY-OK: unrelated note in the next paragraph\n"
96 )
97 if not cross_paragraph:
98 print("[SELFTEST FAIL] LEGACY-OK in one paragraph suppressed a violation in a")
99 print(" different paragraph.")
100 return 1
101
102 print("[SELFTEST OK] fires on an un-annotated term, stays quiet on a wrapped")
103 print(" paragraph-scoped LEGACY-OK, and does not leak across paragraphs.")
104 return 0
105
106
107def main() -> int:
108 """Run --selftest, or scan stdin for banned terms and report."""
109 if "--selftest" in sys.argv[1:]:
110 return _selftest()
111
112 text = sys.stdin.read()
113 violations = find_violations(text)
114
115 if violations:
116 print("[FAIL] Non-inclusive terminology in commit message(s):")
117 for v in violations:
118 print(v)
119 return 1
120
121 print("[PASS] Commit message terminology clean.")
122 return 0
123
124
125if __name__ == "__main__":
126 sys.exit(main())
void main(void)
The application entry point Reset_Handler hands control to.
Definition main.c:298