From ef00a4df06979d412655e097bfa5b83a0764a5a3 Mon Sep 17 00:00:00 2001 From: Matheus Marchini Date: Tue, 1 Oct 2019 09:55:38 -0700 Subject: [PATCH] git-node: add config option to set wait times Add two new config options: `waitTimeSingleApproval` and `waitTimeMultiApproval`, which can be used to set how long ncu should wait before landing a PR with 1 approval or with 2+ approvals. --- docs/git-node.md | 28 ++++++++++ lib/pr_checker.js | 20 ++++++- lib/session.js | 10 ++++ test/fixtures/data.js | 6 ++ test/unit/pr_checker.test.js | 104 +++++++++++++++++++++++++++++++++++ 5 files changed, 165 insertions(+), 3 deletions(-) diff --git a/docs/git-node.md b/docs/git-node.md index 68bd6ffb..69b5b3c6 100644 --- a/docs/git-node.md +++ b/docs/git-node.md @@ -7,10 +7,12 @@ A custom Git command for managing pull requests. You can run it as - [Prerequisites](#git-node-land-prerequisites) - [Git bash for Windows](#git-bash-for-windows) - [Demo & Usage](#demo--usage) + - [Optional Settings](#git-node-land-optional-settings) - [`git node backport`](#git-node-backport) - [Example](#example) - [`git node sync`](#git-node-sync) - [`git node metadata`](#git-node-metadata) + - [Optional Settings](#git-node-metadata-optional-settings) - [`git node v8`](#git-node-v8) - [Prerequisites](#git-node-v8-prerequisites) - [`git node v8 major`](#git-node-v8-major) @@ -134,6 +136,14 @@ Options: --help Show help [boolean] ``` + + +### Optional Settings + +The same Settings used by +[`git node metadata`](#git-node-metadata-optional-settings) are also used by +`git node land`. + ## `git node backport` Demo: https://asciinema.org/a/221244 @@ -230,6 +240,24 @@ $ git commit --amend -F msg.txt git node metadata 167 --repo llnode --readme ../node/README.md ``` + + +### Optional Settings + +Some projects might not follow the same rules as nodejs/node. To properly +validate Pull Requests for these projects, node-core-utils accept the following +optional settings: + +```bash +cd path/to/project +# waitTimeSingleApproval is the minimum wait time (in hours) before +# landing a PR with only one approval. Default to 7 days. +ncu-config set waitTimeSingleApproval 168 +# waitTimeMultiApproval is the minimum wait time (in hours) before +# landing a PR with only two or more approvals. Default to 48 hours. +ncu-config set waitTimeMultiApproval 48 +``` + ## `git node v8` Update or patch the V8 engine. diff --git a/lib/pr_checker.js b/lib/pr_checker.js index 815c7961..ddeb127a 100644 --- a/lib/pr_checker.js +++ b/lib/pr_checker.js @@ -49,6 +49,20 @@ class PRChecker { ); } + get waitTimeSingleApproval() { + if (this.argv.waitTimeSingleApproval === undefined) { + return WAIT_TIME_SINGLE_APPROVAL; + } + return this.argv.waitTimeSingleApproval; + } + + get waitTimeMultiApproval() { + if (this.argv.waitTimeMultiApproval === undefined) { + return WAIT_TIME_MULTI_APPROVAL; + } + return this.argv.waitTimeMultiApproval; + } + checkAll(checkComments = false) { const status = [ this.checkCommitsAfterReview(), @@ -148,8 +162,8 @@ class PRChecker { const msFromCreateTime = now.getTime() - createTime.getTime(); const minutesFromCreateTime = Math.ceil(msFromCreateTime / MINUTE); const hoursFromCreateTime = Math.ceil(msFromCreateTime / HOUR); - let timeLeftMulti = WAIT_TIME_MULTI_APPROVAL - hoursFromCreateTime; - const timeLeftSingle = WAIT_TIME_SINGLE_APPROVAL - hoursFromCreateTime; + let timeLeftMulti = this.waitTimeMultiApproval - hoursFromCreateTime; + const timeLeftSingle = this.waitTimeSingleApproval - hoursFromCreateTime; if (approved.length >= 2) { if (isFastTracked || isCodeAndLearn) { @@ -160,7 +174,7 @@ class PRChecker { } if (timeLeftMulti === 0) { const timeLeftMins = - WAIT_TIME_MULTI_APPROVAL * 60 - minutesFromCreateTime; + this.waitTimeMultiApproval * 60 - minutesFromCreateTime; cli.error(`This PR needs to wait ${timeLeftMins} more minutes to land`); return false; } diff --git a/lib/session.js b/lib/session.js index 0d6d1dc8..a516a85b 100644 --- a/lib/session.js +++ b/lib/session.js @@ -50,6 +50,8 @@ class Session { upstream: this.upstream, branch: this.branch, readme: this.readme, + waitTimeSingleApproval: this.waitTimeSingleApproval, + waitTimeMultiApproval: this.waitTimeMultiApproval, prid: this.prid }; } @@ -78,6 +80,14 @@ class Session { return this.config.readme; } + get waitTimeSingleApproval() { + return this.config.waitTimeSingleApproval; + } + + get waitTimeMultiApproval() { + return this.config.waitTimeMultiApproval; + } + get pullName() { return `${this.owner}/${this.repo}/pulls/${this.prid}`; } diff --git a/test/fixtures/data.js b/test/fixtures/data.js index 678d85f1..3ca3e42f 100644 --- a/test/fixtures/data.js +++ b/test/fixtures/data.js @@ -24,6 +24,11 @@ const requestedChangesReviewers = { approved: [] }; +const noReviewers = { + requestedChanges: [], + approved: [] +}; + const approvingReviews = readJSON('reviews_approved.json'); const requestingChangesReviews = readJSON('reviews_requesting_changes.json'); @@ -87,6 +92,7 @@ module.exports = { requestedChanges, allGreenReviewers, singleGreenReviewer, + noReviewers, requestedChangesReviewers, approvingReviews, requestingChangesReviews, diff --git a/test/unit/pr_checker.test.js b/test/unit/pr_checker.test.js index b78618e8..595a18ac 100644 --- a/test/unit/pr_checker.test.js +++ b/test/unit/pr_checker.test.js @@ -20,6 +20,7 @@ const { requestedChangesReviewers, approvingReviews, requestingChangesReviews, + noReviewers, commentsWithCI, commentsWithLiteCI, commentsWithLGTM, @@ -217,6 +218,42 @@ describe('PRChecker', () => { cli.assertCalledWith(expectedLogs); }); + it('should succeed if waitTimeMultiApproval is set', () => { + const cli = new TestCLI(); + + const expectedLogs = { + ok: + [ [ 'Approvals: 4' ], + [ '- Foo User (@foo): https://github.com/nodejs/node/pull/16438#pullrequestreview-71480624' ], + [ '- Quux User (@Quux): LGTM' ], + [ '- Baz User (@Baz): https://github.com/nodejs/node/pull/16438#pullrequestreview-71488236' ], + [ '- Bar User (@bar) (TSC): lgtm' ] ], + info: [ [ 'This PR was created on Fri, 30 Nov 2018 17:50:44 GMT' ] ] + }; + + const youngPR = Object.assign({}, firstTimerPR, { + createdAt: LT_48H + }); + + const data = { + pr: youngPR, + reviewers: allGreenReviewers, + comments: commentsWithLGTM, + reviews: approvingReviews, + commits: simpleCommits, + collaborators, + authorIsNew: () => true, + getThread() { + return PRData.prototype.getThread.call(this); + } + }; + const checker = new PRChecker(cli, data, { waitTimeMultiApproval: 23 }); + + const status = checker.checkReviewsAndWait(new Date(NOW)); + assert(status); + cli.assertCalledWith(expectedLogs); + }); + it('should error when PR is younger than 48h and older than 47h', () => { const cli = new TestCLI(); @@ -326,6 +363,73 @@ describe('PRChecker', () => { cli.assertCalledWith(expectedLogs); }); + it('should succeed with 1 approval with waitTimeSingleApproval set', () => { + const cli = new TestCLI(); + + const expectedLogs = { + ok: + [ [ 'Approvals: 1' ], + [ '- Foo User (@foo): https://github.com/nodejs/node/pull/16438#pullrequestreview-71480624' ] ], + info: + [ [ 'This PR was created on Fri, 30 Nov 2018 17:50:44 GMT' ] ] + }; + + const youngPR = Object.assign({}, firstTimerPR, { + createdAt: LT_48H + }); + + const data = { + pr: youngPR, + reviewers: singleGreenReviewer, + comments: commentsWithLGTM, + reviews: approvingReviews, + commits: simpleCommits, + collaborators, + authorIsNew: () => true, + getThread() { + return PRData.prototype.getThread.call(this); + } + }; + const checker = new PRChecker(cli, data, { waitTimeSingleApproval: 0 }); + + const status = checker.checkReviewsAndWait(new Date(NOW)); + assert(status); + cli.assertCalledWith(expectedLogs); + }); + + it('should error with 0 approval and waitTimeSingleApproval=0', () => { + const cli = new TestCLI(); + + const expectedLogs = { + info: + [ [ 'This PR was created on Fri, 30 Nov 2018 17:50:44 GMT' ] ], + error: + [ [ 'Approvals: 0' ] ] + }; + + const youngPR = Object.assign({}, firstTimerPR, { + createdAt: LT_48H + }); + + const data = { + pr: youngPR, + reviewers: noReviewers, + comments: commentsWithLGTM, + reviews: approvingReviews, + commits: simpleCommits, + collaborators, + authorIsNew: () => true, + getThread() { + return PRData.prototype.getThread.call(this); + } + }; + const checker = new PRChecker(cli, data, { waitTimeSingleApproval: 0 }); + + const status = checker.checkReviewsAndWait(new Date(NOW)); + assert(!status); + cli.assertCalledWith(expectedLogs); + }); + it('should log as expected if PR can be fast-tracked', () => { const cli = new TestCLI();