Skip to content

Commit b324c99

Browse files
fix: treat fast-track with not enough approvals as non-fatal (#676)
* fix: treat `fast-track` with not enough approvals as non-fatal * fix: update error messages * fix: error message fixup * fix: error message fixup * test: add test for fast-track with insufficient approvals
1 parent ebcf18e commit b324c99

File tree

2 files changed

+69
-16
lines changed

2 files changed

+69
-16
lines changed

lib/pr_checker.js

+18-12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const WAIT_TIME_SINGLE_APPROVAL = 24 * 7;
2727
const GITHUB_SUCCESS_CONCLUSIONS = ['SUCCESS', 'NEUTRAL', 'SKIPPED'];
2828

2929
const FAST_TRACK_RE = /^Fast-track has been requested by @(.+?)\. Please 👍 to approve\.$/;
30+
const FAST_TRACK_MIN_APPROVALS = 2;
3031
const GIT_CONFIG_GUIDE_URL = 'https://github.com/nodejs/node/blob/99b1ada/doc/guides/contributing/pull-requests.md#step-1-fork';
3132

3233
export default class PRChecker {
@@ -138,7 +139,7 @@ export default class PRChecker {
138139
const { requestedChanges, approved } = reviewers;
139140
const labels = pr.labels.nodes.map((l) => l.name);
140141

141-
const isFastTracked = labels.includes('fast-track');
142+
let isFastTracked = labels.includes('fast-track');
142143
const isCodeAndLearn = labels.includes('code-and-learn');
143144
const isSemverMajor = labels.includes('semver-major');
144145

@@ -168,6 +169,7 @@ export default class PRChecker {
168169
}
169170
}
170171

172+
let fastTrackAppendix = '';
171173
if (isFastTracked) {
172174
const comment = [...this.comments].reverse().find((c) =>
173175
FAST_TRACK_RE.test(c.bodyText));
@@ -183,14 +185,15 @@ export default class PRChecker {
183185
r.user.login !== pr.author.login &&
184186
collaborators.includes(r.user.login.toLowerCase())).length;
185187

186-
if (requester === pr.author.login && approvals < 2) {
187-
cli.error('The fast-track request requires' +
188-
" at least two collaborators' approvals (👍).");
189-
return false;
190-
} else if (approvals === 0) {
191-
cli.error('The fast-track request requires' +
192-
" at least one collaborator's approval (👍).");
193-
return false;
188+
const missingFastTrackApprovals = FAST_TRACK_MIN_APPROVALS - approvals -
189+
(requester === pr.author.login ? 0 : 1);
190+
if (missingFastTrackApprovals > 0) {
191+
isFastTracked = false;
192+
fastTrackAppendix = ' (or 0 hours if there ' +
193+
`${missingFastTrackApprovals === 1 ? 'is' : 'are'} ` +
194+
`${missingFastTrackApprovals} more approval` +
195+
`${missingFastTrackApprovals === 1 ? '' : 's'} (👍) of ` +
196+
'the fast-track request from collaborators).';
194197
}
195198
}
196199

@@ -211,10 +214,12 @@ export default class PRChecker {
211214
if (timeLeftMulti === 0) {
212215
const timeLeftMins =
213216
this.waitTimeMultiApproval * 60 - minutesFromCreateTime;
214-
cli.error(`This PR needs to wait ${timeLeftMins} more minutes to land`);
217+
cli.error(`This PR needs to wait ${timeLeftMins} ` +
218+
`more minutes to land${fastTrackAppendix}`);
215219
return false;
216220
}
217-
cli.error(`This PR needs to wait ${timeLeftMulti} more hours to land`);
221+
cli.error(`This PR needs to wait ${timeLeftMulti} more ` +
222+
`hours to land${fastTrackAppendix}`);
218223
return false;
219224
}
220225

@@ -224,7 +229,8 @@ export default class PRChecker {
224229
}
225230
timeLeftMulti = timeLeftMulti < 0 || isFastTracked ? 0 : timeLeftMulti;
226231
cli.error(`This PR needs to wait ${timeLeftSingle} more hours to land ` +
227-
`(or ${timeLeftMulti} hours if there is one more approval)`);
232+
`(or ${timeLeftMulti} hours if there is one more approval)` +
233+
fastTrackAppendix);
228234
return false;
229235
}
230236
}

test/unit/pr_checker.test.js

+51-4
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,9 @@ describe('PRChecker', () => {
644644
[['This PR was created on Fri, 30 Nov 2018 17:50:44 GMT'],
645645
['This PR is being fast-tracked']],
646646
error:
647-
[['The fast-track request requires at' +
648-
" least two collaborators' approvals (👍)."]]
647+
[['This PR needs to wait 24 more hours to land (or 0 hours if ' +
648+
'there are 2 more approvals (👍) of the fast-track request from ' +
649+
'collaborators).']]
649650
};
650651

651652
const pr = Object.assign({}, firstTimerPR, {
@@ -694,8 +695,9 @@ describe('PRChecker', () => {
694695
[['This PR was created on Fri, 30 Nov 2018 17:50:44 GMT'],
695696
['This PR is being fast-tracked']],
696697
error:
697-
[['The fast-track request requires at' +
698-
" least one collaborator's approval (👍)."]]
698+
[['This PR needs to wait 24 more hours to land (or 0 hours if ' +
699+
'there is 1 more approval (👍) of the fast-track request from ' +
700+
'collaborators).']]
699701
};
700702

701703
const pr = Object.assign({}, firstTimerPR, {
@@ -727,6 +729,51 @@ describe('PRChecker', () => {
727729
cli.assertCalledWith(expectedLogs);
728730
});
729731

732+
it('should error when not enough approvals or fast-track approvals', () => {
733+
const cli = new TestCLI();
734+
735+
const expectedLogs = {
736+
ok:
737+
[['Approvals: 1'],
738+
['- Foo User (@foo): https://github.com/nodejs/node/pull/16438#pullrequestreview-71480624']],
739+
info:
740+
[['This PR was created on Fri, 30 Nov 2018 17:50:44 GMT'],
741+
['This PR is being fast-tracked']],
742+
error:
743+
[['This PR needs to wait 144 more hours to land (or 24 hours if ' +
744+
'there is one more approval) (or 0 hours if there is 1 more ' +
745+
'approval (👍) of the fast-track request from collaborators).']]
746+
};
747+
748+
const pr = Object.assign({}, firstTimerPR, {
749+
createdAt: LT_48H,
750+
labels: {
751+
nodes: [
752+
{ name: 'fast-track' }
753+
]
754+
}
755+
});
756+
757+
const data = {
758+
pr,
759+
reviewers: singleGreenReviewer,
760+
comments: commentsWithFastTrackInsuffientApprovals,
761+
reviews: approvingReviews,
762+
commits: [],
763+
collaborators,
764+
authorIsNew: () => true,
765+
getThread() {
766+
return PRData.prototype.getThread.call(this);
767+
}
768+
};
769+
const checker = new PRChecker(cli, data, {}, argv);
770+
771+
cli.clearCalls();
772+
const status = checker.checkReviewsAndWait(new Date(NOW));
773+
assert(!status);
774+
cli.assertCalledWith(expectedLogs);
775+
});
776+
730777
it('should error when missing fast-track request comment', () => {
731778
const cli = new TestCLI();
732779

0 commit comments

Comments
 (0)