Skip to content

Commit 9fdeeb2

Browse files
authored
Fix re-request loop for user reviewers (#302)
If a user was requested for review, but their review didn't change the status of the policy for some reason, policy-bot could immediately re-request them as a reviewer. This was most likely to happen if a user left a "request changes" review on a repository that does not have a disapproval policy. To fix this, look at all reviews on the current commit and exclude their authors from the list of requests. If the pull request is updated in the future, the users become eligible for requesting again. Because we already list reviews for basic policy evaluation, this shouldn't add any new requests.
1 parent ccf65b1 commit 9fdeeb2

File tree

4 files changed

+35
-7
lines changed

4 files changed

+35
-7
lines changed

pull/context.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,7 @@ type Review struct {
201201
Author string
202202
State ReviewState
203203
Body string
204-
205-
// ID is the GitHub node ID of the review, used to resolve dismissals
206-
ID string
204+
SHA string
207205
}
208206

209207
type ReviewerType string

pull/github.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ func (ghc *GitHubContext) loadPagedData() error {
746746
switch r.State {
747747
case "COMMENTED":
748748
comments = append(comments, r.ToComment())
749+
fallthrough
749750
case "APPROVED", "CHANGES_REQUESTED":
750751
reviews = append(reviews, r.ToReview())
751752
}
@@ -982,6 +983,9 @@ type v4PullRequestReview struct {
982983
State string
983984
Body string
984985
SubmittedAt time.Time
986+
Commit struct {
987+
OID string
988+
}
985989
}
986990

987991
func (r *v4PullRequestReview) ToReview() *Review {
@@ -990,6 +994,7 @@ func (r *v4PullRequestReview) ToReview() *Review {
990994
Author: r.Author.GetV3Login(),
991995
State: ReviewState(strings.ToLower(r.State)),
992996
Body: r.Body,
997+
SHA: r.Commit.OID,
993998
}
994999
}
9951000

pull/github_test.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func TestReviews(t *testing.T) {
193193
reviews, err := ctx.Reviews()
194194
require.NoError(t, err)
195195

196-
require.Len(t, reviews, 2, "incorrect number of reviews")
196+
require.Len(t, reviews, 3, "incorrect number of reviews")
197197
assert.Equal(t, 2, dataRule.Count, "no http request was made")
198198

199199
expectedTime, err := time.Parse(time.RFC3339, "2018-06-27T20:33:26Z")
@@ -209,11 +209,16 @@ func TestReviews(t *testing.T) {
209209
assert.Equal(t, ReviewApproved, reviews[1].State)
210210
assert.Equal(t, "the body", reviews[1].Body)
211211

212+
assert.Equal(t, "jgiannuzzi", reviews[2].Author)
213+
assert.Equal(t, expectedTime.Add(-4*time.Second).Add(5*time.Minute), reviews[2].CreatedAt)
214+
assert.Equal(t, ReviewCommented, reviews[2].State)
215+
assert.Equal(t, "A review comment", reviews[2].Body)
216+
212217
// verify that the review list is cached
213218
reviews, err = ctx.Reviews()
214219
require.NoError(t, err)
215220

216-
require.Len(t, reviews, 2, "incorrect number of reviews")
221+
require.Len(t, reviews, 3, "incorrect number of reviews")
217222
assert.Equal(t, 2, dataRule.Count, "cached reviews were not used")
218223
}
219224

@@ -373,7 +378,7 @@ func TestMixedReviewCommentPaging(t *testing.T) {
373378

374379
assert.Equal(t, 2, dataRule.Count, "cached values were not used")
375380
assert.Len(t, comments, 3, "incorrect number of comments")
376-
assert.Len(t, reviews, 2, "incorrect number of reviews")
381+
assert.Len(t, reviews, 3, "incorrect number of reviews")
377382
}
378383

379384
func TestIsOrgMember(t *testing.T) {

server/handler/base.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,32 @@ func (b *Base) requestReviews(ctx context.Context, prctx pull.Context, client *g
299299
logger.Debug().Msgf("Waiting for %s to spread out reviewer processing", delay)
300300
time.Sleep(delay)
301301

302-
// check again if someone assigned a reviewer while we were calculating users to request
302+
// Get existing reviewers _after_ computing the selection and sleeping in
303+
// case something assigned reviewers (or left reviews) in the meantime.
303304
reviewers, err := prctx.RequestedReviewers()
304305
if err != nil {
305306
return err
306307
}
307308

309+
// After a user leaves a review, GitHub removes the user from the request
310+
// list. If the review didn't actually change the state of the requesting
311+
// rule, policy-bot may request the same user again. To avoid this, include
312+
// any reviews on the head commit in the set of existing reviewers to avoid
313+
// re-requesting them until the content changes.
314+
head := prctx.HeadSHA()
315+
reviews, err := prctx.Reviews()
316+
if err != nil {
317+
return err
318+
}
319+
for _, r := range reviews {
320+
if r.SHA == head {
321+
reviewers = append(reviewers, &pull.Reviewer{
322+
Type: pull.ReviewerUser,
323+
Name: r.Author,
324+
})
325+
}
326+
}
327+
308328
if diff := selection.Difference(reviewers); !diff.IsEmpty() {
309329
req := selectionToReviewersRequest(diff)
310330
logger.Debug().

0 commit comments

Comments
 (0)