feat: auto-release status:in-progress on close and merge (#58)
When Gitea-Tools closes an issue, merges a PR, or closes a PR without merging, remove status:in-progress from the affected issue(s) so closed work doesn't retain stale in-progress state (as happened with #46/#48). Helpers (deterministic, unit-testable) in mcp_server.py: - extract_linked_issue_numbers(text, branch_name): pure; matches Closes/Fixes/Resolves/Refs #N and issue-<N> in a branch name (not review/pr-N). - release_in_progress_label(issue_numbers, repo_context): removes ONLY status:in-progress; absent label / undefined repo label = no-op success; per-issue failures recorded in `failed` (never falsely reported clean); emits an audit record per removal when auditing is enabled. - cleanup_in_progress_for_pr(pr_payload, repo_context): links from PR title/body + head branch, then releases; empty when nothing confidently linked (no guessing, no mutation). Wired into: - gitea_close_issue → releases from that issue. - gitea_merge_pr → releases from PR-linked issues (reuses the read-back PR). - gitea_edit_pr (state=closed) → releases from PR-linked issues WITHOUT closing them. Each returns the cleanup summary under `in_progress_cleanup`. Only necessary label GET/DELETE calls added; no auth/profile/retry/config/worktree/release-tag changes; backwards compatible. Tests: tests/test_in_progress_cleanup.py (18 cases) — extraction, present/absent label, no-issues/no-link no-op, multiple issues, failure reported, label not defined, only-status-removed, PR keyword/branch links, close-without-merge does not close the issue, and audit records the removal. Updated TestCloseIssue for the added release calls. Full suite 322 passed / 0 failures; py_compile clean; git diff --check clean; no secrets. Closes #58. Refs #46, #48. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -89,12 +89,21 @@ class TestCloseIssue(unittest.TestCase):
|
||||
@patch("mcp_server.api_request")
|
||||
@patch("mcp_server.get_auth_header", return_value=FAKE_AUTH)
|
||||
def test_closes_issue(self, _auth, mock_api):
|
||||
mock_api.return_value = {"state": "closed"}
|
||||
# PATCH close, then status:in-progress release (labels lookup, issue
|
||||
# labels, DELETE) — see #58.
|
||||
mock_api.side_effect = [
|
||||
{"state": "closed"}, # PATCH close
|
||||
[{"id": 10, "name": "status:in-progress"}], # repo labels
|
||||
[{"name": "status:in-progress"}], # issue #42 labels
|
||||
None, # DELETE label
|
||||
]
|
||||
result = gitea_close_issue(issue_number=42)
|
||||
self.assertTrue(result["success"])
|
||||
self.assertIn("42", result["message"])
|
||||
payload = mock_api.call_args[0][3]
|
||||
self.assertEqual(payload["state"], "closed")
|
||||
first = mock_api.call_args_list[0]
|
||||
self.assertEqual(first.args[0], "PATCH")
|
||||
self.assertEqual(first.args[3]["state"], "closed")
|
||||
self.assertIn(42, result["in_progress_cleanup"]["released"])
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user