Skip to content

Commit 56bf990

Browse files
committed
Adding support for two new 'When' values: 'always-in-non-wips' and 'never-in-non-wips' to allow rules to be configured for just non-WIPs.
This new functionality currently cannot be used since commitlint prevents custom 'When' values in its top-level linter, but the work had already been done so checking this in in case in future this changes. I created a GitHub Issue asking about that possibility: conventional-changelog/commitlint#1003.
1 parent 32a0f68 commit 56bf990

21 files changed

+746
-213
lines changed

packages/commitlint-config-github/src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ const supportedTypes = [
2222

2323
export const commitlintConfigGitHub: CommitlintConfigGitHub = {
2424
rules: {
25-
[commitlintGitHubRules.issueNumberMissing]: [2, When.NEVER],
25+
[commitlintGitHubRules.issueNumberMissing]: [2, When.NON_WIPS_NEVER],
2626
[commitlintGitHubRules.issueNumberFormat]: [2, When.ALWAYS],
2727
[commitlintGitHubRules.issueNumberDuplicate]: [2, When.NEVER],
2828

2929
[commitlintGitHubRules.wipAllowed]: [2, When.ALWAYS],
3030

31-
[commitlintGitHubRules.subjectEmpty]: [2, When.NEVER],
31+
[commitlintGitHubRules.subjectEmpty]: [2, When.NON_WIPS_NEVER],
3232
[commitlintGitHubRules.subjectSeparator]: [2, When.ALWAYS],
33-
[commitlintGitHubRules.subjectCase]: [2, When.ALWAYS, 'sentence-case'],
34-
[commitlintGitHubRules.subjectFullStop]: [1, When.ALWAYS], // Warning only
33+
[commitlintGitHubRules.subjectCase]: [2, When.NON_WIPS_ALWAYS, 'sentence-case'],
34+
[commitlintGitHubRules.subjectFullStop]: [1, When.NON_WIPS_ALWAYS], // Warning only
3535

3636
[commitlintGitHubRules.typeCase]: [2, When.ALWAYS, 'lower-case'],
3737
[commitlintGitHubRules.typeEnum]: [2, When.ALWAYS, supportedTypes],

packages/commitlint-github-utils/@types/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export const enum When {
5252
ALWAYS = 'always',
5353
NEVER = 'never',
5454
IGNORED = 'ignored', // For when the rule doesn't vary based on the 'when' value passed in
55+
NON_WIPS_ALWAYS = 'always-in-non-wips',
56+
NON_WIPS_NEVER = 'never-in-non-wips',
5557
}
5658

5759
// TODO: Remove once @commitlint/types is published

packages/commitlint-github-utils/src/handleWipCommits.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
import { ParsedCommitMessage, When, WipHandledResult } from '../@types';
22

33
/**
4-
* Handles short-circuiting WIP commits to immediately return as valid. Previsouly it also converted the if the when passed in
5-
* when we had custom When values to allow rules to be configured to only be applicable to non-WIPs (e.g. NON_WIPS_ALWAYS, NON_WIPS_NEVER).
6-
* However commitlint currently restricts When values in the top-level configuration to just 'always' and 'never' and rejects configuration
7-
* with any other value.
8-
*
9-
* Therefore we have stripped out support for custom When values until a time when they may be supported (or not):
10-
* See: https://github.com/conventional-changelog/commitlint/issues/1003
4+
* Handles both short-circuiting WIP commits to return as valid if the whenPassedIn is only applicable to non-WIPs (e.g. NON_WIPS_ALWAYS, NON_WIPS_NEVER).
5+
* Also handles converting those Whens if the commit isn't a WIP, and so returns NEVER when given NON_WIPS_NEVER or ALWAYS otherwise.
6+
* This is so that standard rules can use the standard When values and not deal with NON_WIPS_* When values.
117
*
128
* @param commitMessage the parsed commit message
139
* @param whenPassedIn the When passed in
1410
* @returns { isWipValidated: true } if the When given only applies to non-WIP commits and the commit is a WIP;
1511
* or { isWipValidated: false, when } with the When to apply to the non-WIP, see description above.
1612
*/
17-
function handleWipCommits(commitMessage: ParsedCommitMessage, when?: When): WipHandledResult {
18-
if (commitMessage.isWip) {
19-
return { isWipValidated: true };
13+
function handleWipCommits(commitMessage: ParsedCommitMessage, whenPassedIn?: When): WipHandledResult {
14+
let when = whenPassedIn;
15+
16+
// Handle the non-wips 'when' if specified as it is custom and not intended to be passed to a base rule resolver
17+
if (when === When.NON_WIPS_ALWAYS || when === When.NON_WIPS_NEVER) {
18+
// If the commit is a WIP commit we don't validate as requested not to by the When
19+
if (commitMessage.isWip) {
20+
return { isWipValidated: true };
21+
}
22+
23+
// Otherwise the commit the commit isn't a WIP so we just need to convert the non-wips 'when'
24+
// to the corresponding standard 'when' for the baseResolver to understand.
25+
if (when === When.NON_WIPS_ALWAYS) {
26+
when = When.ALWAYS;
27+
} else {
28+
when = When.NEVER;
29+
}
2030
}
2131

2232
return { isWipValidated: false, when };

packages/commitlint-github-utils/src/tests/handleWipCommits.test.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,32 @@ const NON_WIP_COMMIT: ParsedCommitMessage = {
1616
};
1717

1818
describe('handleWipCommits', () => {
19-
it('should return validated when passed a WIP Commit regardless of the "When" clause', () => {
20-
expect(handleWipCommits(WIP_COMMIT, When.ALWAYS).isWipValidated).toBe(true);
21-
expect(handleWipCommits(WIP_COMMIT, When.NEVER).isWipValidated).toBe(true);
19+
it('should return validated when passed a WIP Commit and the "When" clause is only for non-WIPs', () => {
20+
expect(handleWipCommits(WIP_COMMIT, When.NON_WIPS_ALWAYS).isWipValidated).toBe(true);
21+
expect(handleWipCommits(WIP_COMMIT, When.NON_WIPS_NEVER).isWipValidated).toBe(true);
2222
});
2323

24-
it('should return not validated when passed a non-WIP Commit regardless of the "When" clause', () => {
24+
it('should return not validated when passed a non-WIP Commit and the "When" clause is only for non-WIPs', () => {
25+
expect(handleWipCommits(NON_WIP_COMMIT, When.NON_WIPS_ALWAYS).isWipValidated).toBe(false);
26+
expect(handleWipCommits(NON_WIP_COMMIT, When.NON_WIPS_NEVER).isWipValidated).toBe(false);
27+
});
28+
29+
it('should return not validated when passed a WIP and non-WIP Commits and the "When" clause is not just for non-WIPs', () => {
30+
expect(handleWipCommits(WIP_COMMIT, When.ALWAYS).isWipValidated).toBe(false);
31+
expect(handleWipCommits(WIP_COMMIT, When.NEVER).isWipValidated).toBe(false);
32+
expect(handleWipCommits(WIP_COMMIT, When.IGNORED).isWipValidated).toBe(false);
33+
2534
expect(handleWipCommits(NON_WIP_COMMIT, When.ALWAYS).isWipValidated).toBe(false);
2635
expect(handleWipCommits(NON_WIP_COMMIT, When.NEVER).isWipValidated).toBe(false);
36+
expect(handleWipCommits(NON_WIP_COMMIT, When.IGNORED).isWipValidated).toBe(false);
37+
});
38+
39+
it('should return the corresponding "standard" When when passed a non-WIP Commit and the "When" clause is only for non-WIPs', () => {
40+
expect(handleWipCommits(NON_WIP_COMMIT, When.NON_WIPS_ALWAYS).when).toBe(When.ALWAYS);
41+
expect(handleWipCommits(NON_WIP_COMMIT, When.NON_WIPS_NEVER).when).toBe(When.NEVER);
2742
});
2843

29-
it('should return the When passed in when passed a non-WIP Commit regardless of the "When" clause', () => {
44+
it('should return the existing "standard" When when passed a non-WIP Commit and the "When" clause is already standard', () => {
3045
expect(handleWipCommits(NON_WIP_COMMIT, When.ALWAYS).when).toBe(When.ALWAYS);
3146
expect(handleWipCommits(NON_WIP_COMMIT, When.NEVER).when).toBe(When.NEVER);
3247
expect(handleWipCommits(NON_WIP_COMMIT, When.IGNORED).when).toBe(When.IGNORED);

packages/commitlint-plugin-github-rules/src/rules/githubIssueNumbers/isCorrectFormat.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ const githubIssueNumberFormatRuleResolver: RuleResolver<void> = parsed => {
77

88
const commitMessage = utils.parseCommitMessage(rawCommitMessage);
99

10-
// We short circuit and return true for WIP commits since we don't valdiate those
11-
if (commitMessage.isWip) {
12-
return [true];
13-
}
14-
1510
const issueNumbersValid = commitMessage.rawIssueNumbers == null || commitMessage.issueNumbers.length > 0;
1611

1712
return [

packages/commitlint-plugin-github-rules/src/rules/githubIssueNumbers/isDuplicate.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ const githubIssueNumberDuplicateRuleResolver: RuleResolver<void> = parsed => {
99

1010
const commitMessage = utils.parseCommitMessage(rawCommitMessage);
1111

12-
// We short circuit and return true for WIP commits since we don't valdiate those
13-
if (commitMessage.isWip) {
14-
return [true];
15-
}
16-
1712
const issueNumbersValid = commitMessage.rawIssueNumbers == null || isNoDuplicates(commitMessage.issueNumbers);
1813

1914
return [issueNumbersValid, 'the commit message duplicate issue numbers referenced in the commit message prefix.'];

packages/commitlint-plugin-github-rules/src/rules/githubIssueNumbers/isMissing.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,34 @@ const githubIssueNumberMissingRuleResolver: RuleResolver<boolean> = (parsed, whe
88

99
const commitMessage = utils.parseCommitMessage(rawCommitMessage);
1010

11-
// We short circuit and return true for WIP commits since we don't valdiate those
12-
if (commitMessage.isWip) {
11+
const issueNumbersFound = commitMessage.rawIssueNumbers != null;
12+
13+
// If we're only required to validate non-WIPs and the commit is a WIP then just return true
14+
if (commitMessage.isWip && (when === When.NON_WIPS_ALWAYS || when === When.NON_WIPS_NEVER)) {
1315
return [true];
1416
}
1517

16-
const issueNumbersFound = commitMessage.rawIssueNumbers != null;
17-
1818
// First check for the standard case where no issue numbers are found and it is required to never be missing
19-
if (!issueNumbersFound && when === When.NEVER) {
19+
if (!issueNumbersFound && (when === When.NEVER || when === When.NON_WIPS_NEVER)) {
20+
if (commitMessage.isWip) {
21+
return [
22+
false,
23+
'the WIP commit message must start with the associated GitHub issue number in parentheses followed by space. For example: (#42) WIP: My git commit message.',
24+
];
25+
}
26+
2027
return [
2128
false,
2229
'the commit message must start with the associated GitHub issue number in parentheses followed by space. For example: (#42) My git commit message.',
2330
];
2431
}
2532

2633
// Next check the odd case of when issue numbers are found but it was required to always be missing
27-
if (issueNumbersFound && when === When.ALWAYS) {
34+
if (issueNumbersFound && (when === When.ALWAYS || when === When.NON_WIPS_ALWAYS)) {
35+
if (commitMessage.isWip) {
36+
return [false, 'the WIP commit message must NOT start with the associated GitHub issue number in parentheses.'];
37+
}
38+
2839
return [false, 'the commit message must NOT start with the associated GitHub issue number in parentheses.'];
2940
}
3041

packages/commitlint-plugin-github-rules/src/rules/githubIssueNumbers/tests/isCorrectFormat.test.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ describe('githubIssueNumberFormatRuleResolver', () => {
1111
it('should always return an error response if an empty commit message is provided', () => {
1212
expect(parse(When.ALWAYS, '')[0]).toEqual(false);
1313
expect(parse(When.NEVER, '')[0]).toEqual(false);
14+
expect(parse(When.NON_WIPS_ALWAYS, '')[0]).toEqual(false);
15+
expect(parse(When.NON_WIPS_NEVER, '')[0]).toEqual(false);
1416
});
1517

1618
it('should return a success response if one issue number is specified', () => {
1719
expect(parse(When.ALWAYS, '(#1) my commit message')[0]).toEqual(true);
1820
expect(parse(When.ALWAYS, '(#1) chore: my commit message')[0]).toEqual(true);
21+
expect(parse(When.ALWAYS, '(#1) WIP: my commit message')[0]).toEqual(true);
1922
});
2023

2124
it('should return a success response if multiple issue numbers are specified', () => {
@@ -24,44 +27,40 @@ describe('githubIssueNumberFormatRuleResolver', () => {
2427

2528
expect(parse(When.ALWAYS, '(#1, #2) chore: my commit message')[0]).toEqual(true);
2629
expect(parse(When.ALWAYS, '(#1,#2) chore: my commit message')[0]).toEqual(true);
30+
31+
expect(parse(When.ALWAYS, '(#1, #2) WIP: my commit message')[0]).toEqual(true);
32+
expect(parse(When.ALWAYS, '(#1,#2) WIP: my commit message')[0]).toEqual(true);
2733
});
2834

2935
it('should return a success response if no issue numbers are specified (as nothing to validate)', () => {
3036
expect(parse(When.ALWAYS, 'my commit message')[0]).toEqual(true);
3137
expect(parse(When.ALWAYS, 'chore: my commit message')[0]).toEqual(true);
38+
expect(parse(When.ALWAYS, 'WIP: my commit message')[0]).toEqual(true);
3239
});
3340

3441
it('should return an error response if not in correct format', () => {
3542
expect(parse(When.ALWAYS, '(1) my commit message')[0]).toEqual(false);
3643
expect(parse(When.ALWAYS, '(1) chore: my commit message')[0]).toEqual(false);
44+
expect(parse(When.ALWAYS, '(1) WIP: my commit message')[0]).toEqual(false);
3745

3846
expect(parse(When.ALWAYS, '(bob) my commit message')[0]).toEqual(false);
3947
expect(parse(When.ALWAYS, '(bob) chore: my commit message')[0]).toEqual(false);
48+
expect(parse(When.ALWAYS, '(bob) WIP: my commit message')[0]).toEqual(false);
4049

4150
expect(parse(When.ALWAYS, '(#bob) my commit message')[0]).toEqual(false);
4251
expect(parse(When.ALWAYS, '(#bob) chore: my commit message')[0]).toEqual(false);
52+
expect(parse(When.ALWAYS, '(#bob) WIP: my commit message')[0]).toEqual(false);
4353

4454
expect(parse(When.ALWAYS, '(#1, #2bob) my commit message')[0]).toEqual(false);
4555
expect(parse(When.ALWAYS, '(#1, #2bob) chore: my commit message')[0]).toEqual(false);
56+
expect(parse(When.ALWAYS, '(#1, #2bob) WIP: my commit message')[0]).toEqual(false);
4657

4758
expect(parse(When.ALWAYS, '(#1,#2bob) my commit message')[0]).toEqual(false);
4859
expect(parse(When.ALWAYS, '(#1,#2bob) chore: my commit message')[0]).toEqual(false);
60+
expect(parse(When.ALWAYS, '(#1,#2bob) WIP: my commit message')[0]).toEqual(false);
4961

5062
expect(parse(When.ALWAYS, '(#1, #2 bob) my commit message')[0]).toEqual(false);
5163
expect(parse(When.ALWAYS, '(#1, #2 bob) chore: my commit message')[0]).toEqual(false);
52-
});
53-
54-
it('should always return a success response for any WIP commit as rules are disabled for WIPs', () => {
55-
expect(parse(When.ALWAYS, '(#1) WIP: my commit message')[0]).toEqual(true);
56-
expect(parse(When.ALWAYS, '(#1, #2) WIP: my commit message')[0]).toEqual(true);
57-
expect(parse(When.ALWAYS, '(#1,#2) WIP: my commit message')[0]).toEqual(true);
58-
59-
expect(parse(When.ALWAYS, 'WIP')[0]).toEqual(true);
60-
expect(parse(When.ALWAYS, 'WIP2')[0]).toEqual(true);
61-
expect(parse(When.ALWAYS, 'WIP: my commit message')[0]).toEqual(true);
62-
expect(parse(When.ALWAYS, 'WIP2: My commit message')[0]).toEqual(true);
63-
expect(parse(When.ALWAYS, 'WIP 2: My commit message')[0]).toEqual(true);
64-
expect(parse(When.ALWAYS, 'WIP2 - My commit message')[0]).toEqual(true);
65-
expect(parse(When.ALWAYS, 'WIP 2 - My commit message')[0]).toEqual(true);
64+
expect(parse(When.ALWAYS, '(#1, #2 bob) WIP: my commit message')[0]).toEqual(false);
6665
});
6766
});

packages/commitlint-plugin-github-rules/src/rules/githubIssueNumbers/tests/isDuplicate.test.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,43 @@ describe('githubIssueNumberDuplicateRuleResolver', () => {
1111
it('should always return an error response if an empty commit message is provided', () => {
1212
expect(parse(When.ALWAYS, '')[0]).toEqual(false);
1313
expect(parse(When.NEVER, '')[0]).toEqual(false);
14+
expect(parse(When.NON_WIPS_ALWAYS, '')[0]).toEqual(false);
15+
expect(parse(When.NON_WIPS_NEVER, '')[0]).toEqual(false);
1416
});
1517

1618
it('should return a success response if there are no duplicates', () => {
1719
expect(parse(When.NEVER, '(#1) my commit message')[0]).toEqual(true);
1820
expect(parse(When.NEVER, '(#1) chore: my commit message')[0]).toEqual(true);
21+
expect(parse(When.NEVER, '(#1) WIP: my commit message')[0]).toEqual(true);
1922

2023
expect(parse(When.NEVER, '(#1, #2) my commit message')[0]).toEqual(true);
2124
expect(parse(When.NEVER, '(#1,#2) my commit message')[0]).toEqual(true);
2225

2326
expect(parse(When.NEVER, '(#123, #23) chore: my commit message')[0]).toEqual(true);
2427
expect(parse(When.NEVER, '(#123,#23) chore: my commit message')[0]).toEqual(true);
28+
29+
expect(parse(When.NEVER, '(#1, #2, #3, #4) WIP: my commit message')[0]).toEqual(true);
30+
expect(parse(When.NEVER, '(#1,#2,#3,#4) WIP: my commit message')[0]).toEqual(true);
2531
});
2632

2733
it('should return a success response if there are no issue numbers', () => {
34+
expect(parse(When.NEVER, 'WIP: my commit message')[0]).toEqual(true);
2835
expect(parse(When.NEVER, 'my commit message')[0]).toEqual(true);
2936
expect(parse(When.NEVER, '() chore: my commit message')[0]).toEqual(true);
3037
});
3138

3239
it('should return an error response if there is at least one duplicate', () => {
3340
expect(parse(When.NEVER, '(#123, #2, #123) my commit message')[0]).toEqual(false);
3441
expect(parse(When.NEVER, '(#1, #1) chore: my commit message')[0]).toEqual(false);
42+
expect(parse(When.NEVER, '(#12, #12, #1) WIP: my commit message')[0]).toEqual(false);
3543

3644
expect(parse(When.NEVER, '(#133, #133) my commit message')[0]).toEqual(false);
3745
expect(parse(When.NEVER, '(#122,#122) my commit message')[0]).toEqual(false);
3846

3947
expect(parse(When.NEVER, '(#123, #123) chore: my commit message')[0]).toEqual(false);
4048
expect(parse(When.NEVER, '(#123,#123) chore: my commit message')[0]).toEqual(false);
41-
});
4249

43-
it('should always return a success response for any WIP commit as rules are disabled for WIPs', () => {
44-
expect(parse(When.NEVER, '(#1) WIP: my commit message')[0]).toEqual(true);
45-
expect(parse(When.NEVER, '(#1, #2) WIP: my commit message')[0]).toEqual(true);
46-
expect(parse(When.NEVER, '(#1,#2) WIP: my commit message')[0]).toEqual(true);
47-
48-
expect(parse(When.NEVER, 'WIP')[0]).toEqual(true);
49-
expect(parse(When.NEVER, 'WIP2')[0]).toEqual(true);
50-
expect(parse(When.NEVER, 'WIP: my commit message')[0]).toEqual(true);
51-
expect(parse(When.NEVER, 'WIP2: My commit message')[0]).toEqual(true);
52-
expect(parse(When.NEVER, 'WIP 2: My commit message')[0]).toEqual(true);
53-
expect(parse(When.NEVER, 'WIP2 - My commit message')[0]).toEqual(true);
54-
expect(parse(When.NEVER, 'WIP 2 - My commit message')[0]).toEqual(true);
50+
expect(parse(When.NEVER, '(#1, #2, #3, #2, #4) WIP: my commit message')[0]).toEqual(false);
51+
expect(parse(When.NEVER, '(#1,#2,#3,#2,#4) WIP: my commit message')[0]).toEqual(false);
5552
});
5653
});

0 commit comments

Comments
 (0)