board_names.py python
209 lines 7.2 KB
Raw
sha256:c07f2f34a0db9f43fe866f157d1935322008545ff8940c06c7c921eb219c55ab NXP-b DONE: independent BV-r2 pass + ISR (SD-17) Human minor ⚠ breaking 2 days ago
1 """Board filename identity helpers (§MR.6.5)."""
2
3 from __future__ import annotations
4
5 import re
6
7 _NON_ALNUM = re.compile(r"[^A-Za-z0-9]+")
8
9
10 def repo_slug(repo_name: str) -> str:
11 """Uppercase ``repo.name`` with non-alnum → ``-`` collapsed (§MR.6.5)."""
12 text = (repo_name or "").strip()
13 if not text:
14 return ""
15 collapsed = _NON_ALNUM.sub("-", text).strip("-")
16 return collapsed.upper()
17
18
19 def expected_handover_basename(repo_name: str, *, lane: str | None = None) -> str:
20 """Default-lane or lane-prefixed handover basename."""
21 slug = repo_slug(repo_name)
22 if lane and lane.strip() and lane.strip().lower() not in {"product", "default"}:
23 return f"{slug}-{lane.strip().upper()}-OVERSEER-HANDOVER.md"
24 return f"{slug}-OVERSEER-HANDOVER.md"
25
26
27 def expected_roadmap_basename(repo_name: str, *, lane: str | None = None) -> str:
28 """Default-lane or lane-prefixed roadmap basename."""
29 slug = repo_slug(repo_name)
30 if lane and lane.strip() and lane.strip().lower() not in {"product", "default"}:
31 return f"{slug}-{lane.strip().upper()}-ROADMAP.md"
32 return f"{slug}-ROADMAP.md"
33
34
35 def expected_handover_title(repo_name: str, *, lane: str | None = None) -> str:
36 """Human title including repo (or lane) label."""
37 label = (repo_name or "").strip() or "Repo"
38 if lane and lane.strip() and lane.strip().lower() not in {"product", "default"}:
39 return f"{label} {lane.strip()} Overseer Handover"
40 # Title-case-ish: keep slug readable — use repo_name as given.
41 pretty = label.replace("-", " ").replace("_", " ")
42 if pretty.islower() or pretty.isupper():
43 pretty = pretty.title()
44 return f"{pretty} Overseer Handover"
45
46
47 def expected_roadmap_title(repo_name: str, *, lane: str | None = None) -> str:
48 label = (repo_name or "").strip() or "Repo"
49 if lane and lane.strip() and lane.strip().lower() not in {"product", "default"}:
50 return f"{label} {lane.strip()} Roadmap"
51 pretty = label.replace("-", " ").replace("_", " ")
52 if pretty.islower() or pretty.isupper():
53 pretty = pretty.title()
54 return f"{pretty} Roadmap"
55
56
57 _BARE_HANDOVER = frozenset({"overseer-handover.md"})
58 _BARE_ROADMAP = frozenset({"roadmap.md"})
59
60
61 def is_bare_legacy_basename(name: str, *, kind: str) -> bool:
62 """True when basename is the bare legacy pair (case-insensitive)."""
63 base = Path_basename(name).lower()
64 if kind == "handover":
65 return base in _BARE_HANDOVER
66 if kind == "roadmap":
67 return base in _BARE_ROADMAP
68 return False
69
70
71 def Path_basename(path_or_name: str) -> str:
72 """Basename helper without importing pathlib at module top for tests."""
73 from pathlib import Path
74
75 return Path(path_or_name).name
76
77
78 def matches_prefixed_pattern(basename: str, repo_name: str, *, kind: str) -> bool:
79 """True when basename starts with ``{REPO_SLUG}-`` and matches kind suffix."""
80 slug = repo_slug(repo_name)
81 if not slug:
82 return False
83 name = Path_basename(basename)
84 if not name.upper().startswith(f"{slug}-"):
85 return False
86 upper = name.upper()
87 if kind == "handover":
88 return upper.endswith("-OVERSEER-HANDOVER.MD") or upper.endswith("OVERSEER-HANDOVER.MD")
89 if kind == "roadmap":
90 return upper.endswith("-ROADMAP.MD") or upper.endswith("ROADMAP.MD")
91 return False
92
93
94 def board_name_violation(
95 *,
96 repo_name: str,
97 handover_basename: str | None,
98 roadmap_basename: str | None,
99 strict: bool,
100 ) -> bool:
101 """True when ``strict_board_names`` and board names are bare/unprefixed."""
102 if not strict:
103 return False
104 if handover_basename is None or roadmap_basename is None:
105 return True
106 if is_bare_legacy_basename(handover_basename, kind="handover"):
107 return True
108 if is_bare_legacy_basename(roadmap_basename, kind="roadmap"):
109 return True
110 if not matches_prefixed_pattern(handover_basename, repo_name, kind="handover"):
111 return True
112 if not matches_prefixed_pattern(roadmap_basename, repo_name, kind="roadmap"):
113 return True
114 return False
115
116
117 def _first_offending_pair(
118 *,
119 repo_name: str,
120 handover_basename: str,
121 roadmap_basename: str,
122 lane: str | None = None,
123 ) -> tuple[str, str]:
124 """Return ``(offending_basename, compliant_target)`` for the first violation."""
125 if is_bare_legacy_basename(handover_basename, kind="handover") or not matches_prefixed_pattern(
126 handover_basename, repo_name, kind="handover"
127 ):
128 return handover_basename, expected_handover_basename(repo_name, lane=lane)
129 return roadmap_basename, expected_roadmap_basename(repo_name, lane=lane)
130
131
132 def check_next_unconfigured_advisory(config) -> str:
133 """Advisory when ``workspace:`` is absent (§NXP.5). Exit stays 0."""
134 repo_name = (config.repo.name or "").strip()
135 handover = Path_basename(config.docs.handover)
136 roadmap = Path_basename(config.docs.roadmap)
137 if board_name_violation(
138 repo_name=repo_name,
139 handover_basename=handover,
140 roadmap_basename=roadmap,
141 strict=True,
142 ):
143 bare, target = _first_offending_pair(
144 repo_name=repo_name,
145 handover_basename=handover,
146 roadmap_basename=roadmap,
147 )
148 return (
149 f"workspace not configured; board name {bare} is bare/legacy — "
150 f"prefer {target}"
151 )
152 return "workspace not configured; board names already compliant"
153
154
155 def status_board_name_advisory(config) -> str | None:
156 """Non-blocking bare-board-name advisory for ``ok status`` (§NXP.6).
157
158 Returns ``None`` when ``workspace:`` is configured or boards are compliant.
159 Never contributes to ``--exit-code``.
160 """
161 if config.workspace is not None:
162 return None
163
164 repo_name = (config.repo.name or "").strip()
165
166 if config.docs.lanes is None:
167 handover = Path_basename(config.docs.handover)
168 roadmap = Path_basename(config.docs.roadmap)
169 if not board_name_violation(
170 repo_name=repo_name,
171 handover_basename=handover,
172 roadmap_basename=roadmap,
173 strict=True,
174 ):
175 return None
176 bare, target = _first_offending_pair(
177 repo_name=repo_name,
178 handover_basename=handover,
179 roadmap_basename=roadmap,
180 )
181 return f"board naming: {bare} is bare/legacy — prefer {target}"
182
183 offenders: list[tuple[str, str, str]] = []
184 for lane_name, lane_docs in config.docs.lanes.items():
185 handover = Path_basename(lane_docs.handover)
186 roadmap = Path_basename(lane_docs.roadmap)
187 if board_name_violation(
188 repo_name=repo_name,
189 handover_basename=handover,
190 roadmap_basename=roadmap,
191 strict=True,
192 ):
193 bare, target = _first_offending_pair(
194 repo_name=repo_name,
195 handover_basename=handover,
196 roadmap_basename=roadmap,
197 lane=lane_name,
198 )
199 offenders.append((lane_name, bare, target))
200
201 if not offenders:
202 return None
203
204 _lane, bare, target = offenders[0]
205 count = len(offenders)
206 return (
207 f"board naming: {count} lane{'s' if count != 1 else ''} non-compliant "
208 f"(first: {bare} — prefer {target})"
209 )
File History 1 commit
sha256:c07f2f34a0db9f43fe866f157d1935322008545ff8940c06c7c921eb219c55ab NXP-b DONE: independent BV-r2 pass + ISR (SD-17) Human minor 2 days ago