fix: address review findings
CI / test (pull_request) Successful in 14s
CI / review (gpt-4.1, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 23s
CI / review (gpt-5, security, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 39s
CI / review (gpt-5, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 1m7s

- Gate inline comment resolution on successful supersede
- Add pagination to ListReviewComments
- Add tests for ListReviewComments and ResolveComment
This commit is contained in:
Rodin
2026-05-02 12:10:37 -07:00
parent 17027c1fa3
commit 3d0ca57a5f
3 changed files with 102 additions and 30 deletions
+5 -1
View File
@@ -357,13 +357,16 @@ func main() {
if existingReview != nil && existingCommentID > 0 { if existingReview != nil && existingCommentID > 0 {
newReviewURL := fmt.Sprintf("%s/%s/%s/pulls/%d#pullrequestreview-%d", strings.TrimRight(*giteaURL, "/"), owner, repoName, prNumber, posted.ID) newReviewURL := fmt.Sprintf("%s/%s/%s/pulls/%d#pullrequestreview-%d", strings.TrimRight(*giteaURL, "/"), owner, repoName, prNumber, posted.ID)
supersededBody := buildSupersededBody(existingReview.Body, existingReview.CommitID, newReviewURL, sentinel) supersededBody := buildSupersededBody(existingReview.Body, existingReview.CommitID, newReviewURL, sentinel)
supersedeOK := false
if err := giteaClient.EditComment(ctx, owner, repoName, existingCommentID, supersededBody); err != nil { if err := giteaClient.EditComment(ctx, owner, repoName, existingCommentID, supersededBody); err != nil {
slog.Warn("could not mark old review as superseded", "comment_id", existingCommentID, "error", err) slog.Warn("could not mark old review as superseded", "comment_id", existingCommentID, "error", err)
} else { } else {
slog.Info("marked old review as superseded", "old_state", existingReview.State, "new_review_id", posted.ID, "pr", prNumber) slog.Info("marked old review as superseded", "old_state", existingReview.State, "new_review_id", posted.ID, "pr", prNumber)
supersedeOK = true
} }
// Resolve old review's inline comments (clears unresolved conversations in PR timeline) // Resolve old review's inline comments only after successful supersede
if supersedeOK {
oldComments, err := giteaClient.ListReviewComments(ctx, owner, repoName, prNumber, existingReview.ID) oldComments, err := giteaClient.ListReviewComments(ctx, owner, repoName, prNumber, existingReview.ID)
if err != nil { if err != nil {
slog.Warn("could not list old review comments for resolution", "review_id", existingReview.ID, "error", err) slog.Warn("could not list old review comments for resolution", "review_id", existingReview.ID, "error", err)
@@ -384,6 +387,7 @@ func main() {
} }
} }
} }
}
} }
+18 -7
View File
@@ -463,22 +463,33 @@ func (c *Client) EditComment(ctx context.Context, owner, repo string, commentID
} }
// ListReviewComments returns the inline comments attached to a specific review. // ListReviewComments returns the inline comments attached to a specific review.
// Paginates through all pages.
func (c *Client) ListReviewComments(ctx context.Context, owner, repo string, prNumber int, reviewID int64) ([]ReviewComment, error) { func (c *Client) ListReviewComments(ctx context.Context, owner, repo string, prNumber int, reviewID int64) ([]ReviewComment, error) {
reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/reviews/%d/comments", const pageSize = 50
var all []ReviewComment
for page := 1; ; page++ {
reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/reviews/%d/comments?limit=%d&page=%d",
c.baseURL, c.baseURL,
url.PathEscape(owner), url.PathEscape(owner),
url.PathEscape(repo), url.PathEscape(repo),
prNumber, prNumber,
reviewID) reviewID,
pageSize,
page)
body, err := c.doGet(ctx, reqURL) body, err := c.doGet(ctx, reqURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("list review comments: %w", err) return nil, fmt.Errorf("list review comments (page %d): %w", page, err)
} }
var comments []ReviewComment var batch []ReviewComment
if err := json.Unmarshal(body, &comments); err != nil { if err := json.Unmarshal(body, &batch); err != nil {
return nil, fmt.Errorf("parse review comments: %w", err) return nil, fmt.Errorf("parse review comments (page %d): %w", page, err)
} }
return comments, nil all = append(all, batch...)
if len(batch) < pageSize {
break
}
}
return all, nil
} }
// ResolveComment marks an inline review comment as resolved. // ResolveComment marks an inline review comment as resolved.
+57
View File
@@ -602,3 +602,60 @@ func TestIsNotFound(t *testing.T) {
}) })
} }
} }
func TestListReviewComments(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `[{"id":100,"path":"main.go","new_position":5,"body":"finding"},{"id":101,"path":"lib.go","new_position":10,"body":"another"}]`)
}))
defer server.Close()
client := NewClient(server.URL, "test-token")
comments, err := client.ListReviewComments(context.Background(), "owner", "repo", 1, 42)
if err != nil {
t.Fatalf("ListReviewComments() error = %v", err)
}
if len(comments) != 2 {
t.Fatalf("got %d comments, want 2", len(comments))
}
if comments[0].ID != 100 {
t.Errorf("comments[0].ID = %d, want 100", comments[0].ID)
}
if comments[1].Path != "lib.go" {
t.Errorf("comments[1].Path = %q, want %q", comments[1].Path, "lib.go")
}
}
func TestResolveComment(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("expected POST, got %s", r.Method)
}
expected := "/api/v1/repos/owner/repo/pulls/comments/99/resolve"
if r.URL.Path != expected {
t.Errorf("path = %q, want %q", r.URL.Path, expected)
}
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
client := NewClient(server.URL, "test-token")
err := client.ResolveComment(context.Background(), "owner", "repo", 99)
if err != nil {
t.Fatalf("ResolveComment() error = %v", err)
}
}
func TestResolveComment_Error(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "not found")
}))
defer server.Close()
client := NewClient(server.URL, "test-token")
err := client.ResolveComment(context.Background(), "owner", "repo", 99)
if err == nil {
t.Fatal("expected error for 404 response")
}
}