feat(dev-infra): standard CLI commands using yargs (#36326)

Creates a standard model for CLI commands provided by ng-dev.
Allows for us to have any of the tools/scripts extend to be
included in the ng-dev command, or be standalone using the same
yargs parser.

PR Close #36326
This commit is contained in:
Joey Perrott 2020-03-26 10:45:09 -07:00 committed by Kara Erickson
parent 326240eb91
commit 43006bcc45
9 changed files with 114 additions and 40 deletions

View File

@ -10,8 +10,11 @@ ts_library(
deps = [ deps = [
"//dev-infra/commit-message", "//dev-infra/commit-message",
"//dev-infra/pullapprove", "//dev-infra/pullapprove",
"//dev-infra/ts-circular-dependencies",
"//dev-infra/utils:config", "//dev-infra/utils:config",
"@npm//@types/node", "@npm//@types/node",
"@npm//@types/yargs",
"@npm//yargs",
], ],
) )

View File

@ -6,26 +6,17 @@
* 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
*/ */
import {readFileSync} from 'fs'; import * as yargs from 'yargs';
import {join} from 'path'; import {tsCircularDependenciesBuilder} from './ts-circular-dependencies/index';
import {verify} from './pullapprove/verify'; import {buildPullapproveParser} from './pullapprove/cli';
import {validateCommitMessage} from './commit-message/validate'; import {buildCommitMessageParser} from './commit-message/cli';
import {getRepoBaseDir} from './utils/config';
const args = process.argv.slice(2); yargs.scriptName('ng-dev')
.demandCommand()
.recommendCommands()
// TODO(josephperrott): Set up proper cli flag/command handling .command('ts-circular-deps <command>', '', tsCircularDependenciesBuilder)
switch (args[0]) { .command('pullapprove <command>', '', buildPullapproveParser)
case 'pullapprove:verify': .command('commit-message <command>', '', buildCommitMessageParser)
verify(); .wrap(120)
break; .strict()
case 'commit-message:pre-commit-validate': .parse();
const commitMessage = readFileSync(join(getRepoBaseDir(), '.git/COMMIT_EDITMSG'), 'utf8');
if (validateCommitMessage(commitMessage)) {
console.info('√ Valid commit message');
}
break;
default:
console.info('No commands were matched');
}

View File

@ -4,15 +4,19 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library")
ts_library( ts_library(
name = "commit-message", name = "commit-message",
srcs = [ srcs = [
"cli.ts",
"config.ts", "config.ts",
"validate.ts", "validate.ts",
"validate-file.ts",
], ],
module_name = "@angular/dev-infra-private/commit-message", module_name = "@angular/dev-infra-private/commit-message",
visibility = ["//dev-infra:__subpackages__"], visibility = ["//dev-infra:__subpackages__"],
deps = [ deps = [
"//dev-infra/utils:config", "//dev-infra/utils:config",
"@npm//@types/node", "@npm//@types/node",
"@npm//@types/yargs",
"@npm//tslib", "@npm//tslib",
"@npm//yargs",
], ],
) )

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. 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 * as yargs from 'yargs';
import {validateFile} from './validate-file';
/** Build the parser for the commit-message commands. */
export function buildCommitMessageParser(localYargs: yargs.Argv) {
return localYargs.help().strict().command(
'pre-commit-validate', 'Validate the most recent commit message', {},
() => { validateFile('.git/COMMIT_EDITMSG'); });
}
if (require.main == module) {
buildCommitMessageParser(yargs).parse();
}

View File

@ -0,0 +1,21 @@
/**
* @license
* Copyright Google Inc. 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 {readFileSync} from 'fs';
import {join} from 'path';
import {getRepoBaseDir} from '../utils/config';
import {validateCommitMessage} from './validate';
/** Validate commit message at the provided file path. */
export function validateFile(filePath: string) {
const commitMessage = readFileSync(join(getRepoBaseDir(), filePath), 'utf8');
if (validateCommitMessage(commitMessage)) {
console.info('√ Valid commit message');
}
}

View File

@ -3,6 +3,7 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library")
ts_library( ts_library(
name = "pullapprove", name = "pullapprove",
srcs = [ srcs = [
"cli.ts",
"group.ts", "group.ts",
"logging.ts", "logging.ts",
"parse-yaml.ts", "parse-yaml.ts",
@ -16,9 +17,11 @@ ts_library(
"@npm//@types/node", "@npm//@types/node",
"@npm//@types/shelljs", "@npm//@types/shelljs",
"@npm//@types/yaml", "@npm//@types/yaml",
"@npm//@types/yargs",
"@npm//minimatch", "@npm//minimatch",
"@npm//shelljs", "@npm//shelljs",
"@npm//tslib", "@npm//tslib",
"@npm//yaml", "@npm//yaml",
"@npm//yargs",
], ],
) )

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. 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 * as yargs from 'yargs';
import {verify} from './verify';
/** Build the parser for the pullapprove commands. */
export function buildPullapproveParser(localYargs: yargs.Argv) {
return localYargs.help().strict().demandCommand().command(
'verify', 'Verify the pullapprove config', {}, () => verify());
}
if (require.main === module) {
buildPullapproveParser(yargs).parse();
}

View File

@ -19,22 +19,30 @@ import {compareGoldens, convertReferenceChainToGolden, Golden} from './golden';
import {convertPathToForwardSlash} from './file_system'; import {convertPathToForwardSlash} from './file_system';
import {loadTestConfig, CircularDependenciesTestConfig} from './config'; import {loadTestConfig, CircularDependenciesTestConfig} from './config';
if (require.main === module) {
const {_: command, config: configArg, warnings} = export function tsCircularDependenciesBuilder(localYargs: yargs.Argv) {
yargs.help() return localYargs.help()
.strict() .strict()
.command('check', 'Checks if the circular dependencies have changed.')
.command('approve', 'Approves the current circular dependencies.')
.demandCommand() .demandCommand()
.option( .option(
'config', 'config',
{type: 'string', demandOption: true, description: 'Path to the configuration file.'}) {type: 'string', demandOption: true, description: 'Path to the configuration file.'})
.option('warnings', {type: 'boolean', description: 'Prints all warnings.'}) .option('warnings', {type: 'boolean', description: 'Prints all warnings.'})
.argv; .command(
'check', 'Checks if the circular dependencies have changed.', {},
(argv: yargs.Arguments) => {
const {config: configArg, warnings} = argv;
const configPath = isAbsolute(configArg) ? configArg : resolve(configArg); const configPath = isAbsolute(configArg) ? configArg : resolve(configArg);
const config = loadTestConfig(configPath); const config = loadTestConfig(configPath);
const isApprove = command.includes('approve'); process.exit(main(false, config, warnings));
process.exit(main(isApprove, config, warnings)); })
.command(
'approve', 'Approves the current circular dependencies.', {}, (argv: yargs.Arguments) => {
const {config: configArg, warnings} = argv;
const configPath = isAbsolute(configArg) ? configArg : resolve(configArg);
const config = loadTestConfig(configPath);
process.exit(main(true, config, warnings));
});
} }
/** /**
@ -126,3 +134,7 @@ function getRelativePath(baseDir: string, path: string) {
function convertReferenceChainToString(chain: ReferenceChain<string>) { function convertReferenceChainToString(chain: ReferenceChain<string>) {
return chain.join(' → '); return chain.join(' → ');
} }
if (require.main === module) {
tsCircularDependenciesBuilder(yargs).parse();
}

View File

@ -35,9 +35,10 @@
"tslint": "tsc -p tools/tsconfig.json && tslint -c tslint.json \"+(packages|modules|scripts|tools)/**/*.+(js|ts)\"", "tslint": "tsc -p tools/tsconfig.json && tslint -c tslint.json \"+(packages|modules|scripts|tools)/**/*.+(js|ts)\"",
"public-api:check": "node goldens/public-api/manage.js test", "public-api:check": "node goldens/public-api/manage.js test",
"public-api:update": "node goldens/public-api/manage.js accept", "public-api:update": "node goldens/public-api/manage.js accept",
"ts-circular-deps": "ts-node dev-infra/ts-circular-dependencies/index.ts --config ./packages/circular-deps-test.conf.js", "ts-circular-deps": "ts-node --transpile-only -- dev-infra/ts-circular-dependencies/index.ts --config ./packages/circular-deps-test.conf.js",
"ts-circular-deps:check": "yarn -s ts-circular-deps check", "ts-circular-deps:check": "yarn -s ts-circular-deps check",
"ts-circular-deps:approve": "yarn -s ts-circular-deps approve" "ts-circular-deps:approve": "yarn -s ts-circular-deps approve",
"ng-dev": "ts-node --transpile-only -- dev-infra/cli.ts"
}, },
"// 1": "dependencies are used locally and by bazel", "// 1": "dependencies are used locally and by bazel",
"dependencies": { "dependencies": {