Skip to content

Commit

Permalink
chore(cli): function to turn yargs output to cliArguments (#32696)
Browse files Browse the repository at this point in the history
**This PR has no impact on CLI functionality**

This PR generates a new function, `convertToCliArgs`, that takes in the output of the `yargs` configuration and turns it into a `CliArguments` type. This allows us to subsequently utilize the strongly-typed object in `cli.ts`, _but this PR does not actually do that_. 

The ultimate benefit is that now we can ensure exactly what goes into this type. We can then enforce that any property allowed to be specified via `cdk.json` is also an option in the cli. When all my refactoring is done, there will be no options that are only specified via `cdk.json`, and no options only specified via CLI.

### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
kaizencc authored Jan 2, 2025
1 parent 3b162fc commit efe7c2e
Show file tree
Hide file tree
Showing 16 changed files with 513 additions and 35 deletions.
1 change: 0 additions & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,4 @@ component_management:

# https://docs.codecov.com/docs/ignoring-paths
ignore:
- packages/aws-cdk/lib/parse-command-line-arguments.ts # this file is generated and some lines cannot be tested
- packages/aws-cdk/lib/cli.ts # we integ test this file
4 changes: 4 additions & 0 deletions packages/aws-cdk/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const config = {
...baseConfig.coveragePathIgnorePatterns,
// Mostly wrappers around the SDK, which get mocked in unit tests
"<rootDir>/lib/api/aws-auth/sdk.ts",
// Files generated by cli-args-gen
"<rootDir>/lib/parse-command-line-arguments.ts",
"<rootDir>/lib/cli-arguments.ts",
"<rootDir>/lib/convert-to-cli-args.ts",
],

// We have many tests here that commonly time out
Expand Down
1 change: 1 addition & 0 deletions packages/aws-cdk/lib/cli-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// GENERATED FROM packages/aws-cdk/lib/config.ts.
// Do not edit by hand; all changes will be overwritten at build time from the config file.
// -------------------------------------------------------------------------------------------
// istanbul ignore file
/* eslint-disable @stylistic/max-len */
import { Command } from './settings';

Expand Down
242 changes: 242 additions & 0 deletions packages/aws-cdk/lib/convert-to-cli-args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
// -------------------------------------------------------------------------------------------
// GENERATED FROM packages/aws-cdk/lib/config.ts.
// Do not edit by hand; all changes will be overwritten at build time from the config file.
// -------------------------------------------------------------------------------------------
// istanbul ignore file
/* eslint-disable @stylistic/max-len */
import { CliArguments, GlobalOptions } from './cli-arguments';
import { Command } from './settings';

// @ts-ignore TS6133
export function convertToCliArgs(args: any): CliArguments {
const globalOptions: GlobalOptions = {
app: args.app,
build: args.build,
context: args.context,
plugin: args.plugin,
trace: args.trace,
strict: args.strict,
lookups: args.lookups,
ignoreErrors: args.ignoreErrors,
json: args.json,
verbose: args.verbose,
debug: args.debug,
profile: args.profile,
proxy: args.proxy,
caBundlePath: args.caBundlePath,
ec2creds: args.ec2creds,
versionReporting: args.versionReporting,
pathMetadata: args.pathMetadata,
assetMetadata: args.assetMetadata,
roleArn: args.roleArn,
staging: args.staging,
output: args.output,
notices: args.notices,
noColor: args.noColor,
ci: args.ci,
unstable: args.unstable,
};
let commandOptions;
switch (args._[0] as Command) {
case 'list':
commandOptions = {
long: args.long,
showDependencies: args.showDependencies,
};
break;

case 'synthesize':
commandOptions = {
exclusively: args.exclusively,
validation: args.validation,
quiet: args.quiet,
};
break;

case 'bootstrap':
commandOptions = {
bootstrapBucketName: args.bootstrapBucketName,
bootstrapKmsKeyId: args.bootstrapKmsKeyId,
examplePermissionsBoundary: args.examplePermissionsBoundary,
customPermissionsBoundary: args.customPermissionsBoundary,
bootstrapCustomerKey: args.bootstrapCustomerKey,
qualifier: args.qualifier,
publicAccessBlockConfiguration: args.publicAccessBlockConfiguration,
tags: args.tags,
execute: args.execute,
trust: args.trust,
trustForLookup: args.trustForLookup,
cloudformationExecutionPolicies: args.cloudformationExecutionPolicies,
force: args.force,
terminationProtection: args.terminationProtection,
showTemplate: args.showTemplate,
toolkitStackName: args.toolkitStackName,
template: args.template,
previousParameters: args.previousParameters,
};
break;

case 'gc':
commandOptions = {
action: args.action,
type: args.type,
rollbackBufferDays: args.rollbackBufferDays,
createdBufferDays: args.createdBufferDays,
confirm: args.confirm,
bootstrapStackName: args.bootstrapStackName,
};
break;

case 'deploy':
commandOptions = {
all: args.all,
buildExclude: args.buildExclude,
exclusively: args.exclusively,
requireApproval: args.requireApproval,
notificationArns: args.notificationArns,
tags: args.tags,
execute: args.execute,
changeSetName: args.changeSetName,
method: args.method,
importExistingResources: args.importExistingResources,
force: args.force,
parameters: args.parameters,
outputsFile: args.outputsFile,
previousParameters: args.previousParameters,
toolkitStackName: args.toolkitStackName,
progress: args.progress,
rollback: args.rollback,
hotswap: args.hotswap,
hotswapFallback: args.hotswapFallback,
watch: args.watch,
logs: args.logs,
concurrency: args.concurrency,
assetParallelism: args.assetParallelism,
assetPrebuild: args.assetPrebuild,
ignoreNoStacks: args.ignoreNoStacks,
};
break;

case 'rollback':
commandOptions = {
all: args.all,
toolkitStackName: args.toolkitStackName,
force: args.force,
validateBootstrapVersion: args.validateBootstrapVersion,
orphan: args.orphan,
};
break;

case 'import':
commandOptions = {
execute: args.execute,
changeSetName: args.changeSetName,
toolkitStackName: args.toolkitStackName,
rollback: args.rollback,
force: args.force,
recordResourceMapping: args.recordResourceMapping,
resourceMapping: args.resourceMapping,
};
break;

case 'watch':
commandOptions = {
buildExclude: args.buildExclude,
exclusively: args.exclusively,
changeSetName: args.changeSetName,
force: args.force,
toolkitStackName: args.toolkitStackName,
progress: args.progress,
rollback: args.rollback,
hotswap: args.hotswap,
hotswapFallback: args.hotswapFallback,
logs: args.logs,
concurrency: args.concurrency,
};
break;

case 'destroy':
commandOptions = {
all: args.all,
exclusively: args.exclusively,
force: args.force,
};
break;

case 'diff':
commandOptions = {
exclusively: args.exclusively,
contextLines: args.contextLines,
template: args.template,
strict: args.strict,
securityOnly: args.securityOnly,
fail: args.fail,
processed: args.processed,
quiet: args.quiet,
changeSet: args.changeSet,
};
break;

case 'metadata':
commandOptions = {};
break;

case 'acknowledge':
commandOptions = {};
break;

case 'notices':
commandOptions = {
unacknowledged: args.unacknowledged,
};
break;

case 'init':
commandOptions = {
language: args.language,
list: args.list,
generateOnly: args.generateOnly,
};
break;

case 'migrate':
commandOptions = {
stackName: args.stackName,
language: args.language,
account: args.account,
region: args.region,
fromPath: args.fromPath,
fromStack: args.fromStack,
outputPath: args.outputPath,
fromScan: args.fromScan,
filter: args.filter,
compress: args.compress,
};
break;

case 'context':
commandOptions = {
reset: args.reset,
force: args.force,
clear: args.clear,
};
break;

case 'docs':
commandOptions = {
browser: args.browser,
};
break;

case 'doctor':
commandOptions = {};
break;
}
const cliArguments: CliArguments = {
_: args._,
globalOptions,
[args._[0]]: commandOptions,
};

return cliArguments;
}
1 change: 1 addition & 0 deletions packages/aws-cdk/lib/parse-command-line-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// GENERATED FROM packages/aws-cdk/lib/config.ts.
// Do not edit by hand; all changes will be overwritten at build time from the config file.
// -------------------------------------------------------------------------------------------
// istanbul ignore file
/* eslint-disable @stylistic/max-len */
import { Argv } from 'yargs';
import * as helpers from './util/yargs-helpers';
Expand Down
5 changes: 3 additions & 2 deletions packages/aws-cdk/scripts/cli-args-gen.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import * as fs from 'fs';
// eslint-disable-next-line import/no-extraneous-dependencies
import { renderYargs, renderCliType } from '@aws-cdk/cli-args-gen';
import { renderYargs, renderCliArgsType, renderCliArgsFunc } from '@aws-cdk/cli-args-gen';
import { makeConfig, YARGS_HELPERS } from '../lib/config';

async function main() {
fs.writeFileSync('./lib/parse-command-line-arguments.ts', await renderYargs(await makeConfig(), YARGS_HELPERS));
fs.writeFileSync('./lib/cli-arguments.ts', await renderCliType(await makeConfig()));
fs.writeFileSync('./lib/cli-arguments.ts', await renderCliArgsType(await makeConfig()));
fs.writeFileSync('./lib/convert-to-cli-args.ts', await renderCliArgsFunc(await makeConfig()));
}

main().then(() => {
Expand Down
73 changes: 60 additions & 13 deletions packages/aws-cdk/test/cli-arguments.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,66 @@
import { CliArguments } from '../lib/cli-arguments';
import { Command } from '../lib/settings';
import { convertToCliArgs } from '../lib/convert-to-cli-args';
import { parseCommandLineArguments } from '../lib/parse-command-line-arguments';

// CliArguments is not being used right now, so the testing suite is rather redundant.
// This file is meant to be populated when CliArguments is used.
test('cli arguments can be used as a type', async () => {
const argv: CliArguments = {
_: [Command.DEPLOY],
test('yargs object can be converted to cli arguments', async () => {
const input = await parseCommandLineArguments(['deploy', '-R', '-v', '--ci']);

const result = convertToCliArgs(input);

expect(result).toEqual({
_: ['deploy'],
globalOptions: {
lookups: true,
app: undefined,
assetMetadata: undefined,
build: undefined,
caBundlePath: undefined,
context: [],
ignoreErrors: false,
noColor: false,
pathMetadata: undefined,
plugin: [],
profile: undefined,
proxy: undefined,
roleArn: undefined,
staging: true,
strict: undefined,
verbose: 1,
versionReporting: undefined,
ci: true,
debug: false,
ec2creds: undefined,
json: false,
verbose: false,
lookups: true,
trace: undefined,
unstable: [],
notices: undefined,
output: undefined,
},
};

expect(argv._[0]).toBe('deploy');
expect(argv.globalOptions?.lookups).toBeTruthy();
deploy: {
all: false,
assetParallelism: undefined,
assetPrebuild: true,
buildExclude: [],
changeSetName: undefined,
concurrency: 1,
execute: undefined,
exclusively: undefined,
force: false,
hotswap: undefined,
hotswapFallback: undefined,
ignoreNoStacks: false,
importExistingResources: false,
logs: true,
method: undefined,
notificationArns: undefined,
outputsFile: undefined,
parameters: [{}],
previousParameters: true,
progress: undefined,
requireApproval: undefined,
rollback: false,
tags: [],
toolkitStackName: undefined,
watch: undefined,
},
});
});
7 changes: 5 additions & 2 deletions tools/@aws-cdk/cli-args-gen/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# cli-args-gen

Generates CDK CLI configurations from the source of truth in `packages/aws-cdk/lib/config.ts`.
Currently generates `yargs` config into `packages/aws-cdk/lib/parse-command-line-arguments.ts` and
strongly-typed CLI arguments interface into `packages/aws-cdk-lib/cli-arguments.ts`.
Currently generates the following files:

- `packages/aws-cdk/lib/parse-command-line-arguments.ts`: `yargs` config.
- `packages/aws-cdk-lib/cli-arguments.ts`: strongly typed `CliArguments` interface.
- `packages/aws-cdk-lib/convert-to-cli-args.ts`: converts the `any` returned by `yargs` to `CliArguments`.

## Usage

Expand Down
Loading

0 comments on commit efe7c2e

Please sign in to comment.