refactor(dev-infra): extract the parsing of a range of commits into a util function (#39726)
A utility function to parse a range of commits allows for other tooling to assess a range of commits. PR Close #39726
This commit is contained in:
parent
3b2e5be6cb
commit
7261e393f1
38
dev-infra/commit-message/utils.ts
Normal file
38
dev-infra/commit-message/utils.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google LLC All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {exec} from '../utils/shelljs';
|
||||||
|
|
||||||
|
import {parseCommitMessage, ParsedCommitMessage} from './parse';
|
||||||
|
|
||||||
|
/** Retrieve and parse each commit message in a provide range. */
|
||||||
|
export function parseCommitMessagesForRange(range: string): ParsedCommitMessage[] {
|
||||||
|
/** A random number used as a split point in the git log result. */
|
||||||
|
const randomValueSeparator = `${Math.random()}`;
|
||||||
|
/**
|
||||||
|
* Custom git log format that provides the commit header and body, separated as expected with the
|
||||||
|
* custom separator as the trailing value.
|
||||||
|
*/
|
||||||
|
const gitLogFormat = `%s%n%n%b${randomValueSeparator}`;
|
||||||
|
|
||||||
|
// Retrieve the commits in the provided range.
|
||||||
|
const result = exec(`git log --reverse --format=${gitLogFormat} ${range}`);
|
||||||
|
if (result.code) {
|
||||||
|
throw new Error(`Failed to get all commits in the range:\n ${result.stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
// Separate the commits from a single string into individual commits.
|
||||||
|
.split(randomValueSeparator)
|
||||||
|
// Remove extra space before and after each commit message.
|
||||||
|
.map(l => l.trim())
|
||||||
|
// Remove any superfluous lines which remain from the split.
|
||||||
|
.filter(line => !!line)
|
||||||
|
// Parse each commit message.
|
||||||
|
.map(commit => parseCommitMessage(commit));
|
||||||
|
}
|
@ -6,55 +6,37 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {error, info} from '../../utils/console';
|
import {error, info} from '../../utils/console';
|
||||||
import {exec} from '../../utils/shelljs';
|
|
||||||
|
|
||||||
import {parseCommitMessage} from '../parse';
|
import {ParsedCommitMessage} from '../parse';
|
||||||
|
import {parseCommitMessagesForRange} from '../utils';
|
||||||
import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from '../validate';
|
import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from '../validate';
|
||||||
|
|
||||||
// Whether the provided commit is a fixup commit.
|
// Whether the provided commit is a fixup commit.
|
||||||
const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup;
|
const isNonFixup = (commit: ParsedCommitMessage) => !commit.isFixup;
|
||||||
|
|
||||||
// Extracts commit header (first line of commit message).
|
// Extracts commit header (first line of commit message).
|
||||||
const extractCommitHeader = (m: string) => parseCommitMessage(m).header;
|
const extractCommitHeader = (commit: ParsedCommitMessage) => commit.header;
|
||||||
|
|
||||||
/** Validate all commits in a provided git commit range. */
|
/** Validate all commits in a provided git commit range. */
|
||||||
export function validateCommitRange(range: string) {
|
export function validateCommitRange(range: string) {
|
||||||
/**
|
/** A list of tuples of the commit header string and a list of error messages for the commit. */
|
||||||
* A random value is used as a string to allow for a definite split point in the git log result.
|
|
||||||
*/
|
|
||||||
const randomValueSeparator = `${Math.random()}`;
|
|
||||||
/**
|
|
||||||
* Custom git log format that provides the commit header and body, separated as expected with the
|
|
||||||
* custom separator as the trailing value.
|
|
||||||
*/
|
|
||||||
const gitLogFormat = `%s%n%n%b${randomValueSeparator}`;
|
|
||||||
/**
|
|
||||||
* A list of tuples containing a commit header string and the list of error messages for the
|
|
||||||
* commit.
|
|
||||||
*/
|
|
||||||
const errors: [commitHeader: string, errors: string[]][] = [];
|
const errors: [commitHeader: string, errors: string[]][] = [];
|
||||||
|
/** A list of parsed commit messages from the range. */
|
||||||
// Retrieve the commits in the provided range.
|
const commits = parseCommitMessagesForRange(range);
|
||||||
const result = exec(`git log --reverse --format=${gitLogFormat} ${range}`);
|
|
||||||
if (result.code) {
|
|
||||||
throw new Error(`Failed to get all commits in the range: \n ${result.stderr}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separate the commits from a single string into individual commits
|
|
||||||
const commits = result.split(randomValueSeparator).map(l => l.trim()).filter(line => !!line);
|
|
||||||
|
|
||||||
info(`Examining ${commits.length} commit(s) in the provided range: ${range}`);
|
info(`Examining ${commits.length} commit(s) in the provided range: ${range}`);
|
||||||
|
|
||||||
// Check each commit in the commit range. Commits are allowed to be fixup commits for other
|
/**
|
||||||
// commits in the provided commit range.
|
* Whether all commits in the range are valid, commits are allowed to be fixup commits for other
|
||||||
const allCommitsInRangeValid = commits.every((m, i) => {
|
* commits in the provided commit range.
|
||||||
|
*/
|
||||||
|
const allCommitsInRangeValid = commits.every((commit, i) => {
|
||||||
const options: ValidateCommitMessageOptions = {
|
const options: ValidateCommitMessageOptions = {
|
||||||
disallowSquash: true,
|
disallowSquash: true,
|
||||||
nonFixupCommitHeaders: isNonFixup(m) ?
|
nonFixupCommitHeaders: isNonFixup(commit) ?
|
||||||
undefined :
|
undefined :
|
||||||
commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader)
|
commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader)
|
||||||
};
|
};
|
||||||
const {valid, errors: localErrors, commit} = validateCommitMessage(m, options);
|
const {valid, errors: localErrors} = validateCommitMessage(commit, options);
|
||||||
if (localErrors.length) {
|
if (localErrors.length) {
|
||||||
errors.push([commit.header, localErrors]);
|
errors.push([commit.header, localErrors]);
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,10 @@ const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
|||||||
|
|
||||||
/** 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(
|
||||||
commitMsg: string, options: ValidateCommitMessageOptions = {}): ValidateCommitMessageResult {
|
commitMsg: string|ParsedCommitMessage,
|
||||||
|
options: ValidateCommitMessageOptions = {}): ValidateCommitMessageResult {
|
||||||
const config = getCommitMessageConfig().commitMessage;
|
const config = getCommitMessageConfig().commitMessage;
|
||||||
const commit = parseCommitMessage(commitMsg);
|
const commit = typeof commitMsg === 'string' ? parseCommitMessage(commitMsg) : commitMsg;
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
/** Perform the validation checks against the parsed commit. */
|
/** Perform the validation checks against the parsed commit. */
|
||||||
|
@ -1783,7 +1783,7 @@ const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
|||||||
/** 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;
|
||||||
const commit = parseCommitMessage(commitMsg);
|
const commit = typeof commitMsg === 'string' ? parseCommitMessage(commitMsg) : commitMsg;
|
||||||
const errors = [];
|
const errors = [];
|
||||||
/** Perform the validation checks against the parsed commit. */
|
/** Perform the validation checks against the parsed commit. */
|
||||||
function validateCommitAndCollectErrors() {
|
function validateCommitAndCollectErrors() {
|
||||||
@ -1978,44 +1978,61 @@ const ValidateFileModule = {
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
// Whether the provided commit is a fixup commit.
|
/** Retrieve and parse each commit message in a provide range. */
|
||||||
const isNonFixup = (m) => !parseCommitMessage(m).isFixup;
|
function parseCommitMessagesForRange(range) {
|
||||||
// Extracts commit header (first line of commit message).
|
/** A random number used as a split point in the git log result. */
|
||||||
const extractCommitHeader = (m) => parseCommitMessage(m).header;
|
|
||||||
/** Validate all commits in a provided git commit range. */
|
|
||||||
function validateCommitRange(range) {
|
|
||||||
/**
|
|
||||||
* A random value is used as a string to allow for a definite split point in the git log result.
|
|
||||||
*/
|
|
||||||
const randomValueSeparator = `${Math.random()}`;
|
const randomValueSeparator = `${Math.random()}`;
|
||||||
/**
|
/**
|
||||||
* Custom git log format that provides the commit header and body, separated as expected with the
|
* Custom git log format that provides the commit header and body, separated as expected with the
|
||||||
* custom separator as the trailing value.
|
* custom separator as the trailing value.
|
||||||
*/
|
*/
|
||||||
const gitLogFormat = `%s%n%n%b${randomValueSeparator}`;
|
const gitLogFormat = `%s%n%n%b${randomValueSeparator}`;
|
||||||
/**
|
|
||||||
* A list of tuples containing a commit header string and the list of error messages for the
|
|
||||||
* commit.
|
|
||||||
*/
|
|
||||||
const errors = [];
|
|
||||||
// Retrieve the commits in the provided range.
|
// Retrieve the commits in the provided range.
|
||||||
const result = exec(`git log --reverse --format=${gitLogFormat} ${range}`);
|
const result = exec(`git log --reverse --format=${gitLogFormat} ${range}`);
|
||||||
if (result.code) {
|
if (result.code) {
|
||||||
throw new Error(`Failed to get all commits in the range: \n ${result.stderr}`);
|
throw new Error(`Failed to get all commits in the range:\n ${result.stderr}`);
|
||||||
}
|
}
|
||||||
// Separate the commits from a single string into individual commits
|
return result
|
||||||
const commits = result.split(randomValueSeparator).map(l => l.trim()).filter(line => !!line);
|
// Separate the commits from a single string into individual commits.
|
||||||
|
.split(randomValueSeparator)
|
||||||
|
// Remove extra space before and after each commit message.
|
||||||
|
.map(l => l.trim())
|
||||||
|
// Remove any superfluous lines which remain from the split.
|
||||||
|
.filter(line => !!line)
|
||||||
|
// Parse each commit message.
|
||||||
|
.map(commit => parseCommitMessage(commit));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google LLC All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
// Whether the provided commit is a fixup commit.
|
||||||
|
const isNonFixup = (commit) => !commit.isFixup;
|
||||||
|
// Extracts commit header (first line of commit message).
|
||||||
|
const extractCommitHeader = (commit) => commit.header;
|
||||||
|
/** Validate all commits in a provided git commit range. */
|
||||||
|
function validateCommitRange(range) {
|
||||||
|
/** A list of tuples of the commit header string and a list of error messages for the commit. */
|
||||||
|
const errors = [];
|
||||||
|
/** A list of parsed commit messages from the range. */
|
||||||
|
const commits = parseCommitMessagesForRange(range);
|
||||||
info(`Examining ${commits.length} commit(s) in the provided range: ${range}`);
|
info(`Examining ${commits.length} commit(s) in the provided range: ${range}`);
|
||||||
// Check each commit in the commit range. Commits are allowed to be fixup commits for other
|
/**
|
||||||
// commits in the provided commit range.
|
* Whether all commits in the range are valid, commits are allowed to be fixup commits for other
|
||||||
const allCommitsInRangeValid = commits.every((m, i) => {
|
* commits in the provided commit range.
|
||||||
|
*/
|
||||||
|
const allCommitsInRangeValid = commits.every((commit, i) => {
|
||||||
const options = {
|
const options = {
|
||||||
disallowSquash: true,
|
disallowSquash: true,
|
||||||
nonFixupCommitHeaders: isNonFixup(m) ?
|
nonFixupCommitHeaders: isNonFixup(commit) ?
|
||||||
undefined :
|
undefined :
|
||||||
commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader)
|
commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader)
|
||||||
};
|
};
|
||||||
const { valid, errors: localErrors, commit } = validateCommitMessage(m, options);
|
const { valid, errors: localErrors } = validateCommitMessage(commit, options);
|
||||||
if (localErrors.length) {
|
if (localErrors.length) {
|
||||||
errors.push([commit.header, localErrors]);
|
errors.push([commit.header, localErrors]);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user