diff --git a/.github/aicodingflow-tests/test_apply_issue_triage_result.py b/.github/aicodingflow-tests/test_apply_issue_triage_result.py index a83a355..37db8a7 100644 --- a/.github/aicodingflow-tests/test_apply_issue_triage_result.py +++ b/.github/aicodingflow-tests/test_apply_issue_triage_result.py @@ -102,6 +102,29 @@ def test_upsert_triage_comment_patches_existing_marker_comment(self) -> None: check=True, ) + def test_find_triage_comment_skips_unowned_and_quoted_markers(self) -> None: + marker = apply_triage.MARKER + comments = [ + {"id": 1, "user": {"login": "reporter"}, "body": f"{marker}\nCopied triage"}, + {"id": 2, "user": {"login": "other[bot]"}, "body": f"{marker}\nOther bot"}, + {"id": 3, "user": {"login": "github-actions[bot]"}, "body": f"Quote: {marker}"}, + {"id": 4, "user": {"login": "github-actions[bot]"}, "body": f"{marker}\nActual triage"}, + ] + with mock.patch.object(apply_triage, "run_gh_json", return_value=[comments]): + self.assertEqual(apply_triage.find_triage_comment("owner/repo", 19), comments[3]) + + def test_upsert_triage_comment_creates_when_only_user_comment_has_marker(self) -> None: + comment = {"id": 123, "user": {"login": "reporter"}, "body": apply_triage.MARKER} + with ( + mock.patch.object(apply_triage, "run_gh_json", return_value=[[comment]]), + mock.patch.object(apply_triage.subprocess, "run") as run, + ): + apply_triage.upsert_triage_comment("owner/repo", 19, "body") + + run.assert_called_once_with( + ["gh", "api", "repos/owner/repo/issues/19/comments", "-f", "body=body"], check=True, + ) + if __name__ == "__main__": unittest.main() diff --git a/.github/aicodingflow-tests/test_nightly_existing_code_review.py b/.github/aicodingflow-tests/test_nightly_existing_code_review.py index a8a6165..4e4bc30 100644 --- a/.github/aicodingflow-tests/test_nightly_existing_code_review.py +++ b/.github/aicodingflow-tests/test_nightly_existing_code_review.py @@ -258,9 +258,16 @@ def test_user_created_duplicate_never_triggers_nightly_triage_dispatch(self) -> dispatch.assert_not_called() def test_triage_comment_lookup_and_dispatch_command(self) -> None: - with mock.patch.object(publish, "run_gh_json", return_value=[[{"body": publish.TRIAGE_COMMENT_MARKER}]]) as fetch: + marker = "" + comments = [ + {"user": {"login": "reporter"}, "body": marker}, + {"user": {"login": "github-actions[bot]"}, "body": marker}, + ] + with mock.patch.object(publish, "run_gh_json", return_value=[comments]) as fetch: self.assertTrue(publish.has_triage_comment("owner/repo", 7)) fetch.assert_called_once() + with mock.patch.object(publish, "run_gh_json", return_value=[comments[:1]]): + self.assertFalse(publish.has_triage_comment("owner/repo", 7)) with mock.patch.object(publish.subprocess, "run") as run: publish.dispatch_triage("owner/repo", "main", 7) run.assert_called_once_with([ diff --git a/.github/scripts/apply_issue_triage_result.py b/.github/scripts/apply_issue_triage_result.py index 35d5369..1aff4b7 100644 --- a/.github/scripts/apply_issue_triage_result.py +++ b/.github/scripts/apply_issue_triage_result.py @@ -14,6 +14,7 @@ MARKER = "" +TRIAGE_COMMENT_AUTHOR = "github-actions[bot]" PROTECTED_LABELS = {"plan-approved", "ready-to-implement", "ready-to-spec"} @@ -55,6 +56,12 @@ def sync_labels( ) +def is_triage_comment(comment: dict[str, Any]) -> bool: + user = comment.get("user") or {} + body = comment.get("body") or "" + return user.get("login") == TRIAGE_COMMENT_AUTHOR and isinstance(body, str) and body.startswith(MARKER) + + def find_triage_comment(repo: str, issue_number: int) -> dict[str, Any] | None: pages = run_gh_json( [ @@ -65,7 +72,7 @@ def find_triage_comment(repo: str, issue_number: int) -> dict[str, Any] | None: ] ) for comment in flatten_pages(pages): - if MARKER in (comment.get("body") or ""): + if is_triage_comment(comment): return comment return None diff --git a/.github/scripts/publish_nightly_findings.py b/.github/scripts/publish_nightly_findings.py index ed22046..3192bec 100644 --- a/.github/scripts/publish_nightly_findings.py +++ b/.github/scripts/publish_nightly_findings.py @@ -13,7 +13,7 @@ from pathlib import Path from typing import Any -from apply_issue_triage_result import MARKER as TRIAGE_COMMENT_MARKER +from apply_issue_triage_result import is_triage_comment from github_api import flatten_gh_pages, run_gh_json from prepare_nightly_review import focus_files, issue_snapshot, parse_cycle_days @@ -181,7 +181,7 @@ def has_triage_comment(repo: str, issue_number: int) -> bool: "api", f"repos/{repo}/issues/{issue_number}/comments?per_page=100", "--paginate", "--slurp", ]) - return any(TRIAGE_COMMENT_MARKER in str(item.get("body") or "") for item in flatten_gh_pages(pages)) + return any(is_triage_comment(item) for item in flatten_gh_pages(pages)) def dispatch_triage(repo: str, default_branch: str, issue_number: int) -> None: