From b014e26f33b38b149447d25cdc434f1e7dc7d7f5 Mon Sep 17 00:00:00 2001 From: Mary Marchini Date: Sat, 8 Aug 2020 19:15:00 -0700 Subject: [PATCH] feat: check Actions and handle doc-only changes Doc-only changes don't need a full Jenkins CI, instead we can check if the last Actions run was successful. Therefore this commit also adds check for Action runs. Jenkins CI messages were improved as well. Fix: https://github.com/nodejs/node-core-utils/issues/324 Fix: https://github.com/nodejs/node/issues/32335 Fix: https://github.com/nodejs/node/issues/29770 --- lib/ci/ci_type_parser.js | 4 +- lib/pr_checker.js | 83 ++- lib/queries/PR.gql | 5 + lib/session.js | 2 +- test/fixtures/comments_with_failed_ci.json | 6 + test/fixtures/comments_with_pending_ci.json | 6 + test/fixtures/data.js | 17 + .../pending/node-test-pull-request-32777.json | 528 ++++++++++++++++++ test/unit/pr_checker.test.js | 166 +++++- 9 files changed, 802 insertions(+), 15 deletions(-) create mode 100644 test/fixtures/comments_with_failed_ci.json create mode 100644 test/fixtures/comments_with_pending_ci.json create mode 100644 test/fixtures/jenkins/pending/node-test-pull-request-32777.json diff --git a/lib/ci/ci_type_parser.js b/lib/ci/ci_type_parser.js index dd87a09d..2d7abed8 100644 --- a/lib/ci/ci_type_parser.js +++ b/lib/ci/ci_type_parser.js @@ -25,8 +25,8 @@ const CI_TYPE_ENUM = { }; const CI_PROVIDERS = { - JENKINS: 'jenkins', - GITHUB: 'github-check' + GITHUB: 'github-check', + NODEJS: 'nodejs' }; const { JOB_CI, FULL_CI } = CI_TYPE_ENUM; diff --git a/lib/pr_checker.js b/lib/pr_checker.js index ca46a0c9..c76265f5 100644 --- a/lib/pr_checker.js +++ b/lib/pr_checker.js @@ -204,7 +204,7 @@ class PRChecker { } async checkCI() { - const ciType = this.argv.ciType || CI_PROVIDERS.JENKINS; + const ciType = this.argv.ciType || CI_PROVIDERS.NODEJS; const providers = Object.values(CI_PROVIDERS); if (!providers.includes(ciType)) { @@ -214,8 +214,8 @@ class PRChecker { } let status = false; - if (ciType === CI_PROVIDERS.JENKINS) { - status = await this.checkJenkinsCI(); + if (ciType === CI_PROVIDERS.NODEJS) { + status = await this.checkNodejsCI(); } else if (ciType === CI_PROVIDERS.GITHUB) { status = this.checkGitHubCI(); } @@ -233,7 +233,7 @@ class PRChecker { let status = true; if (!ciMap.size) { - cli.error('No CI runs detected'); + cli.error('No Jenkins CI runs detected'); this.CIStatus = false; return false; } else if (!this.hasFullCI(ciMap)) { @@ -292,6 +292,13 @@ class PRChecker { cli.error( `${failures.length} failure(s) on the last Jenkins CI run`); status = false; + // NOTE(mmarchini): not sure why PEDING returns null + } else if (result === null) { + cli.error( + 'Last Jenkins CI still running'); + status = false; + } else { + cli.ok('Last Jenkins CI successful'); } } @@ -299,10 +306,47 @@ class PRChecker { return status; } + checkActionsCI() { + const { cli, commits } = this; + + if (!commits || commits.length === 0) { + cli.error('No commits detected'); + return false; + } + + // NOTE(mmarchini): we only care about the last commit. Maybe in the future + // we'll want to check all commits for a successful CI. + const { commit } = commits[commits.length - 1]; + + this.CIStatus = false; + const checkSuites = commit.checkSuites || { nodes: [] }; + if (!commit.status && checkSuites.nodes.length === 0) { + cli.error('No GitHub CI runs detected'); + return false; + } + + // GitHub new Check API + for (const { status, conclusion } of checkSuites.nodes) { + if (status !== 'COMPLETED') { + cli.error('GitHub CI is still running'); + return false; + } + + if (!['SUCCESS', 'NEUTRAL'].includes(conclusion)) { + cli.error('Last GitHub CI failed'); + return false; + } + } + + cli.ok('Last GitHub Actions successful'); + this.CIStatus = true; + return true; + } + checkGitHubCI() { const { cli, commits } = this; - if (!commits) { + if (!commits || commits.length === 0) { cli.error('No commits detected'); return false; } @@ -314,7 +358,7 @@ class PRChecker { this.CIStatus = false; const checkSuites = commit.checkSuites || { nodes: [] }; if (!commit.status && checkSuites.nodes.length === 0) { - cli.error('No CI runs detected'); + cli.error('No GitHub CI runs detected'); return false; } @@ -350,6 +394,33 @@ class PRChecker { return true; } + isOnlyDocChanges() { + const { cli, pr } = this; + + // NOTE(mmarchini): if files not present, fallback + // to old behavior. This should only be the case on old tests + // TODO(mmarchini): add files to all fixtures on old tests + if (!pr.files) { + return false; + } + + for (const { path } of pr.files.nodes) { + if (!path.endsWith('.md')) { + return false; + } + } + cli.info('Doc-only changes'); + return true; + } + + async checkNodejsCI() { + let status = this.checkActionsCI(); + if (!this.isOnlyDocChanges()) { + status &= await this.checkJenkinsCI(); + } + return status; + } + checkAuthor() { const { cli, commits, pr } = this; diff --git a/lib/queries/PR.gql b/lib/queries/PR.gql index 81b81911..27cc1396 100644 --- a/lib/queries/PR.gql +++ b/lib/queries/PR.gql @@ -18,6 +18,11 @@ query PR($prid: Int!, $owner: String!, $repo: String!) { name } }, + files(first: 100) { + nodes { + path + } + }, title, baseRefName, headRefName, diff --git a/lib/session.js b/lib/session.js index 4db0e3d9..9ba61a5b 100644 --- a/lib/session.js +++ b/lib/session.js @@ -99,7 +99,7 @@ class Session { } get ciType() { - return this.config.ciType || 'jenkins'; + return this.config.ciType || 'nodejs'; } get pullName() { diff --git a/test/fixtures/comments_with_failed_ci.json b/test/fixtures/comments_with_failed_ci.json new file mode 100644 index 00000000..ced2258e --- /dev/null +++ b/test/fixtures/comments_with_failed_ci.json @@ -0,0 +1,6 @@ +[ + { + "publishedAt": "2018-10-22T04:16:36.458Z", + "bodyText": "CI: https://ci.nodejs.org/job/node-test-pull-request/15442/" + } +] diff --git a/test/fixtures/comments_with_pending_ci.json b/test/fixtures/comments_with_pending_ci.json new file mode 100644 index 00000000..66c26a82 --- /dev/null +++ b/test/fixtures/comments_with_pending_ci.json @@ -0,0 +1,6 @@ +[ + { + "publishedAt": "2018-10-22T04:16:36.458Z", + "bodyText": "CI: https://ci.nodejs.org/job/node-test-pull-request/32777/" + } +] diff --git a/test/fixtures/data.js b/test/fixtures/data.js index 8c6f17ed..5cb8ae37 100644 --- a/test/fixtures/data.js +++ b/test/fixtures/data.js @@ -36,7 +36,9 @@ const approvingReviews = readJSON('reviews_approved.json'); const requestingChangesReviews = readJSON('reviews_requesting_changes.json'); const commentsWithCI = readJSON('comments_with_ci.json'); +const commentsWithFailedCI = readJSON('comments_with_failed_ci.json'); const commentsWithLGTM = readJSON('comments_with_lgtm.json'); +const commentsWithPendingCI = readJSON('comments_with_pending_ci.json'); const oddCommits = readJSON('odd_commits.json'); const incorrectGitConfigCommits = readJSON('incorrect_git_config_commits.json'); @@ -99,6 +101,18 @@ for (const item of readdirSync(path('./github-ci'))) { githubCI[basename(item, '.json')] = readJSON(`./github-ci/${item}`); }; +const jenkinsCI = {}; + +for (const subdir of readdirSync(path('./jenkins'))) { + for (const item of readdirSync(path(`./jenkins/${subdir}`))) { + if (!item.endsWith('.json')) { + continue; + } + jenkinsCI[`${subdir}/${basename(item, '.json')}`] = + readJSON(`./jenkins/${subdir}/${item}`); + } +}; + module.exports = { approved, requestedChanges, @@ -109,8 +123,11 @@ module.exports = { approvingReviews, requestingChangesReviews, commentsWithCI, + commentsWithFailedCI, commentsWithLGTM, + commentsWithPendingCI, oddCommits, + jenkinsCI, githubCI, incorrectGitConfigCommits, simpleCommits, diff --git a/test/fixtures/jenkins/pending/node-test-pull-request-32777.json b/test/fixtures/jenkins/pending/node-test-pull-request-32777.json new file mode 100644 index 00000000..1054fd58 --- /dev/null +++ b/test/fixtures/jenkins/pending/node-test-pull-request-32777.json @@ -0,0 +1,528 @@ +{ + "_class": "com.tikal.jenkins.plugins.multijob.MultiJobBuild", + "actions": [ + { + "_class": "hudson.model.ParametersAction", + "parameters": [ + { + "_class": "hudson.model.BooleanParameterValue", + "name": "CERTIFY_SAFE", + "value": true + }, + { + "_class": "hudson.model.StringParameterValue", + "name": "TARGET_GITHUB_ORG", + "value": "nodejs" + }, + { + "_class": "hudson.model.StringParameterValue", + "name": "TARGET_REPO_NAME", + "value": "node" + }, + { + "_class": "hudson.model.StringParameterValue", + "name": "PR_ID", + "value": "34761" + }, + { + "_class": "hudson.model.StringParameterValue", + "name": "REBASE_ONTO", + "value": "" + }, + { + "_class": "com.wangyin.parameter.WHideParameterValue", + "name": "DESCRIPTION_SETTER_DESCRIPTION", + "value": "\"\"" + } + ] + }, + { + "_class": "hudson.model.CauseAction", + "causes": [ + { + "_class": "hudson.model.Cause$UserIdCause", + "shortDescription": "Started by user mary marchini", + "userId": "mmarchini", + "userName": "mary marchini" + } + ] + }, + {}, + {}, + {}, + {}, + { + "_class": "hudson.plugins.git.util.BuildData", + "buildsByBranchName": { + "refs/remotes/origin/_jenkins_local_branch": { + "_class": "hudson.plugins.git.util.Build", + "buildNumber": 32777, + "buildResult": null, + "marked": { + "SHA1": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "branch": [ + { + "SHA1": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "name": "refs/remotes/origin/_jenkins_local_branch" + } + ] + }, + "revision": { + "SHA1": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "branch": [ + { + "SHA1": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "name": "refs/remotes/origin/_jenkins_local_branch" + } + ] + } + } + }, + "lastBuiltRevision": { + "SHA1": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "branch": [ + { + "SHA1": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "name": "refs/remotes/origin/_jenkins_local_branch" + } + ] + }, + "remoteUrls": [ + "git@github.com:$TARGET_GITHUB_ORG/$TARGET_REPO_NAME.git" + ], + "scmName": "" + }, + { + "_class": "hudson.plugins.git.GitTagAction" + }, + {}, + {}, + {}, + {}, + { + "_class": "hudson.model.ParametersAction", + "parameters": [ + { + "_class": "hudson.model.StringParameterValue", + "name": "DESCRIPTION_SETTER_DESCRIPTION", + "value": "Testing 34761" + } + ] + }, + { + "_class": "hudson.plugins.parameterizedtrigger.BuildInfoExporterAction" + }, + { + "_class": "com.tikal.jenkins.plugins.multijob.MultiJobTestResults", + "failCount": 0, + "skipCount": 0, + "totalCount": 0, + "urlName": "testReport" + }, + {}, + {}, + {}, + {} + ], + "artifacts": [], + "building": true, + "description": "Testing 34761", + "displayName": "#32777", + "duration": 0, + "estimatedDuration": 1565270, + "executor": {}, + "fullDisplayName": "node-test-pull-request #32777", + "id": "32777", + "keepLog": false, + "number": 32777, + "queueId": 936970, + "result": null, + "timestamp": 1597377490175, + "url": "https://ci.nodejs.org/job/node-test-pull-request/32777/", + "builtOn": "test-ibm-ubuntu1804-x64-1", + "changeSet": { + "_class": "hudson.plugins.git.GitChangeSetList", + "items": [ + { + "_class": "hudson.plugins.git.GitChangeSet", + "affectedPaths": [ + "test/fuzzers/fuzz_url.cc", + "configure.py", + "node.gyp" + ], + "commitId": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "timestamp": 1597352807000, + "author": { + "absoluteUrl": "https://ci.nodejs.org/user/david", + "fullName": "david" + }, + "authorEmail": "david@adalogics.com", + "comment": "Resolved suggestions for improvements in oss-fuzz infrastructure.\n", + "date": "2020-08-13 22:06:47 +0100", + "id": "fd987b66ed783f78aff1785eecfc1b0b1d92075f", + "msg": "Resolved suggestions for improvements in oss-fuzz infrastructure.", + "paths": [ + { + "editType": "edit", + "file": "configure.py" + }, + { + "editType": "edit", + "file": "test/fuzzers/fuzz_url.cc" + }, + { + "editType": "edit", + "file": "node.gyp" + } + ] + } + ], + "kind": "git" + }, + "culprits": [ + { + "absoluteUrl": "https://ci.nodejs.org/user/david", + "fullName": "david" + }, + { + "absoluteUrl": "https://ci.nodejs.org/user/oyyd", + "fullName": "Ouyang Yadong" + }, + { + "absoluteUrl": "https://ci.nodejs.org/user/trott", + "fullName": "Rich Trott" + } + ], + "subBuilds": [ + { + "abort": false, + "build": { + "_class": "com.tikal.jenkins.plugins.multijob.MultiJobBuild", + "subBuilds": [ + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 34989, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-freebsd", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-freebsd/34989/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 35763, + "duration": "24 sec and counting", + "icon": "red_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-osx", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-osx/35763/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 36592, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-linux", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-linux/36592/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 34386, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-smartos", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-smartos/34386/" + }, + { + "abort": false, + "build": { + "_class": "hudson.model.FreeStyleBuild" + }, + "buildNumber": 36264, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-linter", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-linter/36264/" + }, + { + "abort": false, + "build": { + "_class": "hudson.model.FreeStyleBuild" + }, + "buildNumber": 15923, + "duration": "25 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "test-worker", + "jobName": "node-test-commit-custom-suites-freestyle", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-custom-suites-freestyle/15923/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 34459, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-plinux", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-plinux/34459/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 21826, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-linux-containered", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-linux-containered/21826/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 32917, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-arm", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-arm/32917/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 22507, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-linuxone", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-linuxone/22507/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 32069, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-aix", + "multiJobBuild": false, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-aix/32069/" + }, + { + "abort": false, + "build": { + "_class": "com.tikal.jenkins.plugins.multijob.MultiJobBuild", + "subBuilds": [ + { + "abort": false, + "build": { + "_class": "hudson.model.FreeStyleBuild" + }, + "buildNumber": 68259, + "duration": "10 sec", + "icon": "blue.png", + "jobAlias": "", + "jobName": "git-rebase", + "multiJobBuild": false, + "parentBuildNumber": 37751, + "parentJobName": "node-test-commit-windows-fanned", + "phaseName": "Git rebase", + "result": "SUCCESS", + "retry": false, + "url": "job/git-rebase/68259/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 35529, + "duration": "9.9 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-compile-windows", + "multiJobBuild": false, + "parentBuildNumber": 37751, + "parentJobName": "node-test-commit-windows-fanned", + "phaseName": "Compilation", + "result": null, + "retry": false, + "url": "job/node-compile-windows/35529/" + }, + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 4173, + "duration": "10 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-compile-windows-debug", + "multiJobBuild": false, + "parentBuildNumber": 37751, + "parentJobName": "node-test-commit-windows-fanned", + "phaseName": "Compilation", + "result": null, + "retry": false, + "url": "job/node-compile-windows-debug/4173/" + } + ] + }, + "buildNumber": 37751, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-windows-fanned", + "multiJobBuild": true, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-windows-fanned/37751/" + }, + { + "abort": false, + "build": { + "_class": "com.tikal.jenkins.plugins.multijob.MultiJobBuild", + "subBuilds": [ + { + "abort": false, + "build": { + "_class": "hudson.matrix.MatrixBuild" + }, + "buildNumber": 30143, + "duration": "5 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-cross-compile", + "multiJobBuild": false, + "parentBuildNumber": 15899, + "parentJobName": "node-test-commit-arm-fanned", + "phaseName": "Cross Compilation", + "result": null, + "retry": false, + "url": "job/node-cross-compile/30143/" + } + ] + }, + "buildNumber": 15899, + "duration": "24 sec and counting", + "icon": "blue_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit-arm-fanned", + "multiJobBuild": true, + "parentBuildNumber": 40219, + "parentJobName": "node-test-commit", + "phaseName": "Tests", + "result": null, + "retry": false, + "url": "job/node-test-commit-arm-fanned/15899/" + } + ] + }, + "buildNumber": 40219, + "duration": "32 sec and counting", + "icon": "red_anime.gif", + "jobAlias": "", + "jobName": "node-test-commit", + "multiJobBuild": true, + "parentBuildNumber": 32777, + "parentJobName": "node-test-pull-request", + "phaseName": "", + "result": null, + "retry": false, + "url": "job/node-test-commit/40219/" + } + ] +} diff --git a/test/unit/pr_checker.test.js b/test/unit/pr_checker.test.js index 8fca0f21..180f2806 100644 --- a/test/unit/pr_checker.test.js +++ b/test/unit/pr_checker.test.js @@ -7,6 +7,8 @@ const TestCLI = require('../fixtures/test_cli'); const PRData = require('../../lib/pr_data'); const PRChecker = require('../../lib/pr_checker'); +const { jobCache } = require('../../lib/ci/build-types/job'); +jobCache.disable(); const GT_7D = '2018-11-23T17:50:44.477Z'; const LT_7D_GT_48H = '2018-11-27T17:50:44.477Z'; @@ -20,10 +22,13 @@ const { requestedChangesReviewers, approvingReviews, githubCI, + jenkinsCI, requestingChangesReviews, noReviewers, commentsWithCI, + commentsWithFailedCI, commentsWithLGTM, + commentsWithPendingCI, singleCommitAfterReview, multipleCommitsAfterReview, moreThanThreeCommitsAfterReview, @@ -614,12 +619,139 @@ describe('PRChecker', () => { }); describe('checkCI', () => { + it('should error if invalid CI', async() => { + const cli = new TestCLI(); + + const data = { + pr: firstTimerPR, + reviewers: allGreenReviewers, + comments: commentsWithLGTM, + reviews: approvingReviews, + commits: simpleCommits, + collaborators, + authorIsNew: () => true, + getThread() { + return PRData.prototype.getThread.call(this); + } + }; + const checker = new PRChecker( + cli, + data, + {}, + { ...argv, ciType: 'invalid' }); + + cli.clearCalls(); + const status = await checker.checkCI(); + assert(!status); + }); + + it('should error if pending CI', async() => { + const cli = new TestCLI(); + + const jenkins = jenkinsCI['pending/node-test-pull-request-32777']; + + const expectedLogs = { + error: [ + ['No GitHub CI runs detected'], + ['Last Jenkins CI still running'] + ], + info: [ + [`Last Full PR CI on 2018-10-22T04:16:36.458Z: ${jenkins.url}`] + ] + }; + + const data = { + pr: firstTimerPR, + reviewers: allGreenReviewers, + comments: commentsWithPendingCI, + reviews: approvingReviews, + commits: simpleCommits, + collaborators, + authorIsNew: () => true, + getThread() { + return PRData.prototype.getThread.call(this); + } + }; + const checker = new PRChecker( + cli, + data, + { + json: sinon.stub().callsFake(await function(url) { + if (!url.startsWith(jenkins.url)) { + // Throwing won't fail the checkCI call, but returning undefined + // will. + return undefined; + } + return jenkins; + }) + }, + argv); + + cli.clearCalls(); + const status = await checker.checkCI(); + assert(!status); + cli.assertCalledWith(expectedLogs, { + ignore: ['startSpinner', 'updateSpinner', 'stopSpinner'] + }); + }); + + it('should error if failed CI', async() => { + const cli = new TestCLI(); + + const jenkins = jenkinsCI['trigger-failure/node-test-pull-request-15442']; + + const expectedLogs = { + error: [ + ['No GitHub CI runs detected'], + ['1 failure(s) on the last Jenkins CI run'] + ], + info: [ + [`Last Full PR CI on 2018-10-22T04:16:36.458Z: ${jenkins.url}`] + ] + }; + + const data = { + pr: firstTimerPR, + reviewers: allGreenReviewers, + comments: commentsWithFailedCI, + reviews: approvingReviews, + commits: simpleCommits, + collaborators, + authorIsNew: () => true, + getThread() { + return PRData.prototype.getThread.call(this); + } + }; + const checker = new PRChecker( + cli, + data, + { + json: sinon.stub().callsFake(await function(url) { + if (!url.startsWith(jenkins.url)) { + // Throwing won't fail the checkCI call, but returning undefined + // will. + return undefined; + } + return jenkins; + }) + }, + argv); + + cli.clearCalls(); + const status = await checker.checkCI(); + assert(!status); + cli.assertCalledWith(expectedLogs, { + ignore: ['startSpinner', 'updateSpinner', 'stopSpinner'] + }); + }); + it('should error if no CI runs detected', async() => { const cli = new TestCLI(); const expectedLogs = { error: [ - ['No CI runs detected'] + ['No GitHub CI runs detected'], + ['No Jenkins CI runs detected'] ] }; @@ -646,6 +778,12 @@ describe('PRChecker', () => { const cli = new TestCLI(); const expectedLogs = { + ok: [ + ['Last Jenkins CI successful'] + ], + error: [ + ['No commits detected'] + ], info: [ [ 'Last Full PR CI on 2017-10-25T04:16:36.458Z: ' + @@ -693,7 +831,7 @@ describe('PRChecker', () => { const checker = new PRChecker(cli, data, {}, argv); const status = await checker.checkCI(); - assert(status); + assert(!status); cli.assertCalledWith(expectedLogs, { ignore: ['startSpinner', 'updateSpinner', 'stopSpinner'] }); @@ -712,6 +850,12 @@ describe('PRChecker', () => { ], info: [ ['Last Full PR CI on 2017-10-24T11:19:25Z: https://ci.nodejs.org/job/node-test-pull-request/10984/'] + ], + ok: [ + ['Last Jenkins CI successful'] + ], + error: [ + ['No GitHub CI runs detected'] ] }; @@ -754,7 +898,12 @@ describe('PRChecker', () => { 'https://ci.nodejs.org/job/node-test-pull-request/12984/' ] ], - error: [] + ok: [ + ['Last Jenkins CI successful'] + ], + error: [ + ['No GitHub CI runs detected'] + ] }; const checker = new PRChecker(cli, { @@ -792,7 +941,12 @@ describe('PRChecker', () => { 'https://ci.nodejs.org/job/node-test-pull-request/12984/' ] ], - error: [] + ok: [ + ['Last Jenkins CI successful'] + ], + error: [ + ['No GitHub CI runs detected'] + ] }; const checker = new PRChecker(cli, { @@ -835,7 +989,7 @@ describe('PRChecker', () => { const expectedLogs = { error: [ - ['No CI runs detected'] + ['No GitHub CI runs detected'] ] }; @@ -1044,7 +1198,7 @@ describe('PRChecker', () => { const expectedLogs = { error: [ - ['No CI runs detected'] + ['No GitHub CI runs detected'] ] };