feat: show requested-change feedback (Closes #1336)
CI / lint (pull_request) Successful in 3m30s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 5m24s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-24 05:38:30 +00:00
1 parent d2a815c4bc
commit c4bb2f5eba
8 files changed
+340 -12

No files matched your search

+63 -1
View File
@@ -2771,6 +2771,49 @@ async def pull_completion_detail(repository: str, number: int) -> dict:
}
def _normalize_review_comments(comments: object) -> list[dict]:
normalized = []
for comment in (comments if isinstance(comments, list) else [])[:100]:
if not isinstance(comment, dict):
continue
path = comment.get("path")
body = comment.get("body")
if not isinstance(path, str) or not path.strip() or not isinstance(body, str) or not body.strip():
continue
item = {"path": path.strip()[:300], "body": body.strip()[:500]}
position = comment.get("new_position") or comment.get("old_position")
if isinstance(position, int) and position > 0:
item["line"] = position
normalized.append(item)
if len(normalized) == 20:
break
return normalized
def _latest_feedback_review_ids(reviews: object) -> dict[str, int]:
latest: dict[str, tuple[int, str]] = {}
for review in (reviews if isinstance(reviews, list) else [])[:100]:
if not isinstance(review, dict):
continue
user = review.get("user") if isinstance(review.get("user"), dict) else {}
login = user.get("login")
review_id = review.get("id")
state = review.get("state")
if (
not isinstance(login, str) or not login or not isinstance(review_id, int)
or review_id <= 0 or not isinstance(state, str)
):
continue
key = login.casefold()
if key not in latest or review_id >= latest[key][0]:
latest[key] = (review_id, state)
feedback = [
(key, review_id) for key, (review_id, state) in latest.items()
if state in {"REQUEST_CHANGES", "COMMENT"}
]
return dict(feedback[:10])
def _normalize_reviewer_statuses(pull: dict, reviews: object, head_sha: str) -> list[dict]:
"""Return one bounded, current decision per reviewer without exposing raw review data."""
latest: dict[str, dict] = {}
@@ -2792,8 +2835,10 @@ def _normalize_reviewer_statuses(pull: dict, reviews: object, head_sha: str) ->
previous = latest.get(login.casefold())
review_id = review.get("id") if isinstance(review.get("id"), int) else 0
if previous is None or review_id >= previous["id"]:
body = review.get("body")
latest[login.casefold()] = {
"id": review_id, "login": login, "state": state, "commit_id": commit_id,
"summary": body.strip()[:500] if isinstance(body, str) and body.strip() else "",
}
statuses: dict[str, dict] = {}
@@ -2809,6 +2854,8 @@ def _normalize_reviewer_statuses(pull: dict, reviews: object, head_sha: str) ->
"head_sha": review["commit_id"],
"blocking": status in {"changes_requested", "outdated"},
}
if review["summary"]:
statuses[key]["summary"] = review["summary"]
requested = pull.get("requested_reviewers") if isinstance(pull, dict) else []
for reviewer in (requested if isinstance(requested, list) else [])[:25]:
@@ -2839,6 +2886,21 @@ async def pull_completion_review(repository: str, number: int) -> dict:
)
diff, diff_truncated = diff_result
previews = _diff_previews(diff, diff_truncated)
reviewers = _normalize_reviewer_statuses(pull, reviews, sha)
feedback_ids = _latest_feedback_review_ids(reviews)
if feedback_ids:
feedback_payloads = await asyncio.gather(*(
fetch(f"{base}/reviews/{review_id}/comments")
for review_id in feedback_ids.values()
))
feedback = {
login: _normalize_review_comments(payload)
for login, payload in zip(feedback_ids, feedback_payloads)
}
for reviewer in reviewers:
comments = feedback.get(reviewer["login"].casefold(), [])
if comments:
reviewer["comments"] = comments
return {
"repository": repository,
"number": number,
@@ -2849,7 +2911,7 @@ async def pull_completion_review(repository: str, number: int) -> dict:
"merged": pull.get("merged") is True,
"ci_state": status.get("state", "unknown") if isinstance(status, dict) else "unknown",
"checks": _normalize_commit_checks(status),
"reviewers": _normalize_reviewer_statuses(pull, reviews, sha),
"reviewers": reviewers,
"files": [
{
"filename": item.get("filename", ""),