1515package checks
1616
1717import (
18- "fmt"
19- "strings"
20-
2118 "github.com/ossf/scorecard/v4/checker"
19+ "github.com/ossf/scorecard/v4/checks/evaluation"
20+ "github.com/ossf/scorecard/v4/checks/raw"
2221 sce "github.com/ossf/scorecard/v4/errors"
2322)
2423
@@ -27,196 +26,26 @@ const CheckCodeReview = "Code-Review"
2726
2827//nolint:gochecknoinits
2928func init () {
30- if err := registerCheck (CheckCodeReview , DoesCodeReview ); err != nil {
29+ if err := registerCheck (CheckCodeReview , CodeReview ); err != nil {
3130 // this should never happen
3231 panic (err )
3332 }
3433}
3534
36- // DoesCodeReview attempts to determine whether a project requires review before code gets merged.
37- // It uses a set of heuristics:
38- // - Looking at the repo configuration to see if reviews are required.
39- // - Checking if most of the recent merged PRs were "Approved".
40- // - Looking for other well-known review labels.
41- func DoesCodeReview (c * checker.CheckRequest ) checker.CheckResult {
42- // GitHub reviews.
43- ghScore , ghReason , err := githubCodeReview (c )
44- if err != nil {
45- return checker .CreateRuntimeErrorResult (CheckCodeReview , err )
46- }
47-
48- // Review messages.
49- hintScore , hintReason , err := commitMessageHints (c )
50- if err != nil {
51- return checker .CreateRuntimeErrorResult (CheckCodeReview , err )
52- }
53-
54- score , reason := selectBestScoreAndReason (hintScore , ghScore , hintReason , ghReason , c .Dlogger )
55-
56- // Prow CI/CD.
57- prowScore , prowReason , err := prowCodeReview (c )
58- if err != nil {
59- return checker .CreateRuntimeErrorResult (CheckCodeReview , err )
60- }
61-
62- score , reason = selectBestScoreAndReason (prowScore , score , prowReason , reason , c .Dlogger )
63- if score == checker .MinResultScore {
64- c .Dlogger .Info3 (& checker.LogMessage {
65- Text : reason ,
66- })
67- return checker .CreateResultWithScore (CheckCodeReview , "no reviews detected" , score )
68- }
69-
70- if score == checker .InconclusiveResultScore {
71- return checker .CreateInconclusiveResult (CheckCodeReview , "no reviews detected" )
72- }
73-
74- return checker .CreateResultWithScore (CheckCodeReview , checker .NormalizeReason (reason , score ), score )
75- }
76-
77- //nolint
78- func selectBestScoreAndReason (s1 , s2 int , r1 , r2 string ,
79- dl checker.DetailLogger ) (int , string ) {
80- if s1 > s2 {
81- dl .Info3 (& checker.LogMessage {
82- Text : r2 ,
83- })
84- return s1 , r1
85- }
86-
87- dl .Info3 (& checker.LogMessage {
88- Text : r1 ,
89- })
90- return s2 , r2
91- }
92-
93- //nolint
94- func githubCodeReview (c * checker.CheckRequest ) (int , string , error ) {
95- // Look at some merged PRs to see if they were reviewed.
96- totalMerged := 0
97- totalReviewed := 0
98- prs , err := c .RepoClient .ListMergedPRs ()
35+ // CodeReview will check if the maintainers perform code review.
36+ func CodeReview (c * checker.CheckRequest ) checker.CheckResult {
37+ rawData , err := raw .CodeReview (c .RepoClient )
9938 if err != nil {
100- return 0 , "" , sce .WithMessage (sce .ErrScorecardInternal , fmt .Sprintf ("RepoClient.ListMergedPRs: %v" , err ))
39+ e := sce .WithMessage (sce .ErrScorecardInternal , err .Error ())
40+ return checker .CreateRuntimeErrorResult (CheckCodeReview , e )
10141 }
102- for _ , pr := range prs {
103- if pr .MergedAt .IsZero () {
104- continue
105- }
106- totalMerged ++
107-
108- // Check if the PR is approved by a reviewer.
109- foundApprovedReview := false
110- for _ , r := range pr .Reviews {
111- if r .State == "APPROVED" {
112- c .Dlogger .Debug3 (& checker.LogMessage {
113- Text : fmt .Sprintf ("found review approved pr: %d" , pr .Number ),
114- })
115- totalReviewed ++
116- foundApprovedReview = true
117- break
118- }
119- }
120-
121- // Check if the PR is committed by someone other than author. this is kind
122- // of equivalent to a review and is done several times on small prs to save
123- // time on clicking the approve button.
124- if ! foundApprovedReview &&
125- pr .MergeCommit .Committer .Login != "" &&
126- pr .MergeCommit .Committer .Login != pr .Author .Login {
127- c .Dlogger .Debug3 (& checker.LogMessage {
128- Text : fmt .Sprintf ("found PR#%d with committer (%s) different from author (%s)" ,
129- pr .Number , pr .Author .Login , pr .MergeCommit .Committer .Login ),
130- })
131- totalReviewed ++
132- foundApprovedReview = true
133- }
134-
135- if ! foundApprovedReview {
136- c .Dlogger .Debug3 (& checker.LogMessage {
137- Text : fmt .Sprintf ("merged PR without code review: %d" , pr .Number ),
138- })
139- }
140-
141- }
142-
143- return createReturn ("GitHub" , totalReviewed , totalMerged )
144- }
145-
146- //nolint
147- func prowCodeReview (c * checker.CheckRequest ) (int , string , error ) {
148- // Look at some merged PRs to see if they were reviewed
149- totalMerged := 0
150- totalReviewed := 0
151- prs , err := c .RepoClient .ListMergedPRs ()
152- if err != nil {
153- sce .WithMessage (sce .ErrScorecardInternal , fmt .Sprintf ("RepoClient.ListMergedPRs: %v" , err ))
154- }
155- for _ , pr := range prs {
156- if pr .MergedAt .IsZero () {
157- continue
158- }
159- totalMerged ++
160- for _ , l := range pr .Labels {
161- if l .Name == "lgtm" || l .Name == "approved" {
162- totalReviewed ++
163- break
164- }
165- }
166- }
167-
168- return createReturn ("Prow" , totalReviewed , totalMerged )
169- }
170-
171- //nolint
172- func commitMessageHints (c * checker.CheckRequest ) (int , string , error ) {
173- commits , err := c .RepoClient .ListCommits ()
174- if err != nil {
175- return checker .InconclusiveResultScore , "" ,
176- sce .WithMessage (sce .ErrScorecardInternal , fmt .Sprintf ("Client.Repositories.ListCommits: %v" , err ))
177- }
178-
179- total := 0
180- totalReviewed := 0
181- for _ , commit := range commits {
182- isBot := false
183- committer := commit .Committer .Login
184- for _ , substring := range []string {"bot" , "gardener" } {
185- if strings .Contains (committer , substring ) {
186- isBot = true
187- break
188- }
189- }
190- if isBot {
191- c .Dlogger .Debug3 (& checker.LogMessage {
192- Text : fmt .Sprintf ("skip commit from bot account: %s" , committer ),
193- })
194- continue
195- }
196-
197- total ++
198-
199- // check for gerrit use via Reviewed-on and Reviewed-by
200- commitMessage := commit .Message
201- if strings .Contains (commitMessage , "\n Reviewed-on: " ) &&
202- strings .Contains (commitMessage , "\n Reviewed-by: " ) {
203- c .Dlogger .Debug3 (& checker.LogMessage {
204- Text : fmt .Sprintf ("Gerrit review found for commit '%s'" , commit .SHA ),
205- })
206- totalReviewed ++
207- continue
208- }
209- }
210-
211- return createReturn ("Gerrit" , totalReviewed , total )
212- }
21342
214- //nolint
215- func createReturn (reviewName string , reviewed , total int ) (int , string , error ) {
216- if total > 0 {
217- reason := fmt .Sprintf ("%s code reviews found for %v commits out of the last %v" , reviewName , reviewed , total )
218- return checker .CreateProportionalScore (reviewed , total ), reason , nil
43+ // Return raw results.
44+ if c .RawResults != nil {
45+ c .RawResults .CodeReviewResults = rawData
46+ return checker.CheckResult {}
21947 }
22048
221- return checker .InconclusiveResultScore , fmt .Sprintf ("no %v commits found" , reviewName ), nil
49+ // Return the score evaluation.
50+ return evaluation .CodeReview (CheckCodeReview , c .Dlogger , & rawData )
22251}
0 commit comments