From d192c87f6aba0c564ed4882712129733e29f1a41 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 14 Sep 2020 13:21:31 -0700 Subject: [PATCH] refactor(dev-infra): refactor commit-message files (#38845) Refactor the commit-message files to be consistent with how other ng-dev tooling is structured. PR Close #38845 --- dev-infra/commit-message/BUILD.bazel | 22 +--- dev-infra/commit-message/cli.ts | 108 ++---------------- .../commit-message/commit-message-source.ts | 13 +++ dev-infra/commit-message/config.ts | 1 + .../restore-commit-message/cli.ts | 51 +++++++++ .../restore-commit-message.ts | 8 +- dev-infra/commit-message/validate-file/cli.ts | 62 ++++++++++ .../{ => validate-file}/validate-file.ts | 8 +- .../commit-message/validate-range/cli.ts | 50 ++++++++ .../{ => validate-range}/validate-range.ts | 8 +- dev-infra/commit-message/wizard/cli.ts | 54 +++++++++ .../commit-message/{ => wizard}/wizard.ts | 14 +-- 12 files changed, 261 insertions(+), 138 deletions(-) create mode 100644 dev-infra/commit-message/commit-message-source.ts create mode 100644 dev-infra/commit-message/restore-commit-message/cli.ts rename dev-infra/commit-message/{ => restore-commit-message}/restore-commit-message.ts (84%) create mode 100644 dev-infra/commit-message/validate-file/cli.ts rename dev-infra/commit-message/{ => validate-file}/validate-file.ts (87%) create mode 100644 dev-infra/commit-message/validate-range/cli.ts rename dev-infra/commit-message/{ => validate-range}/validate-range.ts (93%) create mode 100644 dev-infra/commit-message/wizard/cli.ts rename dev-infra/commit-message/{ => wizard}/wizard.ts (73%) diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel index 18ad90463a..29e384d6c1 100644 --- a/dev-infra/commit-message/BUILD.bazel +++ b/dev-infra/commit-message/BUILD.bazel @@ -3,18 +3,10 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "commit-message", - srcs = [ - "builder.ts", - "cli.ts", - "commit-message-draft.ts", - "config.ts", - "parse.ts", - "restore-commit-message.ts", - "validate.ts", - "validate-file.ts", - "validate-range.ts", - "wizard.ts", - ], + srcs = glob( + ["**/*.ts"], + exclude = ["**/*.spec.ts"], + ), module_name = "@angular/dev-infra-private/commit-message", visibility = ["//dev-infra:__subpackages__"], deps = [ @@ -32,11 +24,7 @@ ts_library( ts_library( name = "test_lib", testonly = True, - srcs = [ - "builder.spec.ts", - "parse.spec.ts", - "validate.spec.ts", - ], + srcs = glob(["**/*.spec.ts"]), deps = [ ":commit-message", "//dev-infra/utils", diff --git a/dev-infra/commit-message/cli.ts b/dev-infra/commit-message/cli.ts index fb1581ef08..9230b03d0c 100644 --- a/dev-infra/commit-message/cli.ts +++ b/dev-infra/commit-message/cli.ts @@ -6,112 +6,20 @@ * found in the LICENSE file at https://angular.io/license */ import * as yargs from 'yargs'; -import {getUserConfig} from '../utils/config'; -import {info} from '../utils/console'; - -import {restoreCommitMessage} from './restore-commit-message'; -import {validateFile} from './validate-file'; -import {validateCommitRange} from './validate-range'; -import {runWizard} from './wizard'; +import {RestoreCommitMessageModule} from './restore-commit-message/cli'; +import {ValidateFileModule} from './validate-file/cli'; +import {ValidateRangeModule} from './validate-range/cli'; +import {WizardModule} from './wizard/cli'; /** Build the parser for the commit-message commands. */ export function buildCommitMessageParser(localYargs: yargs.Argv) { return localYargs.help() .strict() - .command( - 'restore-commit-message-draft', false, - args => { - return args.option('file-env-variable', { - type: 'string', - array: true, - conflicts: ['file'], - required: true, - description: - 'The key for the environment variable which holds the arguments for the\n' + - 'prepare-commit-msg hook as described here:\n' + - 'https://git-scm.com/docs/githooks#_prepare_commit_msg', - coerce: arg => { - const [file, source] = (process.env[arg] || '').split(' '); - if (!file) { - throw new Error(`Provided environment variable "${arg}" was not found.`); - } - return [file, source]; - }, - }); - }, - args => { - restoreCommitMessage(args['file-env-variable'][0], args['file-env-variable'][1] as any); - }) - .command( - 'wizard [source] [commitSha]', '', ((args: any) => { - return args - .positional( - 'filePath', - {description: 'The file path to write the generated commit message into'}) - .positional('source', { - choices: ['message', 'template', 'merge', 'squash', 'commit'], - description: 'The source of the commit message as described here: ' + - 'https://git-scm.com/docs/githooks#_prepare_commit_msg' - }) - .positional( - 'commitSha', {description: 'The commit sha if source is set to `commit`'}); - }), - async (args: any) => { - await runWizard(args); - }) - .command( - 'pre-commit-validate', 'Validate the most recent commit message', { - 'file': { - type: 'string', - conflicts: ['file-env-variable'], - description: 'The path of the commit message file.', - }, - 'file-env-variable': { - type: 'string', - conflicts: ['file'], - description: - 'The key of the environment variable for the path of the commit message file.', - coerce: arg => { - const file = process.env[arg]; - if (!file) { - throw new Error(`Provided environment variable "${arg}" was not found.`); - } - return file; - }, - }, - 'error': { - type: 'boolean', - description: - 'Whether invalid commit messages should be treated as failures rather than a warning', - default: !!getUserConfig().commitMessage?.errorOnInvalidMessage || !!process.env['CI'] - } - }, - args => { - const file = args.file || args['file-env-variable'] || '.git/COMMIT_EDITMSG'; - validateFile(file, args.error); - }) - .command( - 'validate-range', 'Validate a range of commit messages', { - 'range': { - description: 'The range of commits to check, e.g. --range abc123..xyz456', - demandOption: ' A range must be provided, e.g. --range abc123..xyz456', - type: 'string', - requiresArg: true, - }, - }, - argv => { - // If on CI, and not pull request number is provided, assume the branch - // being run on is an upstream branch. - if (process.env['CI'] && process.env['CI_PULL_REQUEST'] === 'false') { - info(`Since valid commit messages are enforced by PR linting on CI, we do not`); - info(`need to validate commit messages on CI runs on upstream branches.`); - info(); - info(`Skipping check of provided commit range`); - return; - } - validateCommitRange(argv.range); - }); + .command(RestoreCommitMessageModule) + .command(WizardModule) + .command(ValidateFileModule) + .command(ValidateRangeModule); } if (require.main == module) { diff --git a/dev-infra/commit-message/commit-message-source.ts b/dev-infra/commit-message/commit-message-source.ts new file mode 100644 index 0000000000..c8a9640e83 --- /dev/null +++ b/dev-infra/commit-message/commit-message-source.ts @@ -0,0 +1,13 @@ +/** + * @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 + */ + +/** + * The source triggering the git commit message creation. + * As described in: https://git-scm.com/docs/githooks#_prepare_commit_msg + */ +export type CommitMsgSource = 'message'|'template'|'merge'|'squash'|'commit'; diff --git a/dev-infra/commit-message/config.ts b/dev-infra/commit-message/config.ts index 1e6ac4a6e2..613c59decb 100644 --- a/dev-infra/commit-message/config.ts +++ b/dev-infra/commit-message/config.ts @@ -8,6 +8,7 @@ import {assertNoErrors, getConfig, NgDevConfig} from '../utils/config'; +/** Configuration for commit-message comands. */ export interface CommitMessageConfig { maxLineLength: number; minBodyLength: number; diff --git a/dev-infra/commit-message/restore-commit-message/cli.ts b/dev-infra/commit-message/restore-commit-message/cli.ts new file mode 100644 index 0000000000..828ca1adf9 --- /dev/null +++ b/dev-infra/commit-message/restore-commit-message/cli.ts @@ -0,0 +1,51 @@ +/** + * @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 {Arguments, Argv, CommandModule} from 'yargs'; + +import {CommitMsgSource} from '../commit-message-source'; + +import {restoreCommitMessage} from './restore-commit-message'; + +export interface RestoreCommitMessageOptions { + fileEnvVariable: string[]; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs.option('file-env-variable' as 'fileEnvVariable', { + type: 'string', + array: true, + demandOption: true, + description: 'The key for the environment variable which holds the arguments for the\n' + + 'prepare-commit-msg hook as described here:\n' + + 'https://git-scm.com/docs/githooks#_prepare_commit_msg', + coerce: arg => { + const [file, source] = (process.env[arg] || '').split(' '); + if (!file) { + throw new Error(`Provided environment variable "${arg}" was not found.`); + } + return [file, source]; + }, + }); +} + +/** Handles the command. */ +async function handler({fileEnvVariable}: Arguments) { + restoreCommitMessage(fileEnvVariable[0], fileEnvVariable[1] as CommitMsgSource); +} + +/** yargs command module describing the command. */ +export const RestoreCommitMessageModule: CommandModule<{}, RestoreCommitMessageOptions> = { + handler, + builder, + command: 'restore-commit-message-draft', + // Description: Restore a commit message draft if one has been saved from a failed commit attempt. + // No describe is defiend to hide the command from the --help. + describe: false, +}; diff --git a/dev-infra/commit-message/restore-commit-message.ts b/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts similarity index 84% rename from dev-infra/commit-message/restore-commit-message.ts rename to dev-infra/commit-message/restore-commit-message/restore-commit-message.ts index 7375ed3b65..8008728d0b 100644 --- a/dev-infra/commit-message/restore-commit-message.ts +++ b/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts @@ -8,9 +8,10 @@ import {writeFileSync} from 'fs'; -import {debug, log} from '../utils/console'; +import {debug, log} from '../../utils/console'; -import {loadCommitMessageDraft} from './commit-message-draft'; +import {loadCommitMessageDraft} from '../commit-message-draft'; +import {CommitMsgSource} from '../commit-message-source'; /** * Restore the commit message draft to the git to be used as the default commit message. @@ -18,8 +19,7 @@ import {loadCommitMessageDraft} from './commit-message-draft'; * The source provided may be one of the sources described in * https://git-scm.com/docs/githooks#_prepare_commit_msg */ -export function restoreCommitMessage( - filePath: string, source?: 'message'|'template'|'squash'|'commit') { +export function restoreCommitMessage(filePath: string, source?: CommitMsgSource) { if (!!source) { log('Skipping commit message restoration attempt'); if (source === 'message') { diff --git a/dev-infra/commit-message/validate-file/cli.ts b/dev-infra/commit-message/validate-file/cli.ts new file mode 100644 index 0000000000..602b3145cd --- /dev/null +++ b/dev-infra/commit-message/validate-file/cli.ts @@ -0,0 +1,62 @@ +/** + * @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 {Arguments, Argv, CommandModule} from 'yargs'; + +import {getUserConfig} from '../../utils/config'; + +import {validateFile} from './validate-file'; + + +export interface ValidateFileOptions { + file?: string; + fileEnvVariable?: string; + error: boolean; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs + .option('file', { + type: 'string', + conflicts: ['file-env-variable'], + description: 'The path of the commit message file.', + }) + .option('file-env-variable' as 'fileEnvVariable', { + type: 'string', + conflicts: ['file'], + description: 'The key of the environment variable for the path of the commit message file.', + coerce: (arg: string) => { + const file = process.env[arg]; + if (!file) { + throw new Error(`Provided environment variable "${arg}" was not found.`); + } + return file; + }, + }) + .option('error', { + type: 'boolean', + description: + 'Whether invalid commit messages should be treated as failures rather than a warning', + default: !!getUserConfig().commitMessage?.errorOnInvalidMessage || !!process.env['CI'] + }); +} + +/** Handles the command. */ +async function handler({error, file, fileEnvVariable}: Arguments) { + const filePath = file || fileEnvVariable || '.git/COMMIT_EDITMSG'; + validateFile(filePath, error); +} + +/** yargs command module describing the command. */ +export const ValidateFileModule: CommandModule<{}, ValidateFileOptions> = { + handler, + builder, + command: 'pre-commit-validate', + describe: 'Validate the most recent commit message', +}; diff --git a/dev-infra/commit-message/validate-file.ts b/dev-infra/commit-message/validate-file/validate-file.ts similarity index 87% rename from dev-infra/commit-message/validate-file.ts rename to dev-infra/commit-message/validate-file/validate-file.ts index 87973835ca..459fa52fa9 100644 --- a/dev-infra/commit-message/validate-file.ts +++ b/dev-infra/commit-message/validate-file/validate-file.ts @@ -8,11 +8,11 @@ import {readFileSync} from 'fs'; import {resolve} from 'path'; -import {getRepoBaseDir, getUserConfig} from '../utils/config'; -import {error, green, info, log, red, yellow} from '../utils/console'; +import {getRepoBaseDir} from '../../utils/config'; +import {error, green, info, log, red, yellow} from '../../utils/console'; -import {deleteCommitMessageDraft, saveCommitMessageDraft} from './commit-message-draft'; -import {printValidationErrors, validateCommitMessage} from './validate'; +import {deleteCommitMessageDraft, saveCommitMessageDraft} from '../commit-message-draft'; +import {printValidationErrors, validateCommitMessage} from '../validate'; /** Validate commit message at the provided file path. */ export function validateFile(filePath: string, isErrorMode: boolean) { diff --git a/dev-infra/commit-message/validate-range/cli.ts b/dev-infra/commit-message/validate-range/cli.ts new file mode 100644 index 0000000000..6eea3ff95e --- /dev/null +++ b/dev-infra/commit-message/validate-range/cli.ts @@ -0,0 +1,50 @@ +/** + * @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 {Arguments, Argv, CommandModule} from 'yargs'; + +import {info} from '../../utils/console'; + +import {validateCommitRange} from './validate-range'; + + +export interface ValidateRangeOptions { + range: string; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs.option('range', { + description: 'The range of commits to check, e.g. --range abc123..xyz456', + demandOption: ' A range must be provided, e.g. --range abc123..xyz456', + type: 'string', + requiresArg: true, + }); +} + +/** Handles the command. */ +async function handler({range}: Arguments) { + // If on CI, and no pull request number is provided, assume the branch + // being run on is an upstream branch. + if (process.env['CI'] && process.env['CI_PULL_REQUEST'] === 'false') { + info(`Since valid commit messages are enforced by PR linting on CI, we do not`); + info(`need to validate commit messages on CI runs on upstream branches.`); + info(); + info(`Skipping check of provided commit range`); + return; + } + validateCommitRange(range); +} + +/** yargs command module describing the command. */ +export const ValidateRangeModule: CommandModule<{}, ValidateRangeOptions> = { + handler, + builder, + command: 'validate-range', + describe: 'Validate a range of commit messages', +}; diff --git a/dev-infra/commit-message/validate-range.ts b/dev-infra/commit-message/validate-range/validate-range.ts similarity index 93% rename from dev-infra/commit-message/validate-range.ts rename to dev-infra/commit-message/validate-range/validate-range.ts index a6fa8d4e0b..3c3825775f 100644 --- a/dev-infra/commit-message/validate-range.ts +++ b/dev-infra/commit-message/validate-range/validate-range.ts @@ -5,11 +5,11 @@ * 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 {error, info} from '../utils/console'; -import {exec} from '../utils/shelljs'; +import {error, info} from '../../utils/console'; +import {exec} from '../../utils/shelljs'; -import {parseCommitMessage} from './parse'; -import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from './validate'; +import {parseCommitMessage} from '../parse'; +import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from '../validate'; // Whether the provided commit is a fixup commit. const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup; diff --git a/dev-infra/commit-message/wizard/cli.ts b/dev-infra/commit-message/wizard/cli.ts new file mode 100644 index 0000000000..b4cc94cfa6 --- /dev/null +++ b/dev-infra/commit-message/wizard/cli.ts @@ -0,0 +1,54 @@ +/** + * @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 {Arguments, Argv, CommandModule} from 'yargs'; + +import {CommitMsgSource} from '../commit-message-source'; + +import {runWizard} from './wizard'; + + +export interface WizardOptions { + filePath: string; + commitSha: string|undefined; + source: CommitMsgSource|undefined; +} + +/** Builds the command. */ +function builder(yargs: Argv) { + return yargs + .positional('filePath', { + description: 'The file path to write the generated commit message into', + type: 'string', + demandOption: true, + }) + .positional('source', { + choices: ['message', 'template', 'merge', 'squash', 'commit'] as const, + description: 'The source of the commit message as described here: ' + + 'https://git-scm.com/docs/githooks#_prepare_commit_msg' + }) + .positional('commitSha', { + description: 'The commit sha if source is set to `commit`', + type: 'string', + }); +} + +/** Handles the command. */ +async function handler(args: Arguments) { + await runWizard(args); +} + +/** yargs command module describing the command. */ +export const WizardModule: CommandModule<{}, WizardOptions> = { + handler, + builder, + command: 'wizard [source] [commitSha]', + // Description: Run the wizard to build a base commit message before opening to complete. + // No describe is defiend to hide the command from the --help. + describe: false, +}; diff --git a/dev-infra/commit-message/wizard.ts b/dev-infra/commit-message/wizard/wizard.ts similarity index 73% rename from dev-infra/commit-message/wizard.ts rename to dev-infra/commit-message/wizard/wizard.ts index 654e41a038..40623fed10 100644 --- a/dev-infra/commit-message/wizard.ts +++ b/dev-infra/commit-message/wizard/wizard.ts @@ -7,16 +7,12 @@ */ import {writeFileSync} from 'fs'; -import {getUserConfig} from '../utils/config'; -import {debug, info} from '../utils/console'; +import {getUserConfig} from '../../utils/config'; +import {debug, info} from '../../utils/console'; -import {buildCommitMessage} from './builder'; +import {buildCommitMessage} from '../builder'; +import {CommitMsgSource} from '../commit-message-source'; -/** - * The source triggering the git commit message creation. - * As described in: https://git-scm.com/docs/githooks#_prepare_commit_msg - */ -export type PrepareCommitMsgHookSource = 'message'|'template'|'merge'|'squash'|'commit'; /** The default commit message used if the wizard does not procude a commit message. */ const defaultCommitMessage = `(): @@ -25,7 +21,7 @@ const defaultCommitMessage = `(): # lines at 100 characters.>\n\n`; export async function runWizard( - args: {filePath: string, source?: PrepareCommitMsgHookSource, commitSha?: string}) { + args: {filePath: string, source?: CommitMsgSource, commitSha?: string}) { if (getUserConfig().commitMessage?.disableWizard) { debug('Skipping commit message wizard due to enabled `commitMessage.disableWizard` option in'); debug('user config.');