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
6
Called by the inclusive-terminology CI workflow to verify that no commit
7
message on a push or PR contains banned legacy SPI/I2C terminology.
8
9
Usage:
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
13
Exit code:
14
0 -- clean
15
1 -- violations found
16
17
Per-paragraph opt-out: include LEGACY-OK: <reason> anywhere in the same
18
blank-line-delimited paragraph as the offending word. Commit message prose is
19
word-wrapped by editors and git itself, so an opt-out placed at the end of a
20
paragraph must cover every physical line of that paragraph, not only the one
21
it sits on -- a same-line-only rule rejects annotations on ordinary wrapped
22
text.
23
24
@copyright Copyright (c) 2026 Brighton Sikarskie
25
SPDX-License-Identifier: MIT
26
"""
27
28
from
__future__
import
annotations
29
30
import
re
31
import
sys
32
33
BANNED: 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
45
LEGACY_OK: re.Pattern[str] = re.compile(
r"LEGACY-OK\s*:"
, re.IGNORECASE)
46
47
48
def
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
69
def
_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
107
def
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
125
if
__name__ ==
"__main__"
:
126
sys.exit(
main
())
main
void main(void)
The application entry point Reset_Handler hands control to.
Definition
main.c:298
scripts
checks
check_inclusive_terminology_commits.py
Generated by
1.16.1