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 subjectPatternError option to set custom sub validation error #76

Merged
merged 9 commits into from
Jan 18, 2021
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ jobs:
# 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]).+$
# Configure custom error message that should be passed to the logs to better describe the error thrown by subject pattern validation.
# You can pass template values to the error test
# - ${subject} will be replaced with the subject of the PR title
# - ${title} will be replaced with the PR title
# This example will return the following error of PR title "fix(core): Test":
# The subject "Test" found in the pull request title "fix(core): Test" didn't match the configured pattern.
# Please ensure that the subject doesn't start with an uppercase character.
subjectPatternError: |
The subject "${subject}" found in the pull request title "${title}" didn't match the configured pattern.
Please ensure that the subject doesn't start with an uppercase character.
derberg marked this conversation as resolved.
Show resolved Hide resolved
# For work-in-progress PRs you can typically use draft pull requests
# from Github. However, private repositories on the free plan don't have
# this option and therefore this action allows you to opt-in to using the
Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ inputs:
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
subjectPatternError:
description: "Configure custom error message that should be passed to the logs to better describe the error thrown by subject pattern validation."
derberg marked this conversation as resolved.
Show resolved Hide resolved
required: false
wip:
description: "For work-in-progress PRs you can typically use draft pull requests from Github. However, private repositories on the free plan don't have this option and therefore this action allows you to opt-in to using the special '[WIP]' prefix to indicate this state. This will avoid the validation of the PR title and the pull request checks remain pending. Note that a second check will be reported if this is enabled."
required: false
2 changes: 2 additions & 0 deletions src/SubjectPatternErrorParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module.exports = (subjectPatternError, subject, title) =>
subjectPatternError.replace('${subject}', subject).replace('${title}', title);
derberg marked this conversation as resolved.
Show resolved Hide resolved
derberg marked this conversation as resolved.
Show resolved Hide resolved
24 changes: 24 additions & 0 deletions src/SubjectPatternErrorParser.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const subjectPatternErrorParser = require('./SubjectPatternErrorParser');

describe('parse pattern error', () => {
it('parse string without changes if template values not provided', () => {
const customError = 'this is test';
expect(subjectPatternErrorParser(customError)).toEqual(customError);
});

it('parses string properly by replacing one template value', () => {
expect(
subjectPatternErrorParser('this is ${subject} test', 'my subject')
).toEqual('this is my subject test');
});

it('parses string properly by replacing both template values', () => {
expect(
subjectPatternErrorParser(
'this ${title} is ${subject} test',
'my subject',
'my title'
)
).toEqual('this my title is my subject test');
});
});
12 changes: 10 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ const validatePrTitle = require('./validatePrTitle');
module.exports = async function run() {
try {
const client = github.getOctokit(process.env.GITHUB_TOKEN);
const {types, scopes, requireScope, wip, subjectPattern} = parseConfig();
const {
types,
scopes,
requireScope,
wip,
subjectPattern,
subjectPatternError
} = parseConfig();

const contextPullRequest = github.context.payload.pull_request;
if (!contextPullRequest) {
Expand Down Expand Up @@ -38,7 +45,8 @@ module.exports = async function run() {
types,
scopes,
requireScope,
subjectPattern
subjectPattern,
subjectPatternError
});
} catch (error) {
validationError = error;
Expand Down
16 changes: 15 additions & 1 deletion src/parseConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,24 @@ module.exports = function parseConfig() {
subjectPattern = ConfigParser.parseString(process.env.INPUT_SUBJECTPATTERN);
}

let subjectPatternError;
if (process.env.INPUT_SUBJECTPATTERNERROR) {
subjectPatternError = ConfigParser.parseString(
process.env.INPUT_SUBJECTPATTERNERROR
);
}

let wip;
if (process.env.INPUT_WIP) {
wip = ConfigParser.parseBoolean(process.env.INPUT_WIP);
}

return {types, scopes, requireScope, wip, subjectPattern};
return {
types,
scopes,
requireScope,
wip,
subjectPattern,
subjectPatternError
};
};
9 changes: 8 additions & 1 deletion src/validatePrTitle.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const conventionalCommitsConfig = require('conventional-changelog-conventionalcommits');
const conventionalCommitTypes = require('conventional-commit-types');
const parser = require('conventional-commits-parser').sync;
const subjectPatternErrorParser = require('./SubjectPatternErrorParser');

const defaultTypes = Object.keys(conventionalCommitTypes.types);

module.exports = async function validatePrTitle(
prTitle,
{types, scopes, requireScope, subjectPattern} = {}
{types, scopes, requireScope, subjectPattern, subjectPatternError} = {}
) {
if (!types) types = defaultTypes;

Expand Down Expand Up @@ -66,6 +67,12 @@ module.exports = async function validatePrTitle(
if (subjectPattern) {
const match = result.subject.match(new RegExp(subjectPattern));

if (subjectPatternError) {
throw new Error(
subjectPatternErrorParser(subjectPatternError, result.subject, prTitle)
);
}

if (!match) {
throw new Error(
`The subject "${result.subject}" found in pull request title "${prTitle}" doesn't match the configured pattern "${subjectPattern}".`
Expand Down
22 changes: 22 additions & 0 deletions src/validatePrTitle.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,28 @@ describe('description validation', () => {
it('does not validate the description by default', async () => {
await validatePrTitle('fix: sK!"§4123');
});
it('throws custom error', async () => {
derberg marked this conversation as resolved.
Show resolved Hide resolved
const customError =
'The subject found in the pull request title cannot start with an uppercase character.';
await expect(
validatePrTitle('fix: Foobar', {
subjectPattern: '^(?![A-Z]).+$',
subjectPatternError: customError
})
).rejects.toThrow(customError);
});

it('throws custom error enriched with template values', async () => {
derberg marked this conversation as resolved.
Show resolved Hide resolved
await expect(
validatePrTitle('fix: Foobar', {
subjectPattern: '^(?![A-Z]).+$',
subjectPatternError:
'The subject "${subject}" found in the pull request title "${title}" cannot start with an uppercase character.'
})
).rejects.toThrow(
'The subject "Foobar" found in the pull request title "fix: Foobar" cannot start with an uppercase character.'
);
});

it('throws for invalid subjects', async () => {
await expect(
Expand Down