| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package goreviews |
| |
| import ( |
| "slices" |
| "strings" |
| |
| "golang.org/x/oscar/internal/gerrit" |
| "golang.org/x/oscar/internal/reviews" |
| ) |
| |
| // predicates is a list of [reviews.Predicate] classifiers |
| // that are specific to Go. |
| var predicates = []reviews.Predicate{ |
| { |
| Name: "hasPlusTwo", |
| Description: "change has a +2 vote", |
| Score: reviews.ScoreImportant, |
| Applies: hasPlusTwo, |
| }, |
| { |
| Name: "hasPlusOne", |
| Description: "change has a +1 vote", |
| Score: reviews.ScoreSuggested, |
| Applies: hasPlusOne, |
| }, |
| { |
| Name: "hasUnresolvedComments", |
| Description: "change has unresolved comments", |
| Score: reviews.ScoreUninteresting, |
| Applies: hasUnresolvedComments, |
| }, |
| { |
| Name: "trybotsPassed", |
| Description: "trybots passed", |
| Score: reviews.ScoreSuggested, |
| Applies: trybotsPassed, |
| }, |
| { |
| Name: "trybotsFailed", |
| Description: "trybots failed", |
| Score: reviews.ScoreUninteresting, |
| Applies: trybotsFailed, |
| }, |
| } |
| |
| // hasPlusTwo is a [reviews.Score] function that reports whether |
| // the change has a +2 vote. |
| func hasPlusTwo(ch reviews.Change) (bool, error) { |
| return hasCodeReviewVal(ch, 2) |
| } |
| |
| // hasPlusOne is a [reviews.Score] function that reports whether |
| // the change has a +1 vote. |
| func hasPlusOne(ch reviews.Change) (bool, error) { |
| return hasCodeReviewVal(ch, 1) |
| } |
| |
| // hasCodeReviewVal reports whether ch has a Code-Review label of val. |
| func hasCodeReviewVal(ch reviews.Change, val int) (bool, error) { |
| gc := ch.(goChange).GerritChange |
| label := gc.Client.GClient.ChangeLabel(gc.Change, "Code-Review") |
| if label == nil { |
| return false, nil |
| } |
| for _, ai := range label.All { |
| if ai.Value == val { |
| return true, nil |
| } |
| } |
| return false, nil |
| } |
| |
| // hasUnresolvedComments is a [reviews.Score] function that reports whether |
| // the change has unresolved comments. |
| func hasUnresolvedComments(ch reviews.Change) (bool, error) { |
| gc := ch.(goChange).GerritChange |
| _, unresolved := gc.Client.GClient.ChangeCommentCounts(gc.Change) |
| return unresolved > 0, nil |
| } |
| |
| // trybotsPassed is a [reviews.Score] function that reports whether |
| // the trybots passed. We check both the LUCI and non-LUCI trybots for now. |
| func trybotsPassed(ch reviews.Change) (bool, error) { |
| return trybotsCheck(ch, 1) |
| } |
| |
| // trybotsFailed is a [reviews.Score] function that reports whether |
| // the trybots failed. We check both the LUCI and non-LUCI trybots for now. |
| func trybotsFailed(ch reviews.Change) (bool, error) { |
| return trybotsCheck(ch, -1) |
| } |
| |
| // trybotsCheck reports whether the trybot results have the value val. |
| func trybotsCheck(ch reviews.Change, val int) (bool, error) { |
| gc := ch.(goChange).GerritChange |
| |
| seen := func(ai *gerrit.ApprovalInfo) bool { |
| return ai.Value == val |
| } |
| |
| label := gc.Client.GClient.ChangeLabel(gc.Change, "LUCI-TryBot-Result") |
| if label != nil && slices.ContainsFunc(label.All, seen) { |
| return true, nil |
| } |
| |
| label = gc.Client.GClient.ChangeLabel(gc.Change, "TryBot-Result") |
| if label != nil && slices.ContainsFunc(label.All, seen) { |
| return true, nil |
| } |
| |
| return false, nil |
| } |
| |
| // rejects is a list of [reviews.Reject] values that are specific to Go. |
| var rejects = []reviews.Reject{ |
| { |
| Name: "goreviewable", |
| Description: "whether the change is reviewable (Go specific)", |
| Applies: unreviewable, |
| }, |
| { |
| Name: "waitRelease", |
| Description: "change is tagged wait-release", |
| Applies: waitRelease, |
| }, |
| { |
| Name: "hold", |
| Description: "change is on hold", |
| Applies: hold, |
| }, |
| } |
| |
| // unreviewable is a [reviews.Reject] function that reports that |
| // a Go change is unreviewable. Putting "DO NOT REVIEW" in the description |
| // is a Go project convention for marking a change unreviewable. |
| func unreviewable(ch reviews.Change) (bool, error) { |
| desc := ch.Description() |
| if strings.Contains(desc, "DO NOT REVIEW") { |
| return true, nil |
| } |
| return false, nil |
| } |
| |
| // waitRelease is a [reviews.Reject] function that reports whether |
| // the change has the wait-release tag. |
| func waitRelease(ch reviews.Change) (bool, error) { |
| gc := ch.(goChange).GerritChange |
| tags := gc.Client.GClient.ChangeHashtags(gc.Change) |
| r := slices.Contains(tags, "wait-release") |
| return r, nil |
| } |
| |
| // hold is a [reviews.Reject] function that reports whether |
| // the change is on hold. |
| func hold(ch reviews.Change) (bool, error) { |
| gc := ch.(goChange).GerritChange |
| label := gc.Client.GClient.ChangeLabel(gc.Change, "Hold") |
| if label == nil { |
| return false, nil |
| } |
| for _, ai := range label.All { |
| if ai.Value == 1 { |
| return true, nil |
| } |
| } |
| return false, nil |
| } |