From a43040026c495f5ab39d4a28036dc5a16acdf5b1 Mon Sep 17 00:00:00 2001 From: "Abid H. Mujtaba" Date: Thu, 2 Dec 2021 11:13:12 -0500 Subject: [PATCH] feat: Add more supported events to baseRef validator (#598) - The auto-merge workflow (using the `merge` action) listens to `check_suite.*` events in addition to the more standard `pull-request.*` and `pull_request_review.*`. This workflow relies on branch protections to stop the merge while the check_suite is running. Since branch protections are usually setup **only** only on the default branch it means PRs that are targetting non-default branches are auto-merged by the workflow sometime before the check suite has even completed (if `check_suite.*` is used). - Add `check_suite.*` event support to the baseRef validator so that auto-merge can be limited to the default branch. - If more than one pull request is associated with a check_suite the validator will validate ALL of them and only pass if ALL of them pass - If NO pull requests are associated with a check_suite the validator considers this to be a failure since there is no baseRef to validate - Add unit tests for check_suite events. --- __tests__/unit/validators/baseRef.test.js | 98 +++++++++++++++++++++++ docs/changelog.rst | 1 + docs/validators/baseRef.rst | 2 +- lib/validators/baseRef.js | 35 ++++++-- 4 files changed, 130 insertions(+), 6 deletions(-) diff --git a/__tests__/unit/validators/baseRef.test.js b/__tests__/unit/validators/baseRef.test.js index f4b12815..ae15ed69 100644 --- a/__tests__/unit/validators/baseRef.test.js +++ b/__tests__/unit/validators/baseRef.test.js @@ -35,6 +35,34 @@ test('checks that it fail when exclude regex is in baseRef', async () => { expect(baseRefValidation.status).toBe('fail') }) +test('checks that it passes when exclude regex is not in baseRef', async () => { + const baseRef = new BaseRef() + + const settings = { + do: 'baseRef', + must_exclude: { + regex: 'wip' + } + } + + const baseRefValidation = await baseRef.processValidate(mockContext('foo'), settings) + expect(baseRefValidation.status).toBe('pass') +}) + +test('checks that it passes when include regex is in baseRef', async () => { + const baseRef = new BaseRef() + + const settings = { + do: 'baseRef', + must_include: { + regex: '^\\(feat\\)|^\\(doc\\)|^\\(fix\\)' + } + } + + const baseRefValidation = await baseRef.processValidate(mockContext('(feat) foo'), settings) + expect(baseRefValidation.status).toBe('pass') +}) + test('checks that advance setting of must_include works', async () => { const baseRef = new BaseRef() @@ -61,7 +89,77 @@ test('checks that advance setting of must_include works', async () => { expect(baseRefValidation.status).toBe('fail') }) +test('fail when exclude regex is in baseRef of single check_suite pull request', async () => { + const baseRef = new BaseRef() + + const settings = { + do: 'baseRef', + must_exclude: { + regex: 'wip' + } + } + + const context = mockCheckSuiteContext(['WIP foo']) + + const baseRefValidation = await baseRef.processValidate(context, settings) + expect(baseRefValidation.status).toBe('fail') +}) + +test('fail when exclude regex is in one baseRef of multiple check_suite pull requests', async () => { + const baseRef = new BaseRef() + + const settings = { + do: 'baseRef', + must_exclude: { + regex: 'wip' + } + } + + const context = mockCheckSuiteContext(['foo', 'WIP bar', 'baz']) + + const baseRefValidation = await baseRef.processValidate(context, settings) + expect(baseRefValidation.status).toBe('fail') +}) + +test('pass when exclude regex is not in any baseRef of multiple check_suite pull requests', async () => { + const baseRef = new BaseRef() + + const settings = { + do: 'baseRef', + must_exclude: { + regex: 'wip' + } + } + + const context = mockCheckSuiteContext(['foo', 'bar', 'baz']) + + const baseRefValidation = await baseRef.processValidate(context, settings) + expect(baseRefValidation.status).toBe('pass') +}) + +test('fail when include regex exists and there are no pull requests in check_suite', async () => { + const baseRef = new BaseRef() + + const settings = { + do: 'baseRef', + must_include: { + regex: 'foo' + } + } + + const context = mockCheckSuiteContext([]) + + const baseRefValidation = await baseRef.processValidate(context, settings) + expect(baseRefValidation.status).toBe('fail') +}) + const mockContext = baseRef => { const context = Helper.mockContext({ baseRef: baseRef }) return context } + +const mockCheckSuiteContext = baseRefs => { + const context = Helper.mockContext({ eventName: 'check_suite' }) + context.payload.check_suite.pull_requests = baseRefs.map(baseRef => ({ base: { ref: baseRef } })) + return context +} diff --git a/docs/changelog.rst b/docs/changelog.rst index e1420bb9..c0946123 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,5 +1,6 @@ CHANGELOG ===================================== +| November 25, 2021: feat: Add more supported events to baseRef validator `#395 ` _ | November 12, 2021 : feat: Add baseRef filter `#596 `_ | October 19, 2021 : feat: Add validator approval option to exclude users `#594 `_ | October 12, 2021 : feat: Add boolean option for payload filter `#583 `_ diff --git a/docs/validators/baseRef.rst b/docs/validators/baseRef.rst index c54f5f5c..7a674baa 100644 --- a/docs/validators/baseRef.rst +++ b/docs/validators/baseRef.rst @@ -26,4 +26,4 @@ Simple example: Supported Events: :: - 'pull_request.*', 'pull_request_review.*' + 'pull_request.*', 'pull_request_review.*', 'check_suite.*' diff --git a/lib/validators/baseRef.js b/lib/validators/baseRef.js index 0797fdb2..f88c0785 100644 --- a/lib/validators/baseRef.js +++ b/lib/validators/baseRef.js @@ -1,11 +1,14 @@ const { Validator } = require('./validator') +const consolidateResults = require('./options_processor/options/lib/consolidateResults') +const constructOutput = require('./options_processor/options/lib/constructOutput') class BaseRef extends Validator { constructor () { super('baseRef') this.supportedEvents = [ 'pull_request.*', - 'pull_request_review.*' + 'pull_request_review.*', + 'check_suite.*' ] this.supportedSettings = { must_include: { @@ -22,10 +25,32 @@ class BaseRef extends Validator { } async validate (context, validationSettings) { - return this.processOptions( - validationSettings, - this.getPayload(context).base.ref - ) + const payload = this.getPayload(context) + + if (context.eventName === 'check_suite') { + return this.validateCheckSuite(payload, validationSettings) + } + + return this.processOptions(validationSettings, payload.base.ref) + } + + async validateCheckSuite (payload, validationSettings) { + // A check_suite's payload contains multiple pull_requests + // Need to make sure that each pull_request's base ref is valid + const validatorContext = { name: 'baseRef' } + const baseRefs = payload.pull_requests.map(pullRequest => pullRequest.base.ref) + + // If a check_suite has NO associated pull requests it is considered + // a failed validation since there is no baseRef to validate + if (baseRefs.length === 0) { + return constructOutput({ name: 'baseRef' }, undefined, validationSettings, { status: 'fail', description: 'No pull requests associated with check_suite' }) + } + + const results = await Promise.all(baseRefs.map( + baseRef => this.processOptions(validationSettings, baseRef) + )) + + return consolidateResults(results, validatorContext) } }