feat(dev-infra): validate breaking changes commit messages (#41274)
With this change we valid breaking changes descriptions as per our contribution guidelines
See: 88fbc06677/CONTRIBUTING.md (commit-message-footer)
PR Close #41274
This commit is contained in:
parent
b012a56615
commit
e8cae22d66
|
@ -263,5 +263,72 @@ describe('validate-commit-message.js', () => {
|
||||||
VALID);
|
VALID);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('breaking change', () => {
|
||||||
|
it('should allow valid breaking change commit descriptions', () => {
|
||||||
|
const msgWithSummary = 'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is a normal commit message body which does not exceed the max length\n' +
|
||||||
|
'limit. For more details see the following super long URL:\n\n' +
|
||||||
|
'BREAKING CHANGE: This is a summary of a breaking change.';
|
||||||
|
expectValidationResult(validateCommitMessage(msgWithSummary), VALID);
|
||||||
|
|
||||||
|
const msgWithDescription = 'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is a normal commit message body which does not exceed the max length\n' +
|
||||||
|
'limit. For more details see the following super long URL:\n\n' +
|
||||||
|
'BREAKING CHANGE:\n\n' +
|
||||||
|
'This is a full description of the breaking change.';
|
||||||
|
expectValidationResult(validateCommitMessage(msgWithDescription), VALID);
|
||||||
|
|
||||||
|
const msgWithSummaryAndDescription =
|
||||||
|
'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is a normal commit message body which does not exceed the max length\n' +
|
||||||
|
'limit. For more details see the following super long URL:\n\n' +
|
||||||
|
'BREAKING CHANGE: This is a summary of a breaking change.\n\n' +
|
||||||
|
'This is a full description of the breaking change.';
|
||||||
|
expectValidationResult(validateCommitMessage(msgWithSummaryAndDescription), VALID);
|
||||||
|
|
||||||
|
const msgWithNonBreaking = 'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is not a\n' +
|
||||||
|
'breaking change commit.';
|
||||||
|
expectValidationResult(validateCommitMessage(msgWithNonBreaking), VALID);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for non-valid breaking change commit descriptions', () => {
|
||||||
|
const msgWithSummary = 'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is a normal commit message body which does not exceed the max length\n' +
|
||||||
|
'limit. For more details see the following super long URL:\n\n' +
|
||||||
|
'BREAKING CHANGE This is a summary of a breaking change.';
|
||||||
|
expectValidationResult(
|
||||||
|
validateCommitMessage(msgWithSummary), INVALID,
|
||||||
|
[`The commit message body contains an invalid breaking change description.`]);
|
||||||
|
|
||||||
|
const msgWithPlural = 'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is a normal commit message body which does not exceed the max length\n' +
|
||||||
|
'limit. For more details see the following super long URL:\n\n' +
|
||||||
|
'BREAKING CHANGES: This is a summary of a breaking change.';
|
||||||
|
expectValidationResult(
|
||||||
|
validateCommitMessage(msgWithPlural), INVALID,
|
||||||
|
[`The commit message body contains an invalid breaking change description.`]);
|
||||||
|
|
||||||
|
const msgWithDescription = 'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is a normal commit message body which does not exceed the max length\n' +
|
||||||
|
'limit. For more details see the following super long URL:\n\n' +
|
||||||
|
'BREAKING CHANGE:\n' +
|
||||||
|
'This is a full description of the breaking change.';
|
||||||
|
expectValidationResult(
|
||||||
|
validateCommitMessage(msgWithDescription), INVALID,
|
||||||
|
[`The commit message body contains an invalid breaking change description.`]);
|
||||||
|
|
||||||
|
const msgWithSummaryAndDescription =
|
||||||
|
'feat(compiler): this is just an usual commit message tile\n\n' +
|
||||||
|
'This is a normal commit message body which does not exceed the max length\n' +
|
||||||
|
'limit. For more details see the following super long URL:\n\n' +
|
||||||
|
'BREAKING CHANGE\n\n' +
|
||||||
|
'This is a full description of the breaking change.';
|
||||||
|
expectValidationResult(
|
||||||
|
validateCommitMessage(msgWithSummaryAndDescription), INVALID,
|
||||||
|
[`The commit message body contains an invalid breaking change description.`]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,16 @@ export interface ValidateCommitMessageResult {
|
||||||
|
|
||||||
/** Regex matching a URL for an entire commit body line. */
|
/** Regex matching a URL for an entire commit body line. */
|
||||||
const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
||||||
|
/**
|
||||||
|
* Regex matching a breaking change.
|
||||||
|
*
|
||||||
|
* - Starts with BREAKING CHANGE
|
||||||
|
* - Followed by a colon
|
||||||
|
* - Followed by a single space or two consecutive new lines
|
||||||
|
*
|
||||||
|
* NB: Anything after `BREAKING CHANGE` is optional to facilitate the validation.
|
||||||
|
*/
|
||||||
|
const COMMIT_BODY_BREAKING_CHANGE_RE = /^BREAKING CHANGE(:( |\n{2}))?/m;
|
||||||
|
|
||||||
/** Validate a commit message against using the local repo's config. */
|
/** Validate a commit message against using the local repo's config. */
|
||||||
export function validateCommitMessage(
|
export function validateCommitMessage(
|
||||||
|
@ -137,11 +147,24 @@ export function validateCommitMessage(
|
||||||
});
|
});
|
||||||
|
|
||||||
if (lineExceedsMaxLength) {
|
if (lineExceedsMaxLength) {
|
||||||
errors.push(
|
errors.push(`The commit message body contains lines greater than ${
|
||||||
`The commit message body contains lines greater than ${config.maxLineLength} characters`);
|
config.maxLineLength} characters.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Breaking change
|
||||||
|
// Check if the commit message contains a valid break change description.
|
||||||
|
// https://github.com/angular/angular/blob/88fbc066775ab1a2f6a8c75f933375b46d8fa9a4/CONTRIBUTING.md#commit-message-footer
|
||||||
|
const hasBreakingChange = COMMIT_BODY_BREAKING_CHANGE_RE.exec(commit.body);
|
||||||
|
if (hasBreakingChange !== null) {
|
||||||
|
const [, breakingChangeDescription] = hasBreakingChange;
|
||||||
|
if (!breakingChangeDescription) {
|
||||||
|
// Not followed by :, space or two consecutive new lines,
|
||||||
|
errors.push(`The commit message body contains an invalid breaking change description.`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,4 +183,9 @@ export function printValidationErrors(errors: string[], print = error) {
|
||||||
print();
|
print();
|
||||||
print('<body>');
|
print('<body>');
|
||||||
print();
|
print();
|
||||||
|
print(`BREAKING CHANGE: <breaking change summary>`);
|
||||||
|
print();
|
||||||
|
print(`<breaking change description>`);
|
||||||
|
print();
|
||||||
|
print();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1817,6 +1817,16 @@ function parseCommitMessagesForRange(range) {
|
||||||
*/
|
*/
|
||||||
/** Regex matching a URL for an entire commit body line. */
|
/** Regex matching a URL for an entire commit body line. */
|
||||||
const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
||||||
|
/**
|
||||||
|
* Regex matching a breaking change.
|
||||||
|
*
|
||||||
|
* - Starts with BREAKING CHANGE
|
||||||
|
* - Followed by a colon
|
||||||
|
* - Followed by a single space or two consecutive new lines
|
||||||
|
*
|
||||||
|
* NB: Anything after `BREAKING CHANGE` is optional to facilitate the validation.
|
||||||
|
*/
|
||||||
|
const COMMIT_BODY_BREAKING_CHANGE_RE = /^BREAKING CHANGE(:( |\n{2}))?/m;
|
||||||
/** Validate a commit message against using the local repo's config. */
|
/** Validate a commit message against using the local repo's config. */
|
||||||
function validateCommitMessage(commitMsg, options = {}) {
|
function validateCommitMessage(commitMsg, options = {}) {
|
||||||
const config = getCommitMessageConfig().commitMessage;
|
const config = getCommitMessageConfig().commitMessage;
|
||||||
|
@ -1903,9 +1913,21 @@ function validateCommitMessage(commitMsg, options = {}) {
|
||||||
return line.length > config.maxLineLength && !COMMIT_BODY_URL_LINE_RE.test(line);
|
return line.length > config.maxLineLength && !COMMIT_BODY_URL_LINE_RE.test(line);
|
||||||
});
|
});
|
||||||
if (lineExceedsMaxLength) {
|
if (lineExceedsMaxLength) {
|
||||||
errors.push(`The commit message body contains lines greater than ${config.maxLineLength} characters`);
|
errors.push(`The commit message body contains lines greater than ${config.maxLineLength} characters.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Breaking change
|
||||||
|
// Check if the commit message contains a valid break change description.
|
||||||
|
// https://github.com/angular/angular/blob/88fbc066775ab1a2f6a8c75f933375b46d8fa9a4/CONTRIBUTING.md#commit-message-footer
|
||||||
|
const hasBreakingChange = COMMIT_BODY_BREAKING_CHANGE_RE.exec(commit.body);
|
||||||
|
if (hasBreakingChange !== null) {
|
||||||
|
const [, breakingChangeDescription] = hasBreakingChange;
|
||||||
|
if (!breakingChangeDescription) {
|
||||||
|
// Not followed by :, space or two consecutive new lines,
|
||||||
|
errors.push(`The commit message body contains an invalid breaking change description.`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return { valid: validateCommitAndCollectErrors(), errors, commit };
|
return { valid: validateCommitAndCollectErrors(), errors, commit };
|
||||||
|
@ -1921,6 +1943,11 @@ function printValidationErrors(errors, print = error) {
|
||||||
print();
|
print();
|
||||||
print('<body>');
|
print('<body>');
|
||||||
print();
|
print();
|
||||||
|
print(`BREAKING CHANGE: <breaking change summary>`);
|
||||||
|
print();
|
||||||
|
print(`<breaking change description>`);
|
||||||
|
print();
|
||||||
|
print();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue