2121repo = Repo ("." )
2222
2323# Regex patterns
24- PREFIX_RE = re .compile (r"^([a-z0-9_]+:)\s+\S" , re .IGNORECASE )
24+ PREFIX_RE = re .compile (
25+ r"^([a-z0-9_]+:(?:\s+[a-z0-9_]+:)*)\s+\S" ,
26+ re .IGNORECASE ,
27+ )
2528SIGNED_OFF_RE = re .compile (r"Signed-off-by:" , re .IGNORECASE )
2629FENCED_BLOCK_RE = re .compile (
2730 r"""
@@ -231,6 +234,7 @@ def is_version_bump(commit):
231234 return False , f"Missing prefix in commit subject: '{ first_line } '"
232235
233236 subject_prefix = subject_prefix_match .group (1 )
237+ subject_root_prefix = subject_prefix .split ()[0 ]
234238
235239 # Run squash detection (but ignore multi-signoff errors)
236240 bad_squash , reason = detect_bad_squash (body )
@@ -282,6 +286,7 @@ def is_version_bump(commit):
282286
283287 expected_lower = {p .lower () for p in expected }
284288 subj_lower = subject_prefix .lower ()
289+ subj_root_lower = subject_root_prefix .lower ()
285290
286291
287292 # ------------------------------------------------
@@ -320,10 +325,10 @@ def is_version_bump(commit):
320325 # (because the corresponding file exists). Only reject if it's not in the expected list
321326 # or if it's an umbrella prefix that doesn't match.
322327 if len (non_build_prefixes ) > 1 :
323- if subj_lower in umbrella_prefixes :
328+ if subj_root_lower in umbrella_prefixes :
324329 norm_paths = [p .replace (os .sep , "/" ) for p in files ]
325330
326- if subj_lower == "lib:" :
331+ if subj_root_lower == "lib:" :
327332 if not all (p .startswith ("lib/" ) for p in norm_paths ):
328333 expected_list = sorted (expected )
329334 expected_str = ", " .join (expected_list )
@@ -332,7 +337,7 @@ def is_version_bump(commit):
332337 f"Expected one of: { expected_str } "
333338 )
334339
335- elif subj_lower == "tests:" :
340+ elif subj_root_lower == "tests:" :
336341 if not all (p .startswith ("tests/" ) for p in norm_paths ):
337342 expected_list = sorted (expected )
338343 expected_str = ", " .join (expected_list )
@@ -341,7 +346,7 @@ def is_version_bump(commit):
341346 f"Expected one of: { expected_str } "
342347 )
343348
344- elif subj_lower == "http_server:" :
349+ elif subj_root_lower == "http_server:" :
345350 if not all (is_http_server_interface_path (p ) for p in norm_paths ):
346351 expected_list = sorted (expected )
347352 expected_str = ", " .join (expected_list )
@@ -359,7 +364,7 @@ def is_version_bump(commit):
359364 )
360365
361366 # Subject prefix must be one of the expected ones
362- if subj_lower not in expected_lower :
367+ if subj_lower not in expected_lower and subj_root_lower not in expected_lower :
363368 expected_list = sorted (expected )
364369 expected_str = ", " .join (expected_list )
365370 return False , (
@@ -370,7 +375,7 @@ def is_version_bump(commit):
370375
371376 # If build is NOT optional and build: exists among expected,
372377 # then subject MUST be build:
373- if not build_optional and "build:" in expected_lower and subj_lower != "build:" :
378+ if not build_optional and "build:" in expected_lower and subj_root_lower != "build:" :
374379 return False , (
375380 f"Subject prefix '{ subject_prefix } ' does not match files changed.\n "
376381 f"Expected one of: build:"
0 commit comments