@@ -4,8 +4,8 @@ const SECOND = 1000;
4
4
const MINUTE = SECOND * 60 ;
5
5
const HOUR = MINUTE * 60 ;
6
6
7
- const WAIT_TIME = 48 ;
8
- const WAIT_TIME_SINGLE_APPROVAL = 168 ;
7
+ const WAIT_TIME_MULTI_APPROVAL = 24 * 2 ;
8
+ const WAIT_TIME_SINGLE_APPROVAL = 24 * 7 ;
9
9
10
10
const {
11
11
REVIEW_SOURCES : { FROM_COMMENT , FROM_REVIEW_COMMENT }
@@ -49,12 +49,11 @@ class PRChecker {
49
49
) ;
50
50
}
51
51
52
- checkAll ( comments = false ) {
52
+ checkAll ( checkComments = false ) {
53
53
const status = [
54
- this . checkReviews ( comments ) ,
55
54
this . checkCommitsAfterReview ( ) ,
56
55
this . checkCI ( ) ,
57
- this . checkPRWait ( new Date ( ) ) ,
56
+ this . checkReviewsAndWait ( new Date ( ) , checkComments ) ,
58
57
this . checkMergeableState ( ) ,
59
58
this . checkPRState ( ) ,
60
59
this . checkGitConfig ( )
@@ -70,132 +69,104 @@ class PRChecker {
70
69
return status . every ( ( i ) => i ) ;
71
70
}
72
71
73
- getTSCHint ( people ) {
74
- const tsc = people
72
+ getTSC ( people ) {
73
+ return people
75
74
. filter ( ( p ) => p . reviewer . isTSC ( ) )
76
75
. map ( ( p ) => p . reviewer . login ) ;
76
+ }
77
+
78
+ formatReview ( reviewer , review ) {
77
79
let hint = '' ;
78
- if ( tsc . length > 0 ) {
79
- const list = `(${ tsc . join ( ', ' ) } )` ;
80
- hint = `, ${ tsc . length } from TSC ${ list } ` ;
80
+ if ( reviewer . isTSC ( ) ) {
81
+ hint = ' (TSC)' ;
81
82
}
82
- return hint ;
83
+ return `- ${ reviewer . getName ( ) } ${ hint } : ${ review . ref } ` ;
83
84
}
84
85
85
- checkReviews ( comments = false ) {
86
- const {
87
- pr, cli, reviewers : { requestedChanges, approved }
88
- } = this ;
89
- let status = true ;
90
-
91
- if ( requestedChanges . length === 0 ) {
92
- cli . ok ( `Requested Changes: 0` ) ;
93
- } else {
94
- status = false ;
95
- let hint = this . getTSCHint ( requestedChanges ) ;
96
- cli . error ( `Requested Changes: ${ requestedChanges . length } ${ hint } ` ) ;
86
+ displayReviews ( checkComments ) {
87
+ const { cli, reviewers : { requestedChanges, approved } } = this ;
88
+ if ( requestedChanges . length > 0 ) {
89
+ cli . error ( `Requested Changes: ${ requestedChanges . length } ` ) ;
97
90
for ( const { reviewer, review } of requestedChanges ) {
98
- cli . error ( `- ${ reviewer . getName ( ) } : ${ review . ref } ` ) ;
91
+ cli . error ( this . formatReview ( reviewer , review ) ) ;
99
92
}
100
93
}
94
+
101
95
if ( approved . length === 0 ) {
102
- status = false ;
103
96
cli . error ( `Approvals: 0` ) ;
104
- } else {
105
- let hint = this . getTSCHint ( approved ) ;
106
- cli . ok ( `Approvals: ${ approved . length } ${ hint } ` ) ;
107
-
108
- if ( comments ) {
109
- for ( const { reviewer, review } of approved ) {
110
- if ( review . source === FROM_COMMENT ||
111
- review . source === FROM_REVIEW_COMMENT ) {
112
- cli . info (
113
- `- ${ reviewer . getName ( ) } approved in via LGTM in comments` ) ;
114
- }
115
- }
116
- }
97
+ return ;
98
+ }
117
99
118
- const labels = pr . labels . nodes . map ( ( l ) => l . name ) ;
119
- if ( labels . includes ( 'semver-major' ) ) {
120
- const tscApproval = approved . filter ( ( p ) => p . reviewer . isTSC ( ) ) . length ;
121
- if ( tscApproval < 2 ) {
122
- status = false ;
123
- cli . error ( 'semver-major requires at least two TSC approvals' ) ;
124
- }
100
+ cli . ok ( `Approvals: ${ approved . length } ` ) ;
101
+ for ( const { reviewer, review } of approved ) {
102
+ cli . ok ( this . formatReview ( reviewer , review ) ) ;
103
+ if ( checkComments &&
104
+ [ FROM_COMMENT , FROM_REVIEW_COMMENT ] . includes ( review . source ) ) {
105
+ cli . info ( `- ${ reviewer . getName ( ) } approved in via LGTM in comments` ) ;
125
106
}
126
107
}
127
-
128
- return status ;
129
108
}
130
109
131
- /**
132
- * @param {Date } now
133
- */
134
- getWait ( now ) {
110
+ checkReviewsAndWait ( now , checkComments ) {
111
+ const {
112
+ pr, cli, reviewers
113
+ } = this ;
114
+ const { requestedChanges, approved } = reviewers ;
115
+ const labels = pr . labels . nodes . map ( ( l ) => l . name ) ;
116
+
117
+ const isFastTracked = labels . includes ( 'fast-track' ) ;
118
+ const isSemverMajor = labels . includes ( 'semver-major' ) ;
119
+
120
+ const dateStr = new Date ( pr . createdAt ) . toUTCString ( ) ;
121
+ cli . info ( `This PR was created on ${ dateStr } ` ) ;
122
+ this . displayReviews ( checkComments ) ;
123
+ // NOTE: a semver-major PR with fast-track should have either one of
124
+ // these labels removed because that doesn't make sense
125
+ if ( isFastTracked ) {
126
+ cli . info ( 'This PR is being fast-tracked' ) ;
127
+ }
128
+
129
+ if ( approved . length === 0 || requestedChanges . length > 0 ) {
130
+ return false ;
131
+ }
132
+
133
+ if ( isSemverMajor ) {
134
+ const tscApproved = approved
135
+ . filter ( ( p ) => p . reviewer . isTSC ( ) )
136
+ . map ( ( p ) => p . reviewer . login ) ;
137
+ if ( tscApproved . length < 2 ) {
138
+ cli . error ( 'semver-major requires at least 2 TSC approvals' ) ;
139
+ return false ; // 7 day rule doesn't matter here
140
+ }
141
+ }
142
+
135
143
const createTime = new Date ( this . pr . createdAt ) ;
136
144
const hoursFromCreateTime = Math . ceil (
137
145
( now . getTime ( ) - createTime . getTime ( ) ) / HOUR
138
146
) ;
139
- const timeLeft = WAIT_TIME - hoursFromCreateTime ;
140
- const timeLeftSingleApproval =
141
- WAIT_TIME_SINGLE_APPROVAL - hoursFromCreateTime ;
142
-
143
- return {
144
- timeLeft,
145
- timeLeftSingleApproval
146
- } ;
147
- }
147
+ let timeLeftMulti = WAIT_TIME_MULTI_APPROVAL - hoursFromCreateTime ;
148
+ const timeLeftSingle = WAIT_TIME_SINGLE_APPROVAL - hoursFromCreateTime ;
148
149
149
- // TODO: skip some PRs...we might need a label for that
150
- /**
151
- * @param {Date } now
152
- */
153
- checkPRWait ( now ) {
154
- const {
155
- pr, cli, reviewers, CIStatus
156
- } = this ;
157
- const { approved } = reviewers ;
158
- const labels = pr . labels . nodes ;
159
-
160
- const fast =
161
- labels . some ( ( l ) => [ 'fast-track' ] . includes ( l . name ) ) ;
162
- if ( fast ) {
163
- if ( approved . length > 1 && CIStatus ) {
164
- cli . info ( 'This PR is being fast-tracked' ) ;
150
+ if ( approved . length >= 2 ) {
151
+ if ( isFastTracked ) {
165
152
return true ;
166
- } else {
167
- const msg = [ 'This PR is being fast-tracked, but awaiting ' ] ;
168
- if ( approved . length < 2 ) msg . push ( 'approvals of 2 contributors' ) ;
169
- if ( ! CIStatus ) msg . push ( 'a CI run' ) ;
170
-
171
- let warnMsg = msg . length === 2
172
- ? msg . join ( '' ) : `${ msg [ 0 ] + msg [ 1 ] } and ${ msg [ 2 ] } ` ;
173
- cli . warn ( warnMsg ) ;
174
153
}
175
-
154
+ if ( timeLeftMulti < 0 ) {
155
+ return true ;
156
+ }
157
+ cli . error ( `This PR needs to wait ${ timeLeftMulti } more hours to land` ) ;
176
158
return false ;
177
159
}
178
160
179
- const dateStr = new Date ( pr . createdAt ) . toDateString ( ) ;
180
- cli . info ( `This PR was created on ${ dateStr } ` ) ;
181
-
182
- const wait = this . getWait ( now ) ;
183
- if ( approved . length > 1 && wait . timeLeft > 0 ) {
184
- cli . warn ( `Wait at least ${ wait . timeLeft } more hours before landing` ) ;
185
- return false ;
186
- } else if ( approved . length === 1 && wait . timeLeftSingleApproval > 0 ) {
187
- const waitMsg = wait . timeLeft > 0
188
- ? ` and ${ wait . timeLeft } more hours`
189
- : '' ;
190
- cli . warn ( 'Wait at one of the following:' ) ;
191
- cli . warn ( `* another approval${ waitMsg } ` ) ;
192
- cli . warn ( `* ${ wait . timeLeftSingleApproval } more hours` +
193
- ' with existing single approval' ) ;
161
+ if ( approved . length === 1 ) {
162
+ if ( timeLeftSingle < 0 ) {
163
+ return true ;
164
+ }
165
+ timeLeftMulti = timeLeftMulti < 0 ? 0 : timeLeftMulti ;
166
+ cli . error ( `This PR needs to wait ${ timeLeftSingle } more hours to land ` +
167
+ `(or ${ timeLeftMulti } hours if there is one more approval)` ) ;
194
168
return false ;
195
169
}
196
-
197
- cli . info ( 'No more waiting required' ) ;
198
- return true ;
199
170
}
200
171
201
172
hasFullOrLiteCI ( ciMap ) {
0 commit comments