Skip to content

Commit 73a0d6f

Browse files
committed
merge: resolve test conflict with master
2 parents 357d676 + 797031c commit 73a0d6f

File tree

1,805 files changed

+41142
-18255
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,805 files changed

+41142
-18255
lines changed

.github/scripts/commit_prefix_check.py

Lines changed: 126 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,77 @@
2525

2626

2727
# ------------------------------------------------
28-
# Identify expected prefix dynamically from file paths
28+
# Identify expected prefixes dynamically from file paths
2929
# ------------------------------------------------
3030
def infer_prefix_from_paths(paths):
31+
"""
32+
Returns:
33+
- prefixes: a set of allowed prefixes (including build:)
34+
- build_optional: True when commit subject does not need to be build:
35+
(i.e., when any real component — lib/tests/plugins/src — is touched)
36+
"""
3137
prefixes = set()
38+
component_prefixes = set()
39+
build_seen = False
40+
41+
for raw in paths:
42+
# Normalize path separators (Windows compatibility)
43+
p = raw.replace(os.sep, "/")
44+
basename = os.path.basename(p)
45+
46+
# ----- Any CMakeLists.txt → build: candidate -----
47+
if basename == "CMakeLists.txt":
48+
build_seen = True
3249

33-
for p in paths:
50+
# ----- lib/ → lib: -----
51+
if p.startswith("lib/"):
52+
component_prefixes.add("lib:")
53+
54+
# ----- tests/ → tests: -----
55+
if p.startswith("tests/"):
56+
component_prefixes.add("tests:")
57+
58+
# ----- plugins/<name>/ → <name>: -----
3459
if p.startswith("plugins/"):
3560
parts = p.split("/")
36-
prefix = parts[1]
37-
prefixes.add(f"{prefix}:")
38-
continue
61+
if len(parts) > 1:
62+
component_prefixes.add(f"{parts[1]}:")
3963

64+
# ----- src/ → flb_xxx.* → xxx: OR src/<dir>/ → <dir>: -----
65+
# ----- src/ handling -----
4066
if p.startswith("src/"):
67+
parts = p.split("/")
4168
filename = os.path.basename(p)
42-
if filename.startswith("flb_"):
69+
70+
# src/fluent-bit.c → bin:
71+
if filename == "fluent-bit.c":
72+
component_prefixes.add("bin:")
73+
continue
74+
75+
# src/flb_xxx.c → xxx:
76+
if len(parts) == 2 and filename.startswith("flb_"):
4377
core = filename[4:].split(".")[0]
44-
prefixes.add(f"{core}:")
78+
component_prefixes.add(f"{core}:")
79+
continue
80+
81+
# src/<dir>/... → <dir>:
82+
if len(parts) > 2:
83+
src_dir = parts[1]
84+
component_prefixes.add(f"{src_dir}:")
4585
continue
4686

47-
directory = p.split("/")[1]
48-
prefixes.add(f"{directory}:")
49-
continue
5087

51-
return prefixes
88+
# prefixes = component prefixes + build: if needed
89+
prefixes |= component_prefixes
90+
if build_seen:
91+
prefixes.add("build:")
92+
93+
# build_optional:
94+
# True if ANY real component (lib/tests/plugins/src) was modified.
95+
# False only when modifying build system files alone.
96+
build_optional = len(component_prefixes) > 0
97+
98+
return prefixes, build_optional
5299

53100

54101
# ------------------------------------------------
@@ -84,27 +131,27 @@ def detect_bad_squash(body):
84131

85132

86133
# ------------------------------------------------
87-
# Validate commit per test expectations
134+
# Validate commit based on expected behavior and test rules
88135
# ------------------------------------------------
89136
def validate_commit(commit):
90137
msg = commit.message.strip()
91138
first_line, *rest = msg.split("\n")
92139
body = "\n".join(rest)
93140

94-
# Subject must have prefix
141+
# Subject must start with a prefix
95142
subject_prefix_match = PREFIX_RE.match(first_line)
96143
if not subject_prefix_match:
97144
return False, f"Missing prefix in commit subject: '{first_line}'"
98145

99146
subject_prefix = subject_prefix_match.group()
100147

101-
# detect_bad_squash must run but
102-
# validate_commit IGNORE bad-squash reason if it was "multiple sign-offs"
148+
# Run squash detection (but ignore multi-signoff errors)
103149
bad_squash, reason = detect_bad_squash(body)
104150

105151
# If bad squash was caused by prefix lines in body → FAIL
106152
# If list of prefix lines in body → FAIL
107153
if bad_squash:
154+
# Prefix-like lines are always fatal
108155
if "subject-like prefix" in reason:
109156
return False, f"Bad squash detected: {reason}"
110157

@@ -113,7 +160,7 @@ def validate_commit(commit):
113160
# validate_commit ignores multi signoff warnings.
114161
pass
115162

116-
# Subject length
163+
# Subject length check
117164
if len(first_line) > 80:
118165
return False, f"Commit subject too long (>80 chars): '{first_line}'"
119166

@@ -122,30 +169,80 @@ def validate_commit(commit):
122169
if signoff_count == 0:
123170
return False, "Missing Signed-off-by line"
124171

125-
# Determine expected prefix
172+
# Determine expected prefixes + build option flag
126173
files = commit.stats.files.keys()
127-
expected = infer_prefix_from_paths(files)
174+
expected, build_optional = infer_prefix_from_paths(files)
128175

129-
# Docs/CI changes
176+
# When no prefix can be inferred (docs/tools), allow anything
130177
if len(expected) == 0:
131178
return True, ""
132179

133-
# *** TEST EXPECTATION ***
134-
# For mixed components, DO NOT return custom message.
135-
# Instead: same error shape as wrong-prefix case.
136-
if len(expected) > 1:
137-
# Always fail when multiple components are touched (even if prefix matches one)
180+
expected_lower = {p.lower() for p in expected}
181+
subj_lower = subject_prefix.lower()
182+
183+
184+
# ------------------------------------------------
185+
# config_format conditional umbrella rule
186+
# ------------------------------------------------
187+
if "config_format:" in expected_lower:
188+
non_build = {
189+
p for p in expected_lower
190+
if p not in ("build:", "cmakelists.txt:")
191+
}
192+
193+
# Allow ONLY if all non-build prefixes are config_format:
194+
if non_build != {"config_format:"}:
195+
return False, (
196+
f"Subject prefix '{subject_prefix}' does not match files changed.\n"
197+
f"config_format commits must not include other components."
198+
)
199+
200+
# ------------------------------------------------
201+
# Multiple-component detection
202+
# ------------------------------------------------
203+
# Treat pure build-related prefixes ("build:", "CMakeLists.txt:") as non-components.
204+
# Additionally, allow lib: to act as an umbrella for lib subcomponents
205+
# (e.g., ripser:, ripser_wrapper:) when subject prefix is lib:.
206+
non_build_prefixes = {
207+
p
208+
for p in expected_lower
209+
if p not in ("build:", "cmakelists.txt:")
210+
}
211+
212+
# Prefixes that are allowed to cover multiple subcomponents
213+
umbrella_prefixes = {"lib:"}
214+
215+
# If more than one non-build prefix is inferred AND the subject is not an umbrella
216+
# prefix, check if the subject prefix is in the expected list. If it is, allow it
217+
# (because the corresponding file exists). Only reject if it's not in the expected list
218+
# or if it's an umbrella prefix that doesn't match.
219+
if len(non_build_prefixes) > 1 and subj_lower not in umbrella_prefixes:
220+
# If subject prefix is in expected list, it's valid (the corresponding file exists)
221+
if subj_lower not in expected_lower:
222+
expected_list = sorted(expected)
223+
expected_str = ", ".join(expected_list)
224+
return False, (
225+
f"Subject prefix '{subject_prefix}' does not match files changed.\n"
226+
f"Expected one of: {expected_str}"
227+
)
228+
# Subject prefix is in expected list, so it's valid - no need to check further
229+
230+
# Subject prefix must be one of the expected ones
231+
if subj_lower not in expected_lower:
232+
expected_list = sorted(expected)
233+
expected_str = ", ".join(expected_list)
138234
return False, (
139235
f"Subject prefix '{subject_prefix}' does not match files changed.\n"
140-
f"Expected one of: {', '.join(sorted(expected))}"
236+
f"Expected one of: {expected_str}"
141237
)
142238

143-
# Normal prefix mismatch (case-insensitive comparison)
144-
only_expected = next(iter(expected))
145-
if subject_prefix.lower() != only_expected.lower():
239+
240+
# If build is NOT optional and build: exists among expected,
241+
# then subject MUST be build:
242+
if not build_optional and "build:" in expected_lower and subj_lower != "build:":
146243
return False, (
147244
f"Subject prefix '{subject_prefix}' does not match files changed.\n"
148-
f"Expected one of: {only_expected}"
245+
f"Expected one of: build:"
149246
)
150247

151248
return True, ""

0 commit comments

Comments
 (0)