feat: add merged PR cleanup reconciliation report (#269)
Implement gitea_reconcile_merged_cleanups with dry-run reporting for merged PR remote branches and local branches/ worktrees, plus confirmation-gated execute mode. Safety gates cover protected branches, open PR references, active issue locks, dirty worktrees, and delete_branch capability. Closes #269 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -24,6 +24,7 @@ from mcp_server import ( # noqa: E402
|
||||
gitea_merge_pr,
|
||||
gitea_review_pr,
|
||||
gitea_delete_branch,
|
||||
gitea_reconcile_merged_cleanups,
|
||||
gitea_edit_pr,
|
||||
gitea_get_file,
|
||||
gitea_commit_files,
|
||||
@@ -990,6 +991,60 @@ class TestDeleteBranch(unittest.TestCase):
|
||||
self.assertIn("feat%2Fbranch", url)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reconcile merged cleanups (#269)
|
||||
# ---------------------------------------------------------------------------
|
||||
READ_ENV = {
|
||||
"GITEA_ALLOWED_OPERATIONS": "gitea.read",
|
||||
}
|
||||
|
||||
|
||||
class TestReconcileMergedCleanups(unittest.TestCase):
|
||||
|
||||
@patch("mcp_server.api_get_all")
|
||||
@patch("mcp_server.get_auth_header", return_value=FAKE_AUTH)
|
||||
def test_dry_run_builds_report_without_mutations(self, _auth, mock_api_get_all):
|
||||
mock_api_get_all.side_effect = [
|
||||
[
|
||||
{
|
||||
"number": 50,
|
||||
"title": "merged feature",
|
||||
"body": "Closes #50",
|
||||
"merged": True,
|
||||
"merged_at": "2026-07-06T12:00:00Z",
|
||||
"merge_commit_sha": "deadbeef",
|
||||
"head": {"ref": "feat/issue-50-example", "sha": "cafebabe"},
|
||||
}
|
||||
],
|
||||
[],
|
||||
]
|
||||
with patch.dict(os.environ, READ_ENV, clear=True):
|
||||
with patch(
|
||||
"mcp_server._remote_branch_exists",
|
||||
return_value=True,
|
||||
):
|
||||
with patch(
|
||||
"mcp_server.merged_cleanup_reconcile.is_head_ancestor_of_ref",
|
||||
return_value=True,
|
||||
):
|
||||
result = gitea_reconcile_merged_cleanups(
|
||||
dry_run=True,
|
||||
remote="prgs",
|
||||
limit=10,
|
||||
)
|
||||
self.assertTrue(result["success"])
|
||||
self.assertTrue(result["dry_run"])
|
||||
self.assertFalse(result["executed"])
|
||||
self.assertEqual(result["merged_pr_count"], 1)
|
||||
self.assertEqual(result["entries"][0]["issue_number"], 50)
|
||||
|
||||
def test_execute_requires_confirmation(self):
|
||||
with patch.dict(os.environ, READ_ENV, clear=True):
|
||||
with self.assertRaises(ValueError) as ctx:
|
||||
gitea_reconcile_merged_cleanups(dry_run=False, execute_confirmed=False)
|
||||
self.assertIn("execute_confirmed", str(ctx.exception))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Edit PR
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user