Skip to content

Commit 75abd3a

Browse files
authored
feat: check CI failures before land (#422)
1 parent f58051c commit 75abd3a

File tree

4 files changed

+177
-130
lines changed

4 files changed

+177
-130
lines changed

components/metadata.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const fs = require('fs');
1111

1212
module.exports = async function getMetadata(argv, cli) {
1313
const credentials = await auth({
14-
github: true
14+
github: true,
15+
jenkins: true
1516
});
1617
const request = new Request(credentials);
1718

@@ -38,8 +39,8 @@ module.exports = async function getMetadata(argv, cli) {
3839
cli.write(metadata);
3940
cli.separator();
4041

41-
const checker = new PRChecker(cli, data, argv);
42-
const status = checker.checkAll(argv.checkComments);
42+
const checker = new PRChecker(cli, data, request, argv);
43+
const status = await checker.checkAll(argv.checkComments);
4344
return {
4445
status,
4546
request,

lib/ci/ci_type_parser.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ const CI_TYPE_ENUM = {
2626
JOB_CI: 1 << 2
2727
};
2828

29+
const CI_PROVIDERS = {
30+
JENKINS: 'jenkins',
31+
GITHUB: 'github-check'
32+
};
33+
2934
const { JOB_CI, FULL_CI, LITE_CI } = CI_TYPE_ENUM;
3035

3136
const CI_TYPES = new Map([
@@ -209,6 +214,7 @@ module.exports = {
209214
CITGM, PR, COMMIT, BENCHMARK, LIBUV, V8, NOINTL,
210215
LINTER, LITE_PR, LITE_COMMIT
211216
},
217+
CI_PROVIDERS,
212218
isFullCI,
213219
isLiteCI,
214220
JobParser,

lib/pr_checker.js

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ const {
2020
const {
2121
JobParser,
2222
CI_TYPES,
23+
CI_PROVIDERS,
2324
isLiteCI,
2425
isFullCI
2526
} = require('./ci/ci_type_parser');
27+
const { PRBuild } = require('./ci/ci_result_parser');
2628

2729
const GIT_CONFIG_GUIDE_URL = 'https://github.com/nodejs/node/blob/99b1ada/doc/guides/contributing/pull-requests.md#step-1-fork';
2830

@@ -31,8 +33,9 @@ class PRChecker {
3133
* @param {{}} cli
3234
* @param {PRData} data
3335
*/
34-
constructor(cli, data, argv) {
36+
constructor(cli, data, request, argv) {
3537
this.cli = cli;
38+
this.request = request;
3639
this.data = data;
3740
const {
3841
pr, reviewers, comments, reviews, commits, collaborators
@@ -66,10 +69,10 @@ class PRChecker {
6669
return this.argv.waitTimeMultiApproval;
6770
}
6871

69-
checkAll(checkComments = false) {
72+
async checkAll(checkComments = false) {
7073
const status = [
7174
this.checkCommitsAfterReview(),
72-
this.checkCI(),
75+
await this.checkCI(),
7376
this.checkReviewsAndWait(new Date(), checkComments),
7477
this.checkMergeableState(),
7578
this.checkPRState(),
@@ -201,32 +204,42 @@ class PRChecker {
201204
return cis.find(ci => isFullCI(ci) || isLiteCI(ci));
202205
}
203206

204-
checkCI() {
205-
const ciType = this.argv.ciType || 'jenkins';
206-
if (ciType === 'jenkins') {
207-
return this.checkJenkinsCI();
208-
} else if (ciType === 'github-check') {
209-
return this.checkGitHubCI();
207+
async checkCI() {
208+
const ciType = this.argv.ciType || CI_PROVIDERS.JENKINS;
209+
const providers = Object.values(CI_PROVIDERS);
210+
211+
if (!providers.includes(ciType)) {
212+
this.cli.error(
213+
`Invalid ciType ${ciType} - must be one of ${providers.join(', ')}`);
214+
return false;
210215
}
211-
this.cli.error(`Invalid ciType: ${ciType}`);
212-
return false;
216+
217+
let status = false;
218+
if (ciType === CI_PROVIDERS.JENKINS) {
219+
status = await this.checkJenkinsCI();
220+
} else if (ciType === CI_PROVIDERS.GITHUB) {
221+
status = this.checkGitHubCI();
222+
}
223+
224+
return status;
213225
}
214226

215227
// TODO: we might want to check CI status when it's less flaky...
216228
// TODO: not all PR requires CI...labels?
217-
checkJenkinsCI() {
218-
const { cli, commits, argv } = this;
229+
async checkJenkinsCI() {
230+
const { cli, commits, request, argv } = this;
219231
const { maxCommits } = argv;
220232
const thread = this.data.getThread();
221233
const ciMap = new JobParser(thread).parse();
234+
222235
let status = true;
223236
if (!ciMap.size) {
224237
cli.error('No CI runs detected');
225238
this.CIStatus = false;
226239
return false;
227240
} else if (!this.hasFullOrLiteCI(ciMap)) {
228241
status = false;
229-
cli.error('No full CI runs or lite CI runs detected');
242+
cli.error('No full or lite Jenkins CI runs detected');
230243
}
231244

232245
let lastCI;
@@ -236,7 +249,8 @@ class PRChecker {
236249
if (!lastCI || lastCI.date < ci.date) {
237250
lastCI = {
238251
typeName: name,
239-
date: ci.date
252+
date: ci.date,
253+
jobId: ci.jobid
240254
};
241255
}
242256
}
@@ -270,6 +284,16 @@ class PRChecker {
270284
cli.warn(infoMsg);
271285
}
272286
}
287+
288+
// Check the last CI run for its results.
289+
const build = new PRBuild(cli, request, lastCI.jobId);
290+
const { result, failures } = await build.getResults();
291+
292+
if (result === 'FAILURE') {
293+
cli.error(
294+
`${failures.length} failure(s) on the last Jenkins CI run`);
295+
status = false;
296+
}
273297
}
274298

275299
this.CIStatus = status;
@@ -298,12 +322,12 @@ class PRChecker {
298322
// GitHub new Check API
299323
for (const { status, conclusion } of checkSuites.nodes) {
300324
if (status !== 'COMPLETED') {
301-
cli.error('CI is still running');
325+
cli.error('GitHub CI is still running');
302326
return false;
303327
}
304328

305329
if (!['SUCCESS', 'NEUTRAL'].includes(conclusion)) {
306-
cli.error('Last CI failed');
330+
cli.error('Last GitHub CI failed');
307331
return false;
308332
}
309333
}
@@ -312,17 +336,17 @@ class PRChecker {
312336
if (commit.status) {
313337
const { state } = commit.status;
314338
if (state === 'PENDING') {
315-
cli.error('CI is still running');
339+
cli.error('GitHub CI is still running');
316340
return false;
317341
}
318342

319343
if (!['SUCCESS', 'EXPECTED'].includes(state)) {
320-
cli.error('Last CI failed');
344+
cli.error('Last GitHub CI failed');
321345
return false;
322346
}
323347
}
324348

325-
cli.info('Last CI run was successful');
349+
cli.info('Last GitHub CI successful');
326350
this.CIStatus = true;
327351
return true;
328352
}

0 commit comments

Comments
 (0)