From e94e16bbbbc5f2c618bf52a992d60348dcb2c79e Mon Sep 17 00:00:00 2001 From: HappyOnigiri <253838257+NodeMeld@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:31:16 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix(auto=5Ffixer):=20=E3=83=9E=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E6=B8=88=E3=81=BF=20PR=20=E3=81=A7=20refix:merged=20?= =?UTF-8?q?=E3=83=A9=E3=83=99=E3=83=AB=E3=81=8C=E4=BB=98=E3=81=8B=E3=81=AA?= =?UTF-8?q?=E3=81=84=E4=B8=8D=E5=85=B7=E5=90=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auto_fixer.py | 4 +--- tests/test_auto_fixer.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/auto_fixer.py b/src/auto_fixer.py index f074e9e..bc0f9fb 100644 --- a/src/auto_fixer.py +++ b/src/auto_fixer.py @@ -1303,7 +1303,7 @@ def _pr_has_label(pr_data: dict[str, Any], label_name: str) -> bool: def _mark_pr_merged_label_if_needed(repo: str, pr_number: int) -> bool: """Add refix:merged label when PR is merged and eligible.""" - cmd = ["gh", "pr", "view", str(pr_number), "--repo", repo, "--json", "mergedAt,labels,autoMergeRequest"] + cmd = ["gh", "pr", "view", str(pr_number), "--repo", repo, "--json", "mergedAt,labels"] result = subprocess.run( cmd, capture_output=True, @@ -1335,8 +1335,6 @@ def _mark_pr_merged_label_if_needed(repo: str, pr_number: int) -> bool: return False if _pr_has_label(pr_data, REFIX_MERGED_LABEL): return False - if not pr_data.get("autoMergeRequest"): - return False print(f"PR #{pr_number} is merged; adding {REFIX_MERGED_LABEL} label.") _set_pr_merged_label(repo, pr_number) diff --git a/tests/test_auto_fixer.py b/tests/test_auto_fixer.py index 886af98..e0d7c08 100644 --- a/tests/test_auto_fixer.py +++ b/tests/test_auto_fixer.py @@ -1710,10 +1710,10 @@ def test_trigger_pr_auto_merge_treats_already_merged_as_success(self): assert ok is True def test_mark_pr_merged_label_if_needed_adds_label_for_done_merged_pr(self): + # Note: autoMergeRequest is null for merged PRs in real GitHub API pr_view = { "mergedAt": "2026-03-11T00:00:00Z", "labels": [{"name": "refix:done"}], - "autoMergeRequest": {"enabledBy": {"login": "bot"}}, } with ( patch("auto_fixer.subprocess.run", return_value=Mock(returncode=0, stdout=json.dumps(pr_view), stderr="")), From 447970adf74e7ed2097df54bc1d6e66f98e0d865 Mon Sep 17 00:00:00 2001 From: HappyOnigiri <253838257+NodeMeld@users.noreply.github.com> Date: Thu, 12 Mar 2026 02:43:48 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix(auto=5Ffixer):=20refix:merged=20?= =?UTF-8?q?=E3=82=92=20Refix=20=E3=81=8C=20auto-merge=20=E8=A6=81=E6=B1=82?= =?UTF-8?q?=E3=81=97=E3=81=9F=20PR=20=E3=81=AB=E3=81=AE=E3=81=BF=E4=BB=98?= =?UTF-8?q?=E4=B8=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _trigger_pr_auto_merge() 成功時に refix:auto-merge-requested ラベルを付与し、 _mark_pr_merged_label_if_needed() と _backfill_merged_labels() で同ラベルの 存在を確認することで、手動マージされた PR に誤って refix:merged が付く問題を修正。 --- src/auto_fixer.py | 15 ++++++++++++++- tests/test_auto_fixer.py | 21 +++++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/auto_fixer.py b/src/auto_fixer.py index bc0f9fb..7010ddd 100644 --- a/src/auto_fixer.py +++ b/src/auto_fixer.py @@ -101,6 +101,7 @@ REFIX_RUNNING_LABEL = "refix:running" REFIX_DONE_LABEL = "refix:done" REFIX_MERGED_LABEL = "refix:merged" +REFIX_AUTO_MERGE_REQUESTED_LABEL = "refix:auto-merge-requested" CODERABBIT_PROCESSING_MARKER = "Currently processing new changes in this PR." CODERABBIT_RATE_LIMIT_MARKER = "Rate limit exceeded" CODERABBIT_REVIEW_FAILED_MARKER = "## Review failed" @@ -110,6 +111,7 @@ REFIX_RUNNING_LABEL_COLOR = "FBCA04" REFIX_DONE_LABEL_COLOR = "0E8A16" REFIX_MERGED_LABEL_COLOR = "1D76DB" +REFIX_AUTO_MERGE_REQUESTED_LABEL_COLOR = "C2E0C6" FAILED_CI_CONCLUSIONS = {"FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "CANCELLED", "STALE", "STARTUP_FAILURE"} FAILED_CI_STATES = {"ERROR", "FAILURE"} GITHUB_ACTIONS_RUN_URL_PATTERN = re.compile(r"/actions/runs/(\d+)") @@ -1237,6 +1239,12 @@ def _ensure_refix_labels(repo: str) -> None: color=REFIX_MERGED_LABEL_COLOR, description="PR has been merged after Refix auto-merge.", ) + _ensure_repo_label_exists( + repo, + REFIX_AUTO_MERGE_REQUESTED_LABEL, + color=REFIX_AUTO_MERGE_REQUESTED_LABEL_COLOR, + description="Refix has requested auto-merge for this PR.", + ) def _edit_pr_label(repo: str, pr_number: int, *, add: bool, label: str) -> bool: @@ -1288,6 +1296,7 @@ def _set_pr_done_label(repo: str, pr_number: int) -> None: def _set_pr_merged_label(repo: str, pr_number: int) -> None: _ensure_refix_labels(repo) _edit_pr_label(repo, pr_number, add=False, label=REFIX_RUNNING_LABEL) + _edit_pr_label(repo, pr_number, add=False, label=REFIX_AUTO_MERGE_REQUESTED_LABEL) _edit_pr_label(repo, pr_number, add=True, label=REFIX_MERGED_LABEL) @@ -1333,6 +1342,8 @@ def _mark_pr_merged_label_if_needed(repo: str, pr_number: int) -> bool: return False if not _pr_has_label(pr_data, REFIX_DONE_LABEL): return False + if not _pr_has_label(pr_data, REFIX_AUTO_MERGE_REQUESTED_LABEL): + return False if _pr_has_label(pr_data, REFIX_MERGED_LABEL): return False @@ -1343,7 +1354,7 @@ def _mark_pr_merged_label_if_needed(repo: str, pr_number: int) -> bool: def _backfill_merged_labels(repo: str, *, limit: int = 100) -> int: """Backfill refix:merged label for merged PRs already marked refix:done.""" - search_query = f'label:"{REFIX_DONE_LABEL}" -label:"{REFIX_MERGED_LABEL}"' + search_query = f'label:"{REFIX_DONE_LABEL}" label:"{REFIX_AUTO_MERGE_REQUESTED_LABEL}" -label:"{REFIX_MERGED_LABEL}"' cmd = [ "gh", "pr", @@ -1408,6 +1419,7 @@ def _trigger_pr_auto_merge(repo: str, pr_number: int) -> bool: ) if result.returncode == 0: print(f"Auto-merge requested for PR #{pr_number}.") + _edit_pr_label(repo, pr_number, add=True, label=REFIX_AUTO_MERGE_REQUESTED_LABEL) return True stderr_text = (result.stderr or "").strip() @@ -1415,6 +1427,7 @@ def _trigger_pr_auto_merge(repo: str, pr_number: int) -> bool: combined_lower = f"{stdout_text}\n{stderr_text}".lower() if "already merged" in combined_lower: print(f"PR #{pr_number} is already merged.") + _edit_pr_label(repo, pr_number, add=True, label=REFIX_AUTO_MERGE_REQUESTED_LABEL) return True details = stderr_text or stdout_text or "unknown error" diff --git a/tests/test_auto_fixer.py b/tests/test_auto_fixer.py index e0d7c08..2db1494 100644 --- a/tests/test_auto_fixer.py +++ b/tests/test_auto_fixer.py @@ -1683,6 +1683,7 @@ def test_set_pr_merged_label_ensures_labels_before_edit(self): mock_edit.assert_has_calls( [ call("owner/repo", 12, add=False, label="refix:running"), + call("owner/repo", 12, add=False, label="refix:auto-merge-requested"), call("owner/repo", 12, add=True, label="refix:merged"), ] ) @@ -1692,7 +1693,7 @@ def test_trigger_pr_auto_merge_executes_gh_merge(self): ok = auto_fixer._trigger_pr_auto_merge("owner/repo", 7) assert ok is True - mock_run.assert_called_once_with( + mock_run.assert_any_call( ["gh", "pr", "merge", "7", "--repo", "owner/repo", "--auto", "--merge"], capture_output=True, text=True, @@ -1710,10 +1711,9 @@ def test_trigger_pr_auto_merge_treats_already_merged_as_success(self): assert ok is True def test_mark_pr_merged_label_if_needed_adds_label_for_done_merged_pr(self): - # Note: autoMergeRequest is null for merged PRs in real GitHub API pr_view = { "mergedAt": "2026-03-11T00:00:00Z", - "labels": [{"name": "refix:done"}], + "labels": [{"name": "refix:done"}, {"name": "refix:auto-merge-requested"}], } with ( patch("auto_fixer.subprocess.run", return_value=Mock(returncode=0, stdout=json.dumps(pr_view), stderr="")), @@ -1726,7 +1726,7 @@ def test_mark_pr_merged_label_if_needed_adds_label_for_done_merged_pr(self): def test_mark_pr_merged_label_if_needed_skips_when_not_merged(self): pr_view = { "mergedAt": None, - "labels": [{"name": "refix:done"}], + "labels": [{"name": "refix:done"}, {"name": "refix:auto-merge-requested"}], } with ( patch("auto_fixer.subprocess.run", return_value=Mock(returncode=0, stdout=json.dumps(pr_view), stderr="")), @@ -1736,6 +1736,19 @@ def test_mark_pr_merged_label_if_needed_skips_when_not_merged(self): assert ok is False mock_set_merged.assert_not_called() + def test_mark_pr_merged_label_if_needed_skips_when_auto_merge_not_requested(self): + pr_view = { + "mergedAt": "2026-03-11T00:00:00Z", + "labels": [{"name": "refix:done"}], + } + with ( + patch("auto_fixer.subprocess.run", return_value=Mock(returncode=0, stdout=json.dumps(pr_view), stderr="")), + patch("auto_fixer._set_pr_merged_label") as mock_set_merged, + ): + ok = auto_fixer._mark_pr_merged_label_if_needed("owner/repo", 23) + assert ok is False + mock_set_merged.assert_not_called() + def test_backfill_merged_labels_applies_label_to_matching_prs(self): merged_prs = [{"number": 31}, {"number": 32}] with (