Skip to content

Commit 9d7f8bc

Browse files
nikazzioCopilot
andcommitted
fix: handle multi-line semantic-release bullets in changelog policy check
The policy validator only inspected the '- ' line, missing commit links placed on indented continuation lines by python-semantic-release. Collapse multi-line bullets before checking for references. Related #110 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4664f85 commit 9d7f8bc

1 file changed

Lines changed: 43 additions & 6 deletions

File tree

scripts/check_changelog_policy.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,27 +47,64 @@ def _iter_release_blocks(lines: list[str]) -> list[tuple[str, list[str]]]:
4747
return blocks
4848

4949

50+
def _collapse_bullets(block_lines: list[str]) -> list[tuple[str, str]]:
51+
"""Collapse multi-line bullets into (first_line, full_text) pairs.
52+
53+
python-semantic-release places commit links on indented continuation
54+
lines. We join them so the reference check sees the complete bullet.
55+
"""
56+
bullets: list[tuple[str, str]] = []
57+
current_first: str | None = None
58+
current_parts: list[str] = []
59+
60+
for line in block_lines:
61+
if line.startswith("- "):
62+
if current_first is not None:
63+
bullets.append((current_first, " ".join(current_parts)))
64+
current_first = line
65+
current_parts = [line[2:].strip()]
66+
elif current_first is not None and line.startswith(" "):
67+
current_parts.append(line.strip())
68+
else:
69+
if current_first is not None:
70+
bullets.append((current_first, " ".join(current_parts)))
71+
current_first = None
72+
current_parts = []
73+
74+
if current_first is not None:
75+
bullets.append((current_first, " ".join(current_parts)))
76+
77+
return bullets
78+
79+
5080
def _validate_release_block(title: str, block_lines: list[str]) -> list[str]:
5181
errors: list[str] = []
5282
sections_seen: set[str] = set()
5383
current_section: str | None = None
84+
section_lines: list[str] = []
85+
86+
def _check_section_bullets() -> None:
87+
for first_line, full_text in _collapse_bullets(section_lines):
88+
if full_text.lower() == "none.":
89+
continue
90+
if not RE_ISSUE_REF.search(full_text) and not RE_COMMIT_LINK.search(full_text):
91+
errors.append(f"{title}: bullet missing issue/PR reference -> '{first_line}'")
5492

5593
for line in block_lines:
5694
section = RE_SECTION.match(line)
5795
if section:
96+
_check_section_bullets()
5897
current_section = section.group("section")
5998
sections_seen.add(current_section)
99+
section_lines = []
60100
continue
61101

62102
if current_section is None:
63103
continue
64104

65-
if line.startswith("- "):
66-
item = line[2:].strip()
67-
if item.lower() == "none.":
68-
continue
69-
if not RE_ISSUE_REF.search(item) and not RE_COMMIT_LINK.search(item):
70-
errors.append(f"{title}: bullet missing issue/PR reference -> '{line}'")
105+
section_lines.append(line)
106+
107+
_check_section_bullets()
71108

72109
if not sections_seen and not any(line.startswith("**Detailed Changes**:") for line in block_lines):
73110
errors.append(f"{title}: missing at least one '### <section>' subsection")

0 commit comments

Comments
 (0)