feat(dev-infra): update commit-message functions to properly type commits from git log (#41458)

For commits from git log entries additional fields are available such as the reference
hash and author name, update the utility functions in commit-message to include the
parsed fields.  Additionally define, per commit message type, whether to include the
commit in a release notes entry.

PR Close #41458
This commit is contained in:
Joey Perrott 2021-04-01 16:03:38 -07:00 committed by atscott
parent ba3344ddbe
commit c63d00e5b0
4 changed files with 60 additions and 8 deletions

View File

@ -38,11 +38,17 @@ export enum ScopeRequirement {
Forbidden, Forbidden,
} }
export enum ReleaseNotesLevel {
Hidden,
Visible,
}
/** A commit type */ /** A commit type */
export interface CommitType { export interface CommitType {
description: string; description: string;
name: string; name: string;
scope: ScopeRequirement; scope: ScopeRequirement;
releaseNotesLevel: ReleaseNotesLevel;
} }
/** The valid commit types for Angular commit messages. */ /** The valid commit types for Angular commit messages. */
@ -51,45 +57,54 @@ export const COMMIT_TYPES: {[key: string]: CommitType} = {
name: 'build', name: 'build',
description: 'Changes to local repository build system and tooling', description: 'Changes to local repository build system and tooling',
scope: ScopeRequirement.Optional, scope: ScopeRequirement.Optional,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
ci: { ci: {
name: 'ci', name: 'ci',
description: 'Changes to CI configuration and CI specific tooling', description: 'Changes to CI configuration and CI specific tooling',
scope: ScopeRequirement.Forbidden, scope: ScopeRequirement.Forbidden,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
docs: { docs: {
name: 'docs', name: 'docs',
description: 'Changes which exclusively affects documentation.', description: 'Changes which exclusively affects documentation.',
scope: ScopeRequirement.Optional, scope: ScopeRequirement.Optional,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
feat: { feat: {
name: 'feat', name: 'feat',
description: 'Creates a new feature', description: 'Creates a new feature',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Visible,
}, },
fix: { fix: {
name: 'fix', name: 'fix',
description: 'Fixes a previously discovered failure/bug', description: 'Fixes a previously discovered failure/bug',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Visible,
}, },
perf: { perf: {
name: 'perf', name: 'perf',
description: 'Improves performance without any change in functionality or API', description: 'Improves performance without any change in functionality or API',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Visible,
}, },
refactor: { refactor: {
name: 'refactor', name: 'refactor',
description: 'Refactor without any change in functionality or API (includes style changes)', description: 'Refactor without any change in functionality or API (includes style changes)',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
release: { release: {
name: 'release', name: 'release',
description: 'A release point in the repository', description: 'A release point in the repository',
scope: ScopeRequirement.Forbidden, scope: ScopeRequirement.Forbidden,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
test: { test: {
name: 'test', name: 'test',
description: 'Improvements or corrections made to the project\'s test suite', description: 'Improvements or corrections made to the project\'s test suite',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
}; };

View File

@ -41,6 +41,13 @@ export interface Commit {
isRevert: boolean; isRevert: boolean;
} }
/** A parsed commit which originated from a Git Log entry */
export interface CommitFromGitLog extends Commit {
author: string;
hash: string;
shortHash: string;
}
/** /**
* A list of tuples expressing the fields to extract from each commit log entry. The tuple contains * A list of tuples expressing the fields to extract from each commit log entry. The tuple contains
* two values, the first is the key for the property and the second is the template shortcut for the * two values, the first is the key for the property and the second is the template shortcut for the
@ -107,9 +114,16 @@ const parseOptions: Options&{notesPattern: (keywords: string) => RegExp} = {
notesPattern: (keywords: string) => new RegExp(`(${keywords})(?:: ?)(.*)`), notesPattern: (keywords: string) => new RegExp(`(${keywords})(?:: ?)(.*)`),
}; };
/** Parse a commit message into its composite parts. */
export const parseCommitMessage: (fullText: string) => Commit = parseInternal;
/** Parse a commit message from a git log entry into its composite parts. */
export const parseCommitFromGitLog: (fullText: Buffer) => CommitFromGitLog = parseInternal;
/** Parse a full commit message into its composite parts. */ /** Parse a full commit message into its composite parts. */
export function parseCommitMessage(fullText: string|Buffer): Commit { function parseInternal(fullText: string): Commit;
function parseInternal(fullText: Buffer): CommitFromGitLog;
function parseInternal(fullText: string|Buffer): CommitFromGitLog|Commit {
// Ensure the fullText symbol is a `string`, even if a Buffer was provided. // Ensure the fullText symbol is a `string`, even if a Buffer was provided.
fullText = fullText.toString(); fullText = fullText.toString();
/** The commit message text with the fixup and squash markers stripped out. */ /** The commit message text with the fixup and squash markers stripped out. */
@ -148,5 +162,8 @@ export function parseCommitMessage(fullText: string|Buffer): Commit {
isFixup: FIXUP_PREFIX_RE.test(fullText), isFixup: FIXUP_PREFIX_RE.test(fullText),
isSquash: SQUASH_PREFIX_RE.test(fullText), isSquash: SQUASH_PREFIX_RE.test(fullText),
isRevert: REVERT_PREFIX_RE.test(fullText), isRevert: REVERT_PREFIX_RE.test(fullText),
author: commit.author || undefined,
hash: commit.hash || undefined,
shortHash: commit.shortHash || undefined,
}; };
} }

View File

@ -7,7 +7,7 @@
*/ */
import * as gitCommits_ from 'git-raw-commits'; import * as gitCommits_ from 'git-raw-commits';
import {Commit, gitLogFormatForParsing, parseCommitMessage} from './parse'; import {CommitFromGitLog, gitLogFormatForParsing, parseCommitFromGitLog} from './parse';
// Set `gitCommits` as this imported value to address "Cannot call a namespace" error. // Set `gitCommits` as this imported value to address "Cannot call a namespace" error.
const gitCommits = gitCommits_; const gitCommits = gitCommits_;
@ -16,16 +16,16 @@ const gitCommits = gitCommits_;
/** /**
* Find all commits within the given range and return an object describing those. * Find all commits within the given range and return an object describing those.
*/ */
export function getCommitsInRange(from: string, to: string = 'HEAD'): Promise<Commit[]> { export function getCommitsInRange(from: string, to: string = 'HEAD'): Promise<CommitFromGitLog[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
/** List of parsed commit objects. */ /** List of parsed commit objects. */
const commits: Commit[] = []; const commits: CommitFromGitLog[] = [];
/** Stream of raw git commit strings in the range provided. */ /** Stream of raw git commit strings in the range provided. */
const commitStream = gitCommits({from, to, format: gitLogFormatForParsing}); const commitStream = gitCommits({from, to, format: gitLogFormatForParsing});
// Accumulate the parsed commits for each commit from the Readable stream into an array, then // Accumulate the parsed commits for each commit from the Readable stream into an array, then
// resolve the promise with the array when the Readable stream ends. // resolve the promise with the array when the Readable stream ends.
commitStream.on('data', (commit: Buffer) => commits.push(parseCommitMessage(commit))); commitStream.on('data', (commit: Buffer) => commits.push(parseCommitFromGitLog(commit)));
commitStream.on('error', (err: Error) => reject(err)); commitStream.on('error', (err: Error) => reject(err));
commitStream.on('end', () => resolve(commits)); commitStream.on('end', () => resolve(commits));
}); });

View File

@ -1670,52 +1670,66 @@ var ScopeRequirement;
ScopeRequirement[ScopeRequirement["Optional"] = 1] = "Optional"; ScopeRequirement[ScopeRequirement["Optional"] = 1] = "Optional";
ScopeRequirement[ScopeRequirement["Forbidden"] = 2] = "Forbidden"; ScopeRequirement[ScopeRequirement["Forbidden"] = 2] = "Forbidden";
})(ScopeRequirement || (ScopeRequirement = {})); })(ScopeRequirement || (ScopeRequirement = {}));
var ReleaseNotesLevel;
(function (ReleaseNotesLevel) {
ReleaseNotesLevel[ReleaseNotesLevel["Hidden"] = 0] = "Hidden";
ReleaseNotesLevel[ReleaseNotesLevel["Visible"] = 1] = "Visible";
})(ReleaseNotesLevel || (ReleaseNotesLevel = {}));
/** The valid commit types for Angular commit messages. */ /** The valid commit types for Angular commit messages. */
const COMMIT_TYPES = { const COMMIT_TYPES = {
build: { build: {
name: 'build', name: 'build',
description: 'Changes to local repository build system and tooling', description: 'Changes to local repository build system and tooling',
scope: ScopeRequirement.Optional, scope: ScopeRequirement.Optional,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
ci: { ci: {
name: 'ci', name: 'ci',
description: 'Changes to CI configuration and CI specific tooling', description: 'Changes to CI configuration and CI specific tooling',
scope: ScopeRequirement.Forbidden, scope: ScopeRequirement.Forbidden,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
docs: { docs: {
name: 'docs', name: 'docs',
description: 'Changes which exclusively affects documentation.', description: 'Changes which exclusively affects documentation.',
scope: ScopeRequirement.Optional, scope: ScopeRequirement.Optional,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
feat: { feat: {
name: 'feat', name: 'feat',
description: 'Creates a new feature', description: 'Creates a new feature',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Visible,
}, },
fix: { fix: {
name: 'fix', name: 'fix',
description: 'Fixes a previously discovered failure/bug', description: 'Fixes a previously discovered failure/bug',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Visible,
}, },
perf: { perf: {
name: 'perf', name: 'perf',
description: 'Improves performance without any change in functionality or API', description: 'Improves performance without any change in functionality or API',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Visible,
}, },
refactor: { refactor: {
name: 'refactor', name: 'refactor',
description: 'Refactor without any change in functionality or API (includes style changes)', description: 'Refactor without any change in functionality or API (includes style changes)',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
release: { release: {
name: 'release', name: 'release',
description: 'A release point in the repository', description: 'A release point in the repository',
scope: ScopeRequirement.Forbidden, scope: ScopeRequirement.Forbidden,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
test: { test: {
name: 'test', name: 'test',
description: 'Improvements or corrections made to the project\'s test suite', description: 'Improvements or corrections made to the project\'s test suite',
scope: ScopeRequirement.Required, scope: ScopeRequirement.Required,
releaseNotesLevel: ReleaseNotesLevel.Hidden,
}, },
}; };
@ -1790,8 +1804,11 @@ const parseOptions = {
noteKeywords: [NoteSections.BREAKING_CHANGE, NoteSections.DEPRECATED], noteKeywords: [NoteSections.BREAKING_CHANGE, NoteSections.DEPRECATED],
notesPattern: (keywords) => new RegExp(`(${keywords})(?:: ?)(.*)`), notesPattern: (keywords) => new RegExp(`(${keywords})(?:: ?)(.*)`),
}; };
/** Parse a full commit message into its composite parts. */ /** Parse a commit message into its composite parts. */
function parseCommitMessage(fullText) { const parseCommitMessage = parseInternal;
/** Parse a commit message from a git log entry into its composite parts. */
const parseCommitFromGitLog = parseInternal;
function parseInternal(fullText) {
// Ensure the fullText symbol is a `string`, even if a Buffer was provided. // Ensure the fullText symbol is a `string`, even if a Buffer was provided.
fullText = fullText.toString(); fullText = fullText.toString();
/** The commit message text with the fixup and squash markers stripped out. */ /** The commit message text with the fixup and squash markers stripped out. */
@ -1828,6 +1845,9 @@ function parseCommitMessage(fullText) {
isFixup: FIXUP_PREFIX_RE.test(fullText), isFixup: FIXUP_PREFIX_RE.test(fullText),
isSquash: SQUASH_PREFIX_RE.test(fullText), isSquash: SQUASH_PREFIX_RE.test(fullText),
isRevert: REVERT_PREFIX_RE.test(fullText), isRevert: REVERT_PREFIX_RE.test(fullText),
author: commit.author || undefined,
hash: commit.hash || undefined,
shortHash: commit.shortHash || undefined,
}; };
} }
@ -2083,7 +2103,7 @@ function getCommitsInRange(from, to = 'HEAD') {
const commitStream = gitCommits({ from, to, format: gitLogFormatForParsing }); const commitStream = gitCommits({ from, to, format: gitLogFormatForParsing });
// Accumulate the parsed commits for each commit from the Readable stream into an array, then // Accumulate the parsed commits for each commit from the Readable stream into an array, then
// resolve the promise with the array when the Readable stream ends. // resolve the promise with the array when the Readable stream ends.
commitStream.on('data', (commit) => commits.push(parseCommitMessage(commit))); commitStream.on('data', (commit) => commits.push(parseCommitFromGitLog(commit)));
commitStream.on('error', (err) => reject(err)); commitStream.on('error', (err) => reject(err));
commitStream.on('end', () => resolve(commits)); commitStream.on('end', () => resolve(commits));
}); });