diff --git a/commitlint.config.ts b/commitlint.config.ts index 9414d2c6..fb66f9d9 100644 --- a/commitlint.config.ts +++ b/commitlint.config.ts @@ -98,17 +98,14 @@ export default { }, "header-max-length-with-suggestions": ( - { header }: { header: any }, + { raw }: { raw: any }, _: any, maxLineLength: number ) => { - const headerStr = extractStringFromCommitlintParam( - "header", - header - ); + const rawStr = extractStringFromCommitlintParam("rawStr", raw); return Plugins.headerMaxLengthWithSuggestions( - headerStr, + rawStr, maxLineLength ); }, diff --git a/commitlint/abbreviations.ts b/commitlint/abbreviations.ts index b722af3e..94ef3516 100644 --- a/commitlint/abbreviations.ts +++ b/commitlint/abbreviations.ts @@ -46,7 +46,6 @@ export const abbr = { "command": "cmd", "commands": "cmds", "command line": "cmdline", - "compare": "cmp", "compress": "zip", "compressed": "zipped", "concatenate": "concat", diff --git a/commitlint/plugins.ts b/commitlint/plugins.ts index fbe94ce1..4b01451b 100644 --- a/commitlint/plugins.ts +++ b/commitlint/plugins.ts @@ -128,14 +128,46 @@ export abstract class Plugins { } public static headerMaxLengthWithSuggestions( - headerStr: string, + rawStr: string, maxLineLength: number ) { let offence = false; + const lineBreakIndex = rawStr.indexOf("\n"); + let headerStr = rawStr; + if (lineBreakIndex >= 0) { + // Extracting headerStr from rawStr rather than using header directly is a + // workaround for what must be a commitlint or conventional-changelog bug, TODO: report + headerStr = rawStr.substring(0, lineBreakIndex); + } + const headerLength = headerStr.length; let message = `Please do not exceed ${maxLineLength} characters in title (found ${headerLength}).`; - if (!headerStr.startsWith("Merge ") && headerLength > maxLineLength) { + let theMaxLineLengthToCompareWith = maxLineLength; + + // note: a revert of a revert, in new versions of git, is written as "Reapply..." which happens + // to have same length as "Revert" + const extraCharsAllowedBecauseOfARevertOrARevertOfARevert = + 'Revert "'.length + + // we add one because revert commits end with '"' + 1; + + const maxLineLengthAfterAccountingForRevertException = + maxLineLength + extraCharsAllowedBecauseOfARevertOrARevertOfARevert; + if ( + headerStr.endsWith('"') && + (headerStr.startsWith('Revert "') || + headerStr.startsWith('Reapply "')) + ) { + theMaxLineLengthToCompareWith = + maxLineLengthAfterAccountingForRevertException; + message = `Please do not exceed ${maxLineLength} (${maxLineLengthAfterAccountingForRevertException} if it's a revert) characters in title (found ${headerLength}).`; + } + + if ( + !headerStr.startsWith("Merge ") && + headerLength > theMaxLineLengthToCompareWith + ) { offence = true; const colonIndex = headerStr.indexOf(":"); diff --git a/commitlint/tests/plugins.test.ts b/commitlint/tests/plugins.test.ts index aeb70eb9..0da0a342 100644 --- a/commitlint/tests/plugins.test.ts +++ b/commitlint/tests/plugins.test.ts @@ -858,7 +858,7 @@ test("prefer-slash-over-backslash2", () => { test("header-max-length-with-suggestions1", () => { const commitMsgWithThatExceedsHeaderMaxLength = - "foo: this is only a title with a configuration in it that exceeds header max length"; + "foo: this is only a title with the term 'configuration' in it that exceeds header max length"; const headerMaxLength1 = runCommitLintOnMsg( commitMsgWithThatExceedsHeaderMaxLength ); @@ -871,7 +871,7 @@ test("header-max-length-with-suggestions1", () => { test("header-max-length-with-suggestions2", () => { const commitMsgWithThatExceedsHeaderMaxLength = - "foo: this is only a title with a 1 second in it that exceeds header max length"; + "foo: this is only a title with the term '1 second' (which includes a space) in it that exceeds header max length"; const headerMaxLength2 = runCommitLintOnMsg( commitMsgWithThatExceedsHeaderMaxLength ); @@ -986,16 +986,30 @@ test("header-max-length-with-suggestions11", () => { }); test("header-max-length-with-suggestions12", () => { - const commitMsgThatExceedsHeaderMaxLength = - "Split that compares better because blah blah bla very very very long title"; + const commitMsgThatExceedsHeaderMaxLengthBecauseItIsARevert = + 'Revert "This header is a title with less than 50chars"'; const headerMaxLength12 = runCommitLintOnMsg( - commitMsgThatExceedsHeaderMaxLength + commitMsgThatExceedsHeaderMaxLengthBecauseItIsARevert ); - const not_expected_message = `"compares" -> "cmps"`; - expect(headerMaxLength12.status).not.toBe(0); - expect( - (headerMaxLength12.stdout + "").includes(not_expected_message) - ).toEqual(false); + expect(headerMaxLength12.status).toBe(0); +}); + +test("header-max-length-with-suggestions13", () => { + const commitMsgThatExceedsHeaderMaxLengthBecauseItIsARevertOfARevert = + 'Reapply "This header is a title with less than 50chars"'; + const headerMaxLength13 = runCommitLintOnMsg( + commitMsgThatExceedsHeaderMaxLengthBecauseItIsARevertOfARevert + ); + expect(headerMaxLength13.status).toBe(0); +}); + +test("header-max-length-with-suggestions14", () => { + const commitMsgThatExceedsHeaderMaxLengthEvenIfItIsARevert = + 'Revert "This header is a title with moooooooooore than 50chars"'; + const headerMaxLength14 = runCommitLintOnMsg( + commitMsgThatExceedsHeaderMaxLengthEvenIfItIsARevert + ); + expect(headerMaxLength14.status).not.toBe(0); }); test("proper-issue-refs1", () => {