From d1960becba651082c982e139dd776892c048d312 Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Fri, 29 Apr 2022 01:55:35 +0200 Subject: [PATCH 01/10] feat: add disallowScopes option --- action.yml | 3 ++ src/parseConfig.js | 6 ++++ src/validatePrTitle.js | 14 +++++++++ src/validatePrTitle.test.js | 62 +++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/action.yml b/action.yml index 8f9d8a130..0763ddc03 100644 --- a/action.yml +++ b/action.yml @@ -17,6 +17,9 @@ inputs: requireScope: description: "Configure that a scope must always be provided." required: false + disallowScopes: + description: 'Configure a list of disallowed scopes to be used on a PR.' + required: false subjectPattern: description: "Configure additional validation for the subject based on a regex. E.g. '^(?![A-Z]).+$' ensures the subject doesn't start with an uppercase character." required: false diff --git a/src/parseConfig.js b/src/parseConfig.js index a3f8f93f0..bbfa85aa4 100644 --- a/src/parseConfig.js +++ b/src/parseConfig.js @@ -16,6 +16,11 @@ module.exports = function parseConfig() { requireScope = ConfigParser.parseBoolean(process.env.INPUT_REQUIRESCOPE); } + let disallowScopes; + if (process.env.INPUT_DISALLOWSCOPES) { + disallowScopes = ConfigParser.parseEnum(process.env.INPUT_DISALLOWSCOPES); + } + let subjectPattern; if (process.env.INPUT_SUBJECTPATTERN) { subjectPattern = ConfigParser.parseString(process.env.INPUT_SUBJECTPATTERN); @@ -73,6 +78,7 @@ module.exports = function parseConfig() { types, scopes, requireScope, + disallowScopes, wip, subjectPattern, subjectPatternError, diff --git a/src/validatePrTitle.js b/src/validatePrTitle.js index b87f28dfd..7c6853620 100644 --- a/src/validatePrTitle.js +++ b/src/validatePrTitle.js @@ -11,6 +11,7 @@ module.exports = async function validatePrTitle( types, scopes, requireScope, + disallowScopes, subjectPattern, subjectPatternError, headerPattern, @@ -46,6 +47,10 @@ module.exports = async function validatePrTitle( return scopes && !scopes.includes(s); } + function isDisallowedScope(s) { + return disallowScopes && !disallowScopes.includes(s); + } + if (!result.type) { throw new Error( `No release type found in pull request title "${prTitle}". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/\n\n${printAvailableTypes()}` @@ -89,6 +94,15 @@ module.exports = async function validatePrTitle( ); } + const disallowedScopes = givenScopes + ? givenScopes.filter(isDisallowedScope) + : []; + if (disallowScopes && disallowedScopes.length > 0) { + throw new Error( + `Disallowed scope(s) are found: ${disallowScopes.join(', ')}` + ); + } + function throwSubjectPatternError(message) { if (subjectPatternError) { message = formatMessage(subjectPatternError, { diff --git a/src/validatePrTitle.test.js b/src/validatePrTitle.test.js index 073a85ef3..3333c365d 100644 --- a/src/validatePrTitle.test.js +++ b/src/validatePrTitle.test.js @@ -98,6 +98,68 @@ describe('defined scopes', () => { }); }); + describe('disallow scopes', () => { + it('passes when a single scope is provided, but not present in disallowScopes with one item', async () => { + await validatePrTitle('fix(core): Bar', {disallowScopes: ['release']}); + }); + + it('passes when multiple scopes are provided, but not present in disallowScopes with one item', async () => { + await validatePrTitle('fix(core,e2e,bar): Bar', { + disallowScopes: ['release'] + }); + }); + + it('passes when a single scope is provided, but not present in disallowScopes with multiple items', async () => { + await validatePrTitle('fix(core): Bar', { + disallowScopes: ['release', 'test'] + }); + }); + + it('passes when multiple scopes are provided, but not present in disallowScopes with multiple items', async () => { + await validatePrTitle('fix(core,e2e,bar): Bar', { + disallowScopes: ['release', 'test'] + }); + }); + + it('throws when a single scope is provided and it is present in disallowScopes with one item', async () => { + await expect( + validatePrTitle('fix(release): Bar', {disallowScopes: ['release']}) + ).rejects.toThrow('Disallowed scope(s) are found: release'); + }); + + it('throws when a single scope is provided and it is present in disallowScopes with multiple item', async () => { + await expect( + validatePrTitle('fix(release): Bar', { + disallowScopes: ['release', 'test'] + }) + ).rejects.toThrow('Disallowed scope(s) are found: release'); + }); + + it('throws when multiple scopes are provided and one of them is present in disallowScopes with one item ', async () => { + await expect( + validatePrTitle('fix(release,e2e): Bar', { + disallowScopes: ['release'] + }) + ).rejects.toThrow('Disallowed scope(s) are found: release'); + }); + + it('throws when multiple scopes are provided and one of them is present in disallowScopes with multiple items ', async () => { + await expect( + validatePrTitle('fix(release,e2e): Bar', { + disallowScopes: ['release', 'test'] + }) + ).rejects.toThrow('Disallowed scope(s) are found: release'); + }); + + it('throws when multiple scopes are provided and more than one of them are present in disallowScopes', async () => { + await expect( + validatePrTitle('fix(release,test): Bar', { + disallowScopes: ['release', 'test'] + }) + ).rejects.toThrow('Disallowed scope(s) are found: release, test'); + }); + }); + describe('scope allowlist not defined', () => { it('passes when a scope is provided', async () => { await validatePrTitle('fix(core): Bar', { From 154a3e4d2d64a02576ca3b4dee648687fe0ef710 Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Fri, 29 Apr 2022 02:10:09 +0200 Subject: [PATCH 02/10] fix: change isDisallowedScope condition --- src/validatePrTitle.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/validatePrTitle.js b/src/validatePrTitle.js index 7c6853620..3e850ad04 100644 --- a/src/validatePrTitle.js +++ b/src/validatePrTitle.js @@ -48,7 +48,7 @@ module.exports = async function validatePrTitle( } function isDisallowedScope(s) { - return disallowScopes && !disallowScopes.includes(s); + return disallowScopes && disallowScopes.includes(s); } if (!result.type) { @@ -81,6 +81,7 @@ module.exports = async function validatePrTitle( const givenScopes = result.scope ? result.scope.split(',').map((scope) => scope.trim()) : undefined; + const unknownScopes = givenScopes ? givenScopes.filter(isUnknownScope) : []; if (scopes && unknownScopes.length > 0) { throw new Error( From 38bf478e010eb8d69a8d0ee38609181997c57859 Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Fri, 29 Apr 2022 02:26:02 +0200 Subject: [PATCH 03/10] docs: add disallowScopes to README file --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 320df8739..2f8ad216a 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,10 @@ The action works without configuration, however you can provide options for cust ui # Configure that a scope must always be provided. requireScope: true + # Configure which scopes are disallowed in a PR title. + # For instance, by setting the value below, `chore(release): ...` or `ci(e2e,release): ...` will be rejected + disallowScopes: | + release # Configure additional validation for the subject based on a regex. # This example ensures the subject doesn't start with an uppercase character. subjectPattern: ^(?![A-Z]).+$ From e748b0c07d55fe9fe553328220c1c21410240a4e Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Tue, 3 May 2022 23:48:28 +0200 Subject: [PATCH 04/10] Update README.md Co-authored-by: Jan Amann --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f8ad216a..d775cdc95 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ The action works without configuration, however you can provide options for cust ui # Configure that a scope must always be provided. requireScope: true - # Configure which scopes are disallowed in a PR title. - # For instance, by setting the value below, `chore(release): ...` or `ci(e2e,release): ...` will be rejected + # Configure which scopes are disallowed in PR titles. For instance, by setting + # the value below, `chore(release): ...` or `ci(e2e,release): ...` will be rejected. disallowScopes: | release # Configure additional validation for the subject based on a regex. From e0f282eb9a91636a2c97a1cd66a7a66df18eb588 Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Tue, 3 May 2022 23:48:36 +0200 Subject: [PATCH 05/10] Update action.yml Co-authored-by: Jan Amann --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 0763ddc03..ab3323ca4 100644 --- a/action.yml +++ b/action.yml @@ -18,7 +18,7 @@ inputs: description: "Configure that a scope must always be provided." required: false disallowScopes: - description: 'Configure a list of disallowed scopes to be used on a PR.' + description: 'Configure which scopes are disallowed in PR titles.' required: false subjectPattern: description: "Configure additional validation for the subject based on a regex. E.g. '^(?![A-Z]).+$' ensures the subject doesn't start with an uppercase character." From daff439fb7799591ed6a5c5809c18fbc772f35ab Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Tue, 3 May 2022 23:48:47 +0200 Subject: [PATCH 06/10] Update src/validatePrTitle.js Co-authored-by: Jan Amann --- src/validatePrTitle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validatePrTitle.js b/src/validatePrTitle.js index 3e850ad04..19c208481 100644 --- a/src/validatePrTitle.js +++ b/src/validatePrTitle.js @@ -100,7 +100,7 @@ module.exports = async function validatePrTitle( : []; if (disallowScopes && disallowedScopes.length > 0) { throw new Error( - `Disallowed scope(s) are found: ${disallowScopes.join(', ')}` + `Disallowed ${disallowedScopes.length === 1 ? 'scope was' : 'scopes were'} found: ${disallowScopes.join(', ')}` ); } From 4f608293ec7039c15e76ac44808241225df24477 Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Tue, 3 May 2022 23:59:20 +0200 Subject: [PATCH 07/10] test: update test expected messages --- src/validatePrTitle.js | 4 +++- src/validatePrTitle.test.js | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/validatePrTitle.js b/src/validatePrTitle.js index 19c208481..f3369134b 100644 --- a/src/validatePrTitle.js +++ b/src/validatePrTitle.js @@ -100,7 +100,9 @@ module.exports = async function validatePrTitle( : []; if (disallowScopes && disallowedScopes.length > 0) { throw new Error( - `Disallowed ${disallowedScopes.length === 1 ? 'scope was' : 'scopes were'} found: ${disallowScopes.join(', ')}` + `Disallowed ${ + disallowedScopes.length === 1 ? 'scope was' : 'scopes were' + } found: ${disallowScopes.join(', ')}` ); } diff --git a/src/validatePrTitle.test.js b/src/validatePrTitle.test.js index 3333c365d..207eae04a 100644 --- a/src/validatePrTitle.test.js +++ b/src/validatePrTitle.test.js @@ -124,7 +124,7 @@ describe('defined scopes', () => { it('throws when a single scope is provided and it is present in disallowScopes with one item', async () => { await expect( validatePrTitle('fix(release): Bar', {disallowScopes: ['release']}) - ).rejects.toThrow('Disallowed scope(s) are found: release'); + ).rejects.toThrow('Disallowed scope was found: release'); }); it('throws when a single scope is provided and it is present in disallowScopes with multiple item', async () => { @@ -132,7 +132,7 @@ describe('defined scopes', () => { validatePrTitle('fix(release): Bar', { disallowScopes: ['release', 'test'] }) - ).rejects.toThrow('Disallowed scope(s) are found: release'); + ).rejects.toThrow('Disallowed scope was found: release'); }); it('throws when multiple scopes are provided and one of them is present in disallowScopes with one item ', async () => { @@ -140,7 +140,7 @@ describe('defined scopes', () => { validatePrTitle('fix(release,e2e): Bar', { disallowScopes: ['release'] }) - ).rejects.toThrow('Disallowed scope(s) are found: release'); + ).rejects.toThrow('Disallowed scope was found: release'); }); it('throws when multiple scopes are provided and one of them is present in disallowScopes with multiple items ', async () => { @@ -148,7 +148,7 @@ describe('defined scopes', () => { validatePrTitle('fix(release,e2e): Bar', { disallowScopes: ['release', 'test'] }) - ).rejects.toThrow('Disallowed scope(s) are found: release'); + ).rejects.toThrow('Disallowed scope was found: release'); }); it('throws when multiple scopes are provided and more than one of them are present in disallowScopes', async () => { @@ -156,7 +156,7 @@ describe('defined scopes', () => { validatePrTitle('fix(release,test): Bar', { disallowScopes: ['release', 'test'] }) - ).rejects.toThrow('Disallowed scope(s) are found: release, test'); + ).rejects.toThrow('Disallowed scopes were found: release, test'); }); }); From 6dd0594ce88055f3160e53eca6c5a02edf8638ee Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Wed, 4 May 2022 00:04:10 +0200 Subject: [PATCH 08/10] ci: test disallowScopes prop --- .github/workflows/lint-pr-title-preview.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-pr-title-preview.yml b/.github/workflows/lint-pr-title-preview.yml index e61a4f73a..2507dbdec 100644 --- a/.github/workflows/lint-pr-title-preview.yml +++ b/.github/workflows/lint-pr-title-preview.yml @@ -1,4 +1,4 @@ -name: 'Lint PR title preview (current branch)' +name: "Lint PR title preview (current branch)" on: pull_request: types: @@ -19,3 +19,6 @@ jobs: - uses: ./ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + disallowScopes: | + release From 7c52fb607f19bd8fe6c7c7979e75e9ed443b2579 Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Wed, 4 May 2022 00:13:45 +0200 Subject: [PATCH 09/10] fix: pass disallowScopes --- src/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.js b/src/index.js index 763bce938..4fb796a82 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ module.exports = async function run() { types, scopes, requireScope, + disallowScopes, wip, subjectPattern, subjectPatternError, @@ -67,6 +68,7 @@ module.exports = async function run() { types, scopes, requireScope, + disallowScopes, subjectPattern, subjectPatternError, headerPattern, @@ -108,6 +110,7 @@ module.exports = async function run() { types, scopes, requireScope, + disallowScopes, subjectPattern, subjectPatternError, headerPattern, From a3f5e191189ea2ad68c01637422da4443037cabe Mon Sep 17 00:00:00 2001 From: Jeff Mosawy Date: Wed, 4 May 2022 00:15:22 +0200 Subject: [PATCH 10/10] ci: revert test disallowScopes prop --- .github/workflows/lint-pr-title-preview.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/lint-pr-title-preview.yml b/.github/workflows/lint-pr-title-preview.yml index 2507dbdec..c09563d2c 100644 --- a/.github/workflows/lint-pr-title-preview.yml +++ b/.github/workflows/lint-pr-title-preview.yml @@ -19,6 +19,3 @@ jobs: - uses: ./ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - disallowScopes: | - release