fix: close issues after merged PRs

This commit is contained in:
2026-05-06 17:33:16 +08:00
parent 70a17d6675
commit 3c624cc46d
3 changed files with 70 additions and 9 deletions

View File

@@ -292,12 +292,23 @@ class Database:
SELECT t.*
FROM tasks t
JOIN issues i ON i.repo_id = t.repo_id AND i.issue_number = t.issue_number
WHERE t.state = ?
WHERE t.state != ?
AND t.pr_number IS NOT NULL
AND i.state = 'open'
AND (
t.state IN (?, ?)
OR t.lease_owner IS NULL
OR t.lease_expires_at IS NULL
OR t.lease_expires_at < ?
)
ORDER BY t.id
""",
(TaskState.HUMAN_REVIEW_READY.value,),
(
TaskState.CANCELLED.value,
TaskState.HUMAN_REVIEW_READY.value,
TaskState.FAILED.value,
dt_to_db(utcnow()),
),
).fetchall()
return [self._task(row) for row in rows]

View File

@@ -10,7 +10,7 @@ from .agents import CommandRunner, read_report, render_command, write_prompt
from .config import AppConfig
from .db import Database, PullRequestFeedbackCursor
from .gitea import GiteaClient, GiteaComment, GiteaPullReview
from .models import IssueRecord, RepositoryRecord, TaskRecord, TaskState
from .models import ACTIVE_STATES, IssueRecord, RepositoryRecord, TaskRecord, TaskState
from .rendering import (
parse_review_report,
render_human_review_summary,
@@ -92,12 +92,12 @@ def close_issues_for_merged_pull_requests(db: Database, client: GiteaClient) ->
)
client.close_issue(owner=repo.owner, name=repo.name, issue_number=issue.issue_number)
db.update_issue_state(task.repo_id, task.issue_number, "closed")
db.add_event(
task.id,
task.state,
task.state,
f"closed issue #{issue.issue_number} after merged PR #{task.pr_number}",
)
message = f"closed issue #{issue.issue_number} after merged PR #{task.pr_number}"
if task.state in ACTIVE_STATES:
db.clear_pr_feedback_pending(task.id)
db.transition(task.id, TaskState.CANCELLED, message=message, clear_lease=True)
else:
db.add_event(task.id, task.state, task.state, message)
closed += 1
return closed

View File

@@ -656,6 +656,56 @@ def test_close_issues_for_merged_pull_requests_skips_unmerged_pr(db):
assert db.get_issue(repo.id, 1).state == "open" # type: ignore[union-attr]
def test_close_issues_for_merged_pull_requests_handles_queued_feedback_task(db):
repo = db.upsert_repository(
owner="acme",
name="service",
clone_url="https://gitea.test/acme/service.git",
default_branch="main",
enabled=True,
)
db.upsert_issue(
repo_id=repo.id,
issue_number=1,
title="Ready issue",
body="Body",
labels=["agent:ready"],
state="open",
html_url="https://gitea.test/acme/service/issues/1",
)
task = db.create_task(repo.id, 1)
task = transition_to_human_review_ready(db, task.id, pr_number=5, branch_name="agent/issue-1-ready-issue")
db.mark_pr_feedback_pending(task.id)
db.transition(
task.id,
TaskState.DISCOVERED,
message="queued PR feedback from 1 human comment(s)",
clear_lease=True,
)
requests: list[tuple[str, str, dict]] = []
def handler(request: httpx.Request) -> httpx.Response:
payload = json.loads(request.content.decode() or "{}")
requests.append((request.method, request.url.path, payload))
if request.url.path == "/api/v1/repos/acme/service/pulls/5":
return httpx.Response(200, json={"number": 5, "state": "closed", "merged": True})
if request.url.path == "/api/v1/repos/acme/service/issues/1/comments":
return httpx.Response(201, json={"id": 1})
if request.url.path == "/api/v1/repos/acme/service/issues/1":
return httpx.Response(200, json={"number": 1, "state": "closed"})
return httpx.Response(404)
closed = close_issues_for_merged_pull_requests(db, make_client(handler))
updated_task = db.get_task(task.id)
assert closed == 1
assert db.get_issue(repo.id, 1).state == "closed" # type: ignore[union-attr]
assert updated_task is not None
assert updated_task.state == TaskState.CANCELLED
assert not db.has_pending_pr_feedback(task.id)
assert ("PATCH", "/api/v1/repos/acme/service/issues/1", {"state": "closed"}) in requests
def test_run_task_no_diff_becomes_blocked(db, tmp_path):
config = make_config(tmp_path)
seed_task(db)