Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add disallowScopes option #179

Merged
merged 10 commits into from May 4, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -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
jmosawy marked this conversation as resolved.
Show resolved Hide resolved
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]).+$
Expand Down
3 changes: 3 additions & 0 deletions action.yml
Expand Up @@ -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.'
jmosawy marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
6 changes: 6 additions & 0 deletions src/parseConfig.js
Expand Up @@ -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);
Expand Down Expand Up @@ -73,6 +78,7 @@ module.exports = function parseConfig() {
types,
scopes,
requireScope,
disallowScopes,
wip,
subjectPattern,
subjectPatternError,
Expand Down
15 changes: 15 additions & 0 deletions src/validatePrTitle.js
Expand Up @@ -11,6 +11,7 @@ module.exports = async function validatePrTitle(
types,
scopes,
requireScope,
disallowScopes,
subjectPattern,
subjectPatternError,
headerPattern,
Expand Down Expand Up @@ -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()}`
Expand Down Expand Up @@ -76,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(
Expand All @@ -89,6 +95,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(', ')}`
jmosawy marked this conversation as resolved.
Show resolved Hide resolved
);
}

function throwSubjectPatternError(message) {
if (subjectPatternError) {
message = formatMessage(subjectPatternError, {
Expand Down
62 changes: 62 additions & 0 deletions src/validatePrTitle.test.js
Expand Up @@ -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');
});
});
jmosawy marked this conversation as resolved.
Show resolved Hide resolved

describe('scope allowlist not defined', () => {
it('passes when a scope is provided', async () => {
await validatePrTitle('fix(core): Bar', {
Expand Down