diff --git a/.github/scripts/check_pr_body.py b/.github/scripts/check_pr_body.py index 70881e69bcf..4711997323f 100644 --- a/.github/scripts/check_pr_body.py +++ b/.github/scripts/check_pr_body.py @@ -25,6 +25,7 @@ HTML_COMMENT_PATTERN: Final = re.compile(r"", re.DOTALL) HEADING_PATTERN: Final = re.compile(r"^#{2,6}\s+(?P.+?)\s*$") BULLET_PATTERN: Final = re.compile(r"^\s*(?:[-*+]|\d+[.)])\s+(?P<text>.*)$") LABEL_PATTERN: Final = re.compile(r"^[^-*+].*:\s*$") +FENCE_PATTERN: Final = re.compile(r"^\s*(?:```|~~~)") @dataclass(frozen=True, slots=True) @@ -48,9 +49,21 @@ def strip_html_comments(body: str) -> str: def split_sections(body: str) -> tuple[Section, ...]: lines: Final = tuple(body.split("\n")) - headings: Final = tuple( - (index, match.group("title")) for index, line in enumerate(lines) if (match := HEADING_PATTERN.match(line)) - ) + + def scan( + acc: tuple[bool, tuple[tuple[int, str], ...]], + item: tuple[int, str], + ) -> tuple[bool, tuple[tuple[int, str], ...]]: + in_fence, headings = acc + index, line = item + if FENCE_PATTERN.match(line): + return (not in_fence, headings) + if in_fence: + return acc + match: Final = HEADING_PATTERN.match(line) + return (in_fence, (*headings, (index, match.group("title")))) if match else acc + + _, headings = reduce(scan, tuple(enumerate(lines)), (False, ())) ends: Final = tuple(index for index, _ in headings[1:]) + (len(lines),) return tuple(Section(title=title, lines=lines[start + 1 : end]) for (start, title), end in zip(headings, ends)) @@ -92,8 +105,9 @@ def check_bullet_section(section: Section) -> tuple[Violation, ...]: def is_placeholder_line(line: str) -> bool: bullet_match: Final = BULLET_PATTERN.match(line) - content: Final = bullet_match.group("text").strip() if bullet_match else line.strip() - return content == "..." or any(token in line for token in PLACEHOLDER_TOKENS) + if bullet_match and bullet_match.group("text").strip() == "...": + return True + return any(token in line for token in PLACEHOLDER_TOKENS) def check_placeholders(sections: tuple[Section, ...]) -> tuple[Violation, ...]: diff --git a/tests/test_litellm/test_github_check_pr_body.py b/tests/test_litellm/test_github_check_pr_body.py index edf3587e42f..5010829d9d2 100644 --- a/tests/test_litellm/test_github_check_pr_body.py +++ b/tests/test_litellm/test_github_check_pr_body.py @@ -109,3 +109,28 @@ def test_multiline_html_comment_spanning_section_is_stripped(checker): violations = checker.check_body(body, ("litellm/main.py",)) assert len(violations) == 1 assert violations[0].section == "QA runbook" + + +def test_bare_ellipsis_in_proof_output_is_not_a_placeholder(checker): + body = ( + "## Screenshots / Proof of Fix\n\n" + "```\n" + "$ curl http://localhost:4000/v1/chat/completions ...\n" + "{\"id\": \"chatcmpl-abc\",\n" + " ...\n" + " \"usage\": {\"prompt_tokens\": 5}}\n" + "```\n" + ) + assert checker.check_body(body, ()) == () + + +def test_headings_inside_fenced_block_do_not_open_new_sections(checker): + body = ( + "## Screenshots / Proof of Fix\n\n" + "Quoted template excerpt:\n\n" + "```markdown\n" + "## Caveats (if any)\n\n" + "Some prose paragraph that is not a bullet at all\n" + "```\n" + ) + assert checker.check_body(body, ()) == ()