refactor(dev-infra): extract the commit message parsing function into its own file (#38429)
Extracts the commit message parsing function into its own file. PR Close #38429
This commit is contained in:
parent
5f2e475abf
commit
8366effeec
|
@ -6,6 +6,7 @@ ts_library(
|
|||
srcs = [
|
||||
"cli.ts",
|
||||
"config.ts",
|
||||
"parse.ts",
|
||||
"validate.ts",
|
||||
"validate-file.ts",
|
||||
"validate-range.ts",
|
||||
|
@ -23,9 +24,12 @@ ts_library(
|
|||
)
|
||||
|
||||
ts_library(
|
||||
name = "validate-test",
|
||||
name = "test_lib",
|
||||
testonly = True,
|
||||
srcs = ["validate.spec.ts"],
|
||||
srcs = [
|
||||
"parse.spec.ts",
|
||||
"validate.spec.ts",
|
||||
],
|
||||
deps = [
|
||||
":commit-message",
|
||||
"//dev-infra/utils",
|
||||
|
@ -40,7 +44,6 @@ jasmine_node_test(
|
|||
name = "test",
|
||||
bootstrap = ["//tools/testing:node_no_angular_es5"],
|
||||
deps = [
|
||||
":commit-message",
|
||||
":validate-test",
|
||||
"test_lib",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @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 {parseCommitMessage, ParsedCommitMessage} from './parse';
|
||||
|
||||
|
||||
const commitValues = {
|
||||
prefix: '',
|
||||
type: 'fix',
|
||||
scope: 'changed-area',
|
||||
summary: 'This is a short summary of the change',
|
||||
body: 'This is a longer description of the change Closes #1',
|
||||
};
|
||||
|
||||
function buildCommitMessage(params = {}) {
|
||||
const {prefix, type, scope, summary, body} = {...commitValues, ...params};
|
||||
return `${prefix}${type}${scope ? '(' + scope + ')' : ''}: ${summary}\n\n${body}`;
|
||||
}
|
||||
|
||||
|
||||
describe('commit message parsing:', () => {
|
||||
it('parses the scope', () => {
|
||||
const message = buildCommitMessage();
|
||||
expect(parseCommitMessage(message).scope).toBe(commitValues.scope);
|
||||
});
|
||||
|
||||
it('parses the type', () => {
|
||||
const message = buildCommitMessage();
|
||||
expect(parseCommitMessage(message).type).toBe(commitValues.type);
|
||||
});
|
||||
|
||||
it('parses the header', () => {
|
||||
const message = buildCommitMessage();
|
||||
expect(parseCommitMessage(message).header)
|
||||
.toBe(`${commitValues.type}(${commitValues.scope}): ${commitValues.summary}`);
|
||||
});
|
||||
|
||||
it('parses the body', () => {
|
||||
const message = buildCommitMessage();
|
||||
expect(parseCommitMessage(message).body).toBe(commitValues.body);
|
||||
});
|
||||
|
||||
it('parses the body without Github linking', () => {
|
||||
const body = 'This has linking\nCloses #1';
|
||||
const message = buildCommitMessage({body});
|
||||
expect(parseCommitMessage(message).bodyWithoutLinking).toBe('This has linking\n');
|
||||
});
|
||||
|
||||
it('parses the subject', () => {
|
||||
const message = buildCommitMessage();
|
||||
expect(parseCommitMessage(message).subject).toBe(commitValues.summary);
|
||||
});
|
||||
|
||||
it('identifies if a commit is a fixup', () => {
|
||||
const message1 = buildCommitMessage();
|
||||
expect(parseCommitMessage(message1).isFixup).toBe(false);
|
||||
|
||||
const message2 = buildCommitMessage({prefix: 'fixup! '});
|
||||
expect(parseCommitMessage(message2).isFixup).toBe(true);
|
||||
});
|
||||
|
||||
it('identifies if a commit is a revert', () => {
|
||||
const message1 = buildCommitMessage();
|
||||
expect(parseCommitMessage(message1).isRevert).toBe(false);
|
||||
|
||||
const message2 = buildCommitMessage({prefix: 'revert: '});
|
||||
expect(parseCommitMessage(message2).isRevert).toBe(true);
|
||||
|
||||
const message3 = buildCommitMessage({prefix: 'revert '});
|
||||
expect(parseCommitMessage(message3).isRevert).toBe(true);
|
||||
});
|
||||
|
||||
it('identifies if a commit is a squash', () => {
|
||||
const message1 = buildCommitMessage();
|
||||
expect(parseCommitMessage(message1).isSquash).toBe(false);
|
||||
|
||||
const message2 = buildCommitMessage({prefix: 'squash! '});
|
||||
expect(parseCommitMessage(message2).isSquash).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/** A parsed commit message. */
|
||||
export interface ParsedCommitMessage {
|
||||
header: string;
|
||||
body: string;
|
||||
bodyWithoutLinking: string;
|
||||
type: string;
|
||||
scope: string;
|
||||
subject: string;
|
||||
isFixup: boolean;
|
||||
isSquash: boolean;
|
||||
isRevert: boolean;
|
||||
}
|
||||
|
||||
/** Regex determining if a commit is a fixup. */
|
||||
const FIXUP_PREFIX_RE = /^fixup! /i;
|
||||
/** Regex finding all github keyword links. */
|
||||
const GITHUB_LINKING_RE = /((closed?s?)|(fix(es)?(ed)?)|(resolved?s?))\s\#(\d+)/ig;
|
||||
/** Regex determining if a commit is a squash. */
|
||||
const SQUASH_PREFIX_RE = /^squash! /i;
|
||||
/** Regex determining if a commit is a revert. */
|
||||
const REVERT_PREFIX_RE = /^revert:? /i;
|
||||
/** Regex determining the scope of a commit if provided. */
|
||||
const TYPE_SCOPE_RE = /^(\w+)(?:\(([^)]+)\))?\:\s(.+)$/;
|
||||
/** Regex determining the entire header line of the commit. */
|
||||
const COMMIT_HEADER_RE = /^(.*)/i;
|
||||
/** Regex determining the body of the commit. */
|
||||
const COMMIT_BODY_RE = /^.*\n\n([\s\S]*)$/;
|
||||
|
||||
/** Parse a full commit message into its composite parts. */
|
||||
export function parseCommitMessage(commitMsg: string): ParsedCommitMessage {
|
||||
let header = '';
|
||||
let body = '';
|
||||
let bodyWithoutLinking = '';
|
||||
let type = '';
|
||||
let scope = '';
|
||||
let subject = '';
|
||||
|
||||
if (COMMIT_HEADER_RE.test(commitMsg)) {
|
||||
header = COMMIT_HEADER_RE.exec(commitMsg)![1]
|
||||
.replace(FIXUP_PREFIX_RE, '')
|
||||
.replace(SQUASH_PREFIX_RE, '');
|
||||
}
|
||||
if (COMMIT_BODY_RE.test(commitMsg)) {
|
||||
body = COMMIT_BODY_RE.exec(commitMsg)![1];
|
||||
bodyWithoutLinking = body.replace(GITHUB_LINKING_RE, '');
|
||||
}
|
||||
|
||||
if (TYPE_SCOPE_RE.test(header)) {
|
||||
const parsedCommitHeader = TYPE_SCOPE_RE.exec(header)!;
|
||||
type = parsedCommitHeader[1];
|
||||
scope = parsedCommitHeader[2];
|
||||
subject = parsedCommitHeader[3];
|
||||
}
|
||||
return {
|
||||
header,
|
||||
body,
|
||||
bodyWithoutLinking,
|
||||
type,
|
||||
scope,
|
||||
subject,
|
||||
isFixup: FIXUP_PREFIX_RE.test(commitMsg),
|
||||
isSquash: SQUASH_PREFIX_RE.test(commitMsg),
|
||||
isRevert: REVERT_PREFIX_RE.test(commitMsg),
|
||||
};
|
||||
}
|
|
@ -8,7 +8,8 @@
|
|||
import {info} from '../utils/console';
|
||||
import {exec} from '../utils/shelljs';
|
||||
|
||||
import {parseCommitMessage, validateCommitMessage, ValidateCommitMessageOptions} from './validate';
|
||||
import {parseCommitMessage} from './parse';
|
||||
import {validateCommitMessage, ValidateCommitMessageOptions} from './validate';
|
||||
|
||||
// Whether the provided commit is a fixup commit.
|
||||
const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import {error} from '../utils/console';
|
||||
|
||||
import {getCommitMessageConfig} from './config';
|
||||
import {parseCommitMessage} from './parse';
|
||||
|
||||
/** Options for commit message validation. */
|
||||
export interface ValidateCommitMessageOptions {
|
||||
|
@ -15,53 +16,9 @@ export interface ValidateCommitMessageOptions {
|
|||
nonFixupCommitHeaders?: string[];
|
||||
}
|
||||
|
||||
const FIXUP_PREFIX_RE = /^fixup! /i;
|
||||
const GITHUB_LINKING_RE = /((closed?s?)|(fix(es)?(ed)?)|(resolved?s?))\s\#(\d+)/ig;
|
||||
const SQUASH_PREFIX_RE = /^squash! /i;
|
||||
const REVERT_PREFIX_RE = /^revert:? /i;
|
||||
const TYPE_SCOPE_RE = /^(\w+)(?:\(([^)]+)\))?\:\s(.+)$/;
|
||||
const COMMIT_HEADER_RE = /^(.*)/i;
|
||||
const COMMIT_BODY_RE = /^.*\n\n([\s\S]*)$/;
|
||||
/** Regex matching a URL for an entire commit body line. */
|
||||
const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
||||
|
||||
/** Parse a full commit message into its composite parts. */
|
||||
export function parseCommitMessage(commitMsg: string) {
|
||||
let header = '';
|
||||
let body = '';
|
||||
let bodyWithoutLinking = '';
|
||||
let type = '';
|
||||
let scope = '';
|
||||
let subject = '';
|
||||
|
||||
if (COMMIT_HEADER_RE.test(commitMsg)) {
|
||||
header = COMMIT_HEADER_RE.exec(commitMsg)![1]
|
||||
.replace(FIXUP_PREFIX_RE, '')
|
||||
.replace(SQUASH_PREFIX_RE, '');
|
||||
}
|
||||
if (COMMIT_BODY_RE.test(commitMsg)) {
|
||||
body = COMMIT_BODY_RE.exec(commitMsg)![1];
|
||||
bodyWithoutLinking = body.replace(GITHUB_LINKING_RE, '');
|
||||
}
|
||||
|
||||
if (TYPE_SCOPE_RE.test(header)) {
|
||||
const parsedCommitHeader = TYPE_SCOPE_RE.exec(header)!;
|
||||
type = parsedCommitHeader[1];
|
||||
scope = parsedCommitHeader[2];
|
||||
subject = parsedCommitHeader[3];
|
||||
}
|
||||
return {
|
||||
header,
|
||||
body,
|
||||
bodyWithoutLinking,
|
||||
type,
|
||||
scope,
|
||||
subject,
|
||||
isFixup: FIXUP_PREFIX_RE.test(commitMsg),
|
||||
isSquash: SQUASH_PREFIX_RE.test(commitMsg),
|
||||
isRevert: REVERT_PREFIX_RE.test(commitMsg),
|
||||
};
|
||||
}
|
||||
|
||||
/** Validate a commit message against using the local repo's config. */
|
||||
export function validateCommitMessage(
|
||||
commitMsg: string, options: ValidateCommitMessageOptions = {}) {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {PullsListCommitsResponse, PullsMergeParams} from '@octokit/rest';
|
||||
import {prompt} from 'inquirer';
|
||||
|
||||
import {parseCommitMessage} from '../../../commit-message/validate';
|
||||
import {parseCommitMessage} from '../../../commit-message/parse';
|
||||
import {GitClient} from '../../../utils/git';
|
||||
import {GithubApiMergeMethod} from '../config';
|
||||
import {PullRequestFailure} from '../failures';
|
||||
|
|
Loading…
Reference in New Issue