From 85936d351bce0ac268ddf2e8c464e48264e0f130 Mon Sep 17 00:00:00 2001 From: Gennady Kovshenin Date: Thu, 23 May 2024 10:36:29 +0300 Subject: [PATCH] Add min-body-length option When set an error will be emitted if the body does not contain enough characters. This is useful to require commit bodies with more information, like impact, motivation, approach, testing results, new behavior, etc. Default: no minimum. Suggested: at least 150. --- action.yml | 4 +++ dist/index.js | 24 +++++++++++++++++- src/__tests__/input.test.ts | 2 ++ src/__tests__/inspection.test.ts | 42 ++++++++++++++++++++++++++++++++ src/input.ts | 18 ++++++++++++++ src/inspection.ts | 13 ++++++++++ src/mainImpl.ts | 5 ++++ 7 files changed, 107 insertions(+), 1 deletion(-) diff --git a/action.yml b/action.yml index b0e5da8..90ff6ec 100644 --- a/action.yml +++ b/action.yml @@ -24,6 +24,10 @@ inputs: description: 'Maximum length of the commit subject line' required: false default: '' + min-body-length: + description: 'Minimum length of the body of the commit message' + required: false + default: '' max-body-line-length: description: 'Maximum length of a line in the body of the commit message' required: false diff --git a/dist/index.js b/dist/index.js index b308232..d70352b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -29029,6 +29029,7 @@ class Inputs { this.allowOneLiners = values.allowOneLiners; this.additionalVerbs = values.additionalVerbs; this.maxSubjectLength = values.maxSubjectLength; + this.minBodyLength = values.minBodyLength; this.maxBodyLineLength = values.maxBodyLineLength; this.enforceSignOff = values.enforceSignOff; this.validatePullRequestCommits = values.validatePullRequestCommits; @@ -29078,7 +29079,7 @@ function parseIntOrInfinity(text) { return parseInt(text, 10); } function parseInputs(rawInputs) { - const { additionalVerbsInput = '', pathToAdditionalVerbsInput = '', allowOneLinersInput = '', maxSubjectLengthInput = '', maxBodyLineLengthInput = '', enforceSignOffInput = '', validatePullRequestCommitsInput = '', skipBodyCheckInput = '', ignoreMergeCommitsInput = '', ignorePatternsInput = '', } = rawInputs; + const { additionalVerbsInput = '', pathToAdditionalVerbsInput = '', allowOneLinersInput = '', maxSubjectLengthInput = '', minBodyLengthInput = '', maxBodyLineLengthInput = '', enforceSignOffInput = '', validatePullRequestCommitsInput = '', skipBodyCheckInput = '', ignoreMergeCommitsInput = '', ignorePatternsInput = '', } = rawInputs; const additionalVerbs = new Set(); const hasAdditionalVerbsInput = additionalVerbsInput.length > 0; if (additionalVerbsInput) { @@ -29110,6 +29111,13 @@ function parseInputs(rawInputs) { return new MaybeInputs(null, 'Unexpected value for max-subject-line-length. ' + `Expected a number or nothing, got ${maxSubjectLengthInput}`); } + const minBodyLength = !minBodyLengthInput + ? 0 + : parseInt(minBodyLengthInput, 10); + if (Number.isNaN(minBodyLength)) { + return new MaybeInputs(null, 'Unexpected value for min-body-length. ' + + `Expected a number or nothing, got ${minBodyLengthInput}`); + } const maxBodyLineLength = !maxBodyLineLengthInput ? 72 : parseIntOrInfinity(maxBodyLineLengthInput); @@ -29158,6 +29166,7 @@ function parseInputs(rawInputs) { allowOneLiners, additionalVerbs, maxSubjectLength, + minBodyLength, maxBodyLineLength, enforceSignOff, validatePullRequestCommits, @@ -29380,6 +29389,15 @@ function checkBody(subject, bodyLines, inputs) { errors.push('Unexpected empty body'); return errors; } + // Minimum character body length + if (inputs.minBodyLength) { + const bodyLength = bodyLines.join(' ').length; + if (bodyLength < inputs.minBodyLength) { + errors.push(`The body must contain at least ${inputs.minBodyLength} characters. ` + + `The body contains ${bodyLength} characters.`); + return errors; + } + } for (const [i, line] of bodyLines.entries()) { if (urlLineRe.test(line) || linkDefinitionRe.test(line)) { continue; @@ -29574,6 +29592,9 @@ async function runWithExceptions() { const maxSubjectLengthInput = core.getInput('max-subject-line-length', { required: false, }); + const minBodyLengthInput = core.getInput('min-body-length', { + required: false, + }); const maxBodyLineLengthInput = core.getInput('max-body-line-length', { required: false, }); @@ -29597,6 +29618,7 @@ async function runWithExceptions() { pathToAdditionalVerbsInput, allowOneLinersInput, maxSubjectLengthInput, + minBodyLengthInput, maxBodyLineLengthInput, enforceSignOffInput, validatePullRequestCommitsInput, diff --git a/src/__tests__/input.test.ts b/src/__tests__/input.test.ts index 6c05e21..e9dc715 100644 --- a/src/__tests__/input.test.ts +++ b/src/__tests__/input.test.ts @@ -35,6 +35,7 @@ it('parses the inputs.', () => { pathToAdditionalVerbsInput: pathToVerbs, allowOneLinersInput: 'true', maxSubjectLengthInput: '90', + minBodyLengthInput: '120', maxBodyLineLengthInput: '100', enforceSignOffInput: 'true', validatePullRequestCommitsInput: 'true', @@ -56,6 +57,7 @@ it('parses the inputs.', () => { ); expect(inputs.allowOneLiners).toBeTruthy(); expect(inputs.maxSubjectLength).toEqual(90); + expect(inputs.minBodyLength).toEqual(120); expect(inputs.maxBodyLineLength).toEqual(100); expect(inputs.enforceSignOff).toBeTruthy(); expect(inputs.validatePullRequestCommits).toBeTruthy(); diff --git a/src/__tests__/inspection.test.ts b/src/__tests__/inspection.test.ts index acffcd7..330c294 100644 --- a/src/__tests__/inspection.test.ts +++ b/src/__tests__/inspection.test.ts @@ -212,6 +212,7 @@ it( allowOneLiners: false, additionalVerbs: new Set('table'), maxSubjectLength: 50, + minBodyLength: 0, maxBodyLineLength: 72, enforceSignOff: false, validatePullRequestCommits: false, @@ -318,6 +319,47 @@ it('reports too long a subject line with custom max length.', () => { ]); }); +it('reports too short a body length.', () => { + const message = + 'Change SomeClass to OtherClass\n' + + '\n' + + 'This replaces the SomeClass with OtherClass in all of the module\n' + + 'since Some class was deprecated.'; + + const inputs = input.parseInputs({minBodyLengthInput: '100'}).mustInputs(); + + const errors = inspection.check(message, inputs); + expect(errors).toEqual([ + `The body must contain at least 100 characters. ` + + `The body contains 97 characters.` + ]); +}); + +it('accepts a body length.', () => { + const message = + 'Change SomeClass to OtherClass\n' + + '\n' + + 'This replaces the SomeClass with OtherClass in all of the module\n' + + 'since Some class was deprecated.'; + + const inputs = input.parseInputs({minBodyLengthInput: '97'}).mustInputs(); + + const errors = inspection.check(message, inputs); + expect(errors).toEqual([]); +}); + +it('accepts a no minimum body length.', () => { + const message = + 'Change SomeClass to OtherClass\n' + + '\n' + + 'This changes SomeClass to OtherClass'; + + const inputs = input.parseInputs({}).mustInputs(); + + const errors = inspection.check(message, inputs); + expect(errors).toEqual([]); +}); + it('reports too long a body line.', () => { const message = 'Change SomeClass to OtherClass\n' + diff --git a/src/input.ts b/src/input.ts index 58732bb..c9036a7 100644 --- a/src/input.ts +++ b/src/input.ts @@ -6,6 +6,7 @@ interface InputValues { allowOneLiners: boolean; additionalVerbs: Set; maxSubjectLength: number; + minBodyLength: number; maxBodyLineLength: number; enforceSignOff: boolean; validatePullRequestCommits: boolean; @@ -19,6 +20,7 @@ export class Inputs implements InputValues { public pathToAdditionalVerbs: string; public allowOneLiners: boolean; public maxSubjectLength: number; + public minBodyLength: number; public maxBodyLineLength: number; public skipBodyCheck: boolean; public validatePullRequestCommits: boolean; @@ -38,6 +40,7 @@ export class Inputs implements InputValues { this.allowOneLiners = values.allowOneLiners; this.additionalVerbs = values.additionalVerbs; this.maxSubjectLength = values.maxSubjectLength; + this.minBodyLength = values.minBodyLength; this.maxBodyLineLength = values.maxBodyLineLength; this.enforceSignOff = values.enforceSignOff; this.validatePullRequestCommits = values.validatePullRequestCommits; @@ -82,6 +85,7 @@ interface RawInputs { pathToAdditionalVerbsInput?: string; allowOneLinersInput?: string; maxSubjectLengthInput?: string; + minBodyLengthInput?: string; maxBodyLineLengthInput?: string; enforceSignOffInput?: string; validatePullRequestCommitsInput?: string; @@ -118,6 +122,7 @@ export function parseInputs(rawInputs: RawInputs): MaybeInputs { pathToAdditionalVerbsInput = '', allowOneLinersInput = '', maxSubjectLengthInput = '', + minBodyLengthInput = '', maxBodyLineLengthInput = '', enforceSignOffInput = '', validatePullRequestCommitsInput = '', @@ -176,6 +181,18 @@ export function parseInputs(rawInputs: RawInputs): MaybeInputs { ); } + const minBodyLength: number = !minBodyLengthInput + ? 0 + : parseInt(minBodyLengthInput, 10); + + if (Number.isNaN(minBodyLength)) { + return new MaybeInputs( + null, + 'Unexpected value for min-body-length. ' + + `Expected a number or nothing, got ${minBodyLengthInput}`, + ); + } + const maxBodyLineLength: number = !maxBodyLineLengthInput ? 72 : parseIntOrInfinity(maxBodyLineLengthInput); @@ -253,6 +270,7 @@ export function parseInputs(rawInputs: RawInputs): MaybeInputs { allowOneLiners, additionalVerbs, maxSubjectLength, + minBodyLength, maxBodyLineLength, enforceSignOff, validatePullRequestCommits, diff --git a/src/inspection.ts b/src/inspection.ts index 06bc63c..fdfc4ed 100644 --- a/src/inspection.ts +++ b/src/inspection.ts @@ -228,6 +228,19 @@ function checkBody( return errors; } + // Minimum character body length + if (inputs.minBodyLength) { + const bodyLength = bodyLines.join(' ').length; + + if (bodyLength < inputs.minBodyLength) { + errors.push( + `The body must contain at least ${inputs.minBodyLength} characters. ` + + `The body contains ${bodyLength} characters.`, + ); + return errors; + } + } + for (const [i, line] of bodyLines.entries()) { if (urlLineRe.test(line) || linkDefinitionRe.test(line)) { continue; diff --git a/src/mainImpl.ts b/src/mainImpl.ts index 2c3cc01..9e29f46 100644 --- a/src/mainImpl.ts +++ b/src/mainImpl.ts @@ -25,6 +25,10 @@ async function runWithExceptions(): Promise { required: false, }); + const minBodyLengthInput = core.getInput('min-body-length', { + required: false, + }); + const maxBodyLineLengthInput = core.getInput('max-body-line-length', { required: false, }); @@ -57,6 +61,7 @@ async function runWithExceptions(): Promise { pathToAdditionalVerbsInput, allowOneLinersInput, maxSubjectLengthInput, + minBodyLengthInput, maxBodyLineLengthInput, enforceSignOffInput, validatePullRequestCommitsInput,