From 637d5a982b93c8e603bcc4ed36b25535c512cdf4 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Mon, 14 Oct 2024 12:33:52 -0700 Subject: [PATCH 01/23] init --- package.json | 1 + packages/aws-cdk/.gitignore | 2 + packages/aws-cdk/package.json | 2 + packages/aws-cdk/tsconfig.json | 2 +- tools/@aws-cdk/yargs-gen/.eslintrc.js | 3 + tools/@aws-cdk/yargs-gen/.gitignore | 18 ++ tools/@aws-cdk/yargs-gen/.npmignore | 7 + tools/@aws-cdk/yargs-gen/LICENSE | 201 ++++++++++++++++++ tools/@aws-cdk/yargs-gen/NOTICE | 2 + tools/@aws-cdk/yargs-gen/README.md | 71 +++++++ tools/@aws-cdk/yargs-gen/bin/yargs-gen | 2 + tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 43 ++++ tools/@aws-cdk/yargs-gen/jest.config.js | 11 + tools/@aws-cdk/yargs-gen/lib/config.ts | 43 ++++ tools/@aws-cdk/yargs-gen/lib/index.ts | 1 + tools/@aws-cdk/yargs-gen/package.json | 60 ++++++ tools/@aws-cdk/yargs-gen/test/cli.test.ts | 33 +++ tools/@aws-cdk/yargs-gen/test/history.test.ts | 20 ++ .../@aws-cdk/yargs-gen/test/services.test.ts | 27 +++ tools/@aws-cdk/yargs-gen/tsconfig.json | 21 ++ 20 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 tools/@aws-cdk/yargs-gen/.eslintrc.js create mode 100644 tools/@aws-cdk/yargs-gen/.gitignore create mode 100644 tools/@aws-cdk/yargs-gen/.npmignore create mode 100644 tools/@aws-cdk/yargs-gen/LICENSE create mode 100644 tools/@aws-cdk/yargs-gen/NOTICE create mode 100644 tools/@aws-cdk/yargs-gen/README.md create mode 100755 tools/@aws-cdk/yargs-gen/bin/yargs-gen create mode 100755 tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts create mode 100644 tools/@aws-cdk/yargs-gen/jest.config.js create mode 100644 tools/@aws-cdk/yargs-gen/lib/config.ts create mode 100644 tools/@aws-cdk/yargs-gen/lib/index.ts create mode 100644 tools/@aws-cdk/yargs-gen/package.json create mode 100644 tools/@aws-cdk/yargs-gen/test/cli.test.ts create mode 100644 tools/@aws-cdk/yargs-gen/test/history.test.ts create mode 100644 tools/@aws-cdk/yargs-gen/test/services.test.ts create mode 100644 tools/@aws-cdk/yargs-gen/tsconfig.json diff --git a/package.json b/package.json index 501b9e61a1cd8..37a0888100f02 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "packages/@aws-cdk-testing/*", "packages/@aws-cdk/*/lambda-packages/*", "tools/@aws-cdk/cdk-build-tools", + "tools/@aws-cdk/yargs-gen", "tools/@aws-cdk/cdk-release", "tools/@aws-cdk/node-bundle", "tools/@aws-cdk/pkglint", diff --git a/packages/aws-cdk/.gitignore b/packages/aws-cdk/.gitignore index b4917729cbcb0..771239ed8ce39 100644 --- a/packages/aws-cdk/.gitignore +++ b/packages/aws-cdk/.gitignore @@ -42,3 +42,5 @@ junit.xml lib/**/*.wasm db.json.gz + +lib/parse-command-line-arguments.ts \ No newline at end of file diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index d51101027610e..6fbe829021441 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -7,6 +7,7 @@ }, "scripts": { "build": "cdk-build", + "yargs-gen": "yargs-gen", "watch": "cdk-watch", "lint": "cdk-lint", "pkglint": "pkglint -f", @@ -65,6 +66,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/yargs-gen": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@octokit/rest": "^18.12.0", "@types/archiver": "^5.3.4", diff --git a/packages/aws-cdk/tsconfig.json b/packages/aws-cdk/tsconfig.json index 86529a2bdeb65..49b2a9fe524a9 100644 --- a/packages/aws-cdk/tsconfig.json +++ b/packages/aws-cdk/tsconfig.json @@ -26,7 +26,7 @@ "**/*.ts", "**/*.d.ts", "lib/init-templates/**/*.hook.ts" - ], +, "../../tools/@aws-cdk/yargs-gen/lib/config.ts", "../../tools/@aws-cdk/yargs-gen/bin/pre-build.ts" ], "exclude": [ "lib/init-templates/**/typescript/*/*.ts", "test/integ/cli/sam_cdk_integ_app/**/*", diff --git a/tools/@aws-cdk/yargs-gen/.eslintrc.js b/tools/@aws-cdk/yargs-gen/.eslintrc.js new file mode 100644 index 0000000000000..2658ee8727166 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/tools/@aws-cdk/yargs-gen/.gitignore b/tools/@aws-cdk/yargs-gen/.gitignore new file mode 100644 index 0000000000000..39180f6eb0bb4 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/.gitignore @@ -0,0 +1,18 @@ +# Build files +*.js +node_modules +*.js.map +*.d.ts +lib/services + +# Test artifacts +.LAST_BUILD +.nyc_output +coverage +nyc.config.js +*.snk +junit.xml + +# Keep configs +!.eslintrc.js +!jest.config.js diff --git a/tools/@aws-cdk/yargs-gen/.npmignore b/tools/@aws-cdk/yargs-gen/.npmignore new file mode 100644 index 0000000000000..79f3b5a763216 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/.npmignore @@ -0,0 +1,7 @@ + +.LAST_BUILD +*.snk +junit.xml +.eslintrc.js +# exclude cdk artifacts +**/cdk.out \ No newline at end of file diff --git a/tools/@aws-cdk/yargs-gen/LICENSE b/tools/@aws-cdk/yargs-gen/LICENSE new file mode 100644 index 0000000000000..dcf28b52a83af --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/@aws-cdk/yargs-gen/NOTICE b/tools/@aws-cdk/yargs-gen/NOTICE new file mode 100644 index 0000000000000..c0b1f046c881a --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/tools/@aws-cdk/yargs-gen/README.md b/tools/@aws-cdk/yargs-gen/README.md new file mode 100644 index 0000000000000..b2aeac3be97cd --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/README.md @@ -0,0 +1,71 @@ +# spec2cdk + +Generates AWS CDK L1s in TypeScript from `@aws-cdk/aws-service-spec`. + +## Usage + +```ts +import { generateAll } from '@aws-cdk/spec2cdk'; + +declare const outputDir: string; + +// Generate all modules +await generateAll(outputPath, { outputPath }); + +// Generate modules with specific instructions +await generate({ + 'aws-lambda': { services: ['AWS::Lambda'] }, + 'aws-s3': { services: ['AWS::S3'] }, +}, { outputPath }); +``` + +Refer to code autocompletion for all options. + +### Use as @aws-cdk/cfn2ts replacement + +The package provides a binary that can be used as a drop-in replacement of the legacy `@aws-cdk/cfn2ts` package. +At a code level, import `@aws-cdk/spec2cdk/lib/cfn2ts` for a drop-in replacement. + +## Temporary Schemas + +You can import additional, temporary CloudFormation Registry Schemas to test new functionality that is not yet published in `@aws-cdk/aws-service-spec`. +To do this, drop the schema file into `temporary-schemas/us-east-1` and it will be imported on top of the default model. + +## CLI + +A CLI is available for testing and ad-hoc usage. +However its API is limited and you should use the programmatic interface for implementations. + +```console +Usage: + spec2cdk [--option=value] + +Arguments: + OUTPUT-PATH The directory the generated code will be written to + +Options: + --augmentations [string] [default: %moduleName%/%serviceShortName%-augmentations.generated.ts] + File and path pattern for generated augmentations files + --augmentations-support [boolean] + Generates additional files required for augmentation files to compile. Use for testing only + --clear-output [boolean] + Completely delete the output path before generating new files + --debug [boolean] + Show additional debug output + -h, --help [boolean] + Show this help + --metrics [string] [default: %moduleName%/%serviceShortName%-canned-metrics.generated.ts] + File and path pattern for generated canned metrics files + --pattern [string] [default: %moduleName%/%serviceShortName%.generated.ts] + File and path pattern for generated files + -s, --service [array] + Generate files only for a specific service, e.g. AWS::S3 + +Path patterns can use the following variables: + + %moduleName% The name of the module, e.g. aws-lambda + %serviceName% The full name of the service, e.g. aws-lambda + %serviceShortName% The short name of the service, e.g. lambda + +Note that %moduleName% and %serviceName% can be different if multiple services are generated into a single module. +``` diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen b/tools/@aws-cdk/yargs-gen/bin/yargs-gen new file mode 100755 index 0000000000000..37366cce30541 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('./spec2cdk.js'); diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts new file mode 100755 index 0000000000000..284b2c7364f3d --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -0,0 +1,43 @@ +import * as typewriter from '@cdklabs/typewriter'; +import { code } from '@cdklabs/typewriter'; +import { CliConfig, makeConfig } from '../lib/config'; + +async function main() { + const config = makeConfig(); + + // Create a new module + const scope = new typewriter.Module('aws-cdk'); + + // Add a function to the module ... + const parseCommandLineArguments = new typewriter.FreeFunction(scope, { + name: 'myFunction', + }); + + // ... add statements to the function body + parseCommandLineArguments.addBody(code.stmt.ret(code.expr.lit(1))); + + // Emit the code + const renderer = new typewriter.TypeScriptRenderer(); + // eslint-disable-next-line no-console + console.log(renderer.render(scope)); + + const yargsStuff = ` +return yargs + .env('CDK') + .usage('Usage: cdk -a COMMAND') +` + addCommands(config); +} + +function addCommands(config: CliConfig) { + for (const command of Object.keys(config)) { + for (const _option of Object.keys((config as any)[command])) { + return `.command(['synthesize [STACKS..]', 'synth [STACKS..]'], 'Synthesizes and prints the CloudFormation template for this stack', (yargs: Argv) => yargs` + } + } +} + +main().then(() => { + +}).catch(() => { + +}); \ No newline at end of file diff --git a/tools/@aws-cdk/yargs-gen/jest.config.js b/tools/@aws-cdk/yargs-gen/jest.config.js new file mode 100644 index 0000000000000..1a75d3f3b0092 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/jest.config.js @@ -0,0 +1,11 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); + +module.exports = { + ...baseConfig, + coverageThreshold: { + global: { + // Pretty bad but we disabled snapshots + branches: 30, + }, + }, +}; diff --git a/tools/@aws-cdk/yargs-gen/lib/config.ts b/tools/@aws-cdk/yargs-gen/lib/config.ts new file mode 100644 index 0000000000000..aecb77e132acc --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/lib/config.ts @@ -0,0 +1,43 @@ +// called by a build tool to generate parse-command-line-arguments.ts + +interface YargsCommand { + description: string; + options?: { [optionName: string]: YargsOption }; + aliases?: string[]; + args?: { [argName: string]: YargsArg }; +} + +// might need to expand +interface YargsArg { + variadic: boolean; +} + +interface YargsOption { + type: string; + description: string; + default?: string | boolean; + alias?: string; +} + +export interface CliConfig { + commands: { [commandName: string]: YargsCommand }; +} + +export function makeConfig(): CliConfig { + const config: CliConfig = { + commands: { + deploy: { + description: 'Deploys the stack(s) named STACKS into your AWS account', + options: { + all: { + type: 'boolean', + description: 'Deploy all available stacks', + default: false, + }, + }, + }, + }, + }; + + return config; +} \ No newline at end of file diff --git a/tools/@aws-cdk/yargs-gen/lib/index.ts b/tools/@aws-cdk/yargs-gen/lib/index.ts new file mode 100644 index 0000000000000..bfa3f59b98314 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/lib/index.ts @@ -0,0 +1 @@ +export * from './generate'; diff --git a/tools/@aws-cdk/yargs-gen/package.json b/tools/@aws-cdk/yargs-gen/package.json new file mode 100644 index 0000000000000..2ce182efaebc3 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/package.json @@ -0,0 +1,60 @@ +{ + "name": "@aws-cdk/yargs-gen", + "private": true, + "version": "0.0.0", + "description": "Generate yargs", + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "tools/@aws-cdk/yargs-gen" + }, + "bin": { + "yargs-gen": "bin/yargs-gen" + }, + "main": "./lib/index.js", + "types": "./lib/index.d.ts", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "pkglint": "pkglint -f", + "build+test": "yarn build && yarn test", + "build+extract": "yarn build", + "build+test+extract": "yarn build+test", + "build+test+package": "yarn build+test" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "dependencies": { + "@cdklabs/typewriter": "^0.0.3", + "yargs": "^16.2.0" + }, + "devDependencies": { + "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/pkglint": "0.0.0", + "@types/jest": "^29.5.12", + "@types/node": "^18", + "jest": "^29.7.0" + }, + "keywords": [ + "aws", + "cdk" + ], + "homepage": "https://github.com/aws/aws-cdk", + "engines": { + "node": ">= 14.15.0" + }, + "ubergen": { + "exclude": true + }, + "pkglint": { + "exclude": [ + "dependencies/cdk-point-dependencies" + ] + } +} diff --git a/tools/@aws-cdk/yargs-gen/test/cli.test.ts b/tools/@aws-cdk/yargs-gen/test/cli.test.ts new file mode 100644 index 0000000000000..0a2dc78d9946f --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/test/cli.test.ts @@ -0,0 +1,33 @@ +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { main } from '../lib/cli/cli'; + +describe('cli', () => { + test('can generate specific services', async () => { + await withTemporaryDirectory(async ({ testDir }) => { + await main([testDir, '--service', 'AWS::S3', '--service', 'AWS::SNS']); + + expect(fs.existsSync(path.join(testDir, 'aws-s3', 's3.generated.ts'))).toBe(true); + expect(fs.existsSync(path.join(testDir, 'aws-sns', 'sns.generated.ts'))).toBe(true); + }); + }); +}); + +interface TemporaryDirectoryContext { + readonly testDir: string; +} + +async function withTemporaryDirectory(block: (context: TemporaryDirectoryContext) => Promise) { + const testDir = path.join(os.tmpdir(), 'spec2cdk-test'); + fs.mkdirSync(testDir, { recursive: true }); + + try { + await block({ testDir }); + } finally { + fs.rmSync(testDir, { + recursive: true, + force: true, + }); + } +} diff --git a/tools/@aws-cdk/yargs-gen/test/history.test.ts b/tools/@aws-cdk/yargs-gen/test/history.test.ts new file mode 100644 index 0000000000000..9bc4ee0f3fab9 --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/test/history.test.ts @@ -0,0 +1,20 @@ +import { loadAwsServiceSpec } from '@aws-cdk/aws-service-spec'; +import { SpecDatabase } from '@aws-cdk/service-spec-types'; +import { IScope } from '@cdklabs/typewriter'; +import { AstBuilder } from '../lib/cdk/ast'; + +let db: SpecDatabase; + +beforeAll(async () => { + db = await loadAwsServiceSpec(); +}); + +// In the old cfn2ts implementation we render all types into the spec +// To ensure backwards compatibility we will render previous types +test('Previous types are rendered', () => { + const resource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::CloudFormation::StackSet')[0]; + const ast = AstBuilder.forResource(resource, { db }); + const stackSet = ast.module?.tryFindType('@aws-cdk/cloudformation/stackset-l1.CfnStackSet') as unknown as IScope; + + expect(stackSet.tryFindType('@aws-cdk/cloudformation/stackset-l1.CfnStackSet.ManagedExecutionProperty')).toBeTruthy(); +}); diff --git a/tools/@aws-cdk/yargs-gen/test/services.test.ts b/tools/@aws-cdk/yargs-gen/test/services.test.ts new file mode 100644 index 0000000000000..677a0cdc62eea --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/test/services.test.ts @@ -0,0 +1,27 @@ +import { loadAwsServiceSpec } from '@aws-cdk/aws-service-spec'; +import { SpecDatabase } from '@aws-cdk/service-spec-types'; +import { TypeScriptRenderer } from '@cdklabs/typewriter'; +import { AstBuilder } from '../lib/cdk/ast'; + +const renderer = new TypeScriptRenderer(); +let db: SpecDatabase; + +beforeAll(async () => { + db = await loadAwsServiceSpec(); +}); + +test('can codegen service with arbitrary suffix', () => { + const service = db.lookup('service', 'name', 'equals', 'aws-kinesisanalyticsv2').only(); + + const ast = AstBuilder.forService(service, { db, nameSuffix: 'V2' }); + + const rendered = renderer.render(ast.module); + + // Snapshot tests will fail every time the docs get updated + // expect(rendered).toMatchSnapshot(); + expect(rendered).toContain('class CfnApplicationV2'); + expect(rendered).toContain('namespace CfnApplicationV2'); + expect(rendered).toContain('interface CfnApplicationV2Props'); + expect(rendered).toContain('function convertCfnApplicationV2PropsToCloudFormation'); + expect(rendered).toContain('function CfnApplicationV2ApplicationCodeConfigurationPropertyValidator'); +}); diff --git a/tools/@aws-cdk/yargs-gen/tsconfig.json b/tools/@aws-cdk/yargs-gen/tsconfig.json new file mode 100644 index 0000000000000..8ac2abcd4fc9d --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020", "dom"], + "strict": true, + "alwaysStrict": true, + "declaration": true, + "inlineSourceMap": true, + "inlineSources": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "composite": true, + "incremental": true + }, + "include": ["**/*.ts"], + "exclude": ["**/*.d.ts"] +} From 3097ee5c0d61d7c2221fc3723c65f7b1fe17dbb9 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Mon, 14 Oct 2024 15:34:55 -0700 Subject: [PATCH 02/23] prototype --- lerna.json | 1 + packages/aws-cdk/tsconfig.json | 2 +- tools/@aws-cdk/yargs-gen/bin/yargs-gen | 2 +- tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 53 ++++++++++++------- tools/@aws-cdk/yargs-gen/lib/config.ts | 15 ++++-- tools/@aws-cdk/yargs-gen/lib/index.ts | 2 +- tools/@aws-cdk/yargs-gen/test/cli.test.ts | 32 +---------- tools/@aws-cdk/yargs-gen/test/history.test.ts | 20 ------- .../@aws-cdk/yargs-gen/test/services.test.ts | 27 ---------- 9 files changed, 50 insertions(+), 104 deletions(-) delete mode 100644 tools/@aws-cdk/yargs-gen/test/history.test.ts delete mode 100644 tools/@aws-cdk/yargs-gen/test/services.test.ts diff --git a/lerna.json b/lerna.json index 11ba780008542..bcc9be9ea0112 100644 --- a/lerna.json +++ b/lerna.json @@ -10,6 +10,7 @@ "packages/@aws-cdk-testing/*", "packages/@aws-cdk/*/lambda-packages/*", "tools/@aws-cdk/cdk-build-tools", + "tools/@aws-cdk/yargs-gen", "tools/@aws-cdk/cdk-release", "tools/@aws-cdk/node-bundle", "tools/@aws-cdk/pkglint", diff --git a/packages/aws-cdk/tsconfig.json b/packages/aws-cdk/tsconfig.json index 49b2a9fe524a9..86529a2bdeb65 100644 --- a/packages/aws-cdk/tsconfig.json +++ b/packages/aws-cdk/tsconfig.json @@ -26,7 +26,7 @@ "**/*.ts", "**/*.d.ts", "lib/init-templates/**/*.hook.ts" -, "../../tools/@aws-cdk/yargs-gen/lib/config.ts", "../../tools/@aws-cdk/yargs-gen/bin/pre-build.ts" ], + ], "exclude": [ "lib/init-templates/**/typescript/*/*.ts", "test/integ/cli/sam_cdk_integ_app/**/*", diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen b/tools/@aws-cdk/yargs-gen/bin/yargs-gen index 37366cce30541..45571b6423707 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('./spec2cdk.js'); +require('./yargs-gen.js'); diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index 284b2c7364f3d..b8398f6baa1a9 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -3,37 +3,52 @@ import { code } from '@cdklabs/typewriter'; import { CliConfig, makeConfig } from '../lib/config'; async function main() { - const config = makeConfig(); - - // Create a new module const scope = new typewriter.Module('aws-cdk'); - - // Add a function to the module ... const parseCommandLineArguments = new typewriter.FreeFunction(scope, { - name: 'myFunction', + name: 'parseCommandLineArguments', }); + parseCommandLineArguments.addBody(makeYargs(makeConfig())); - // ... add statements to the function body - parseCommandLineArguments.addBody(code.stmt.ret(code.expr.lit(1))); - - // Emit the code const renderer = new typewriter.TypeScriptRenderer(); // eslint-disable-next-line no-console console.log(renderer.render(scope)); +} - const yargsStuff = ` -return yargs +function makeYargs(config: CliConfig): typewriter.Statement { + const preamble = `yargs .env('CDK') .usage('Usage: cdk -a COMMAND') -` + addCommands(config); -} - -function addCommands(config: CliConfig) { - for (const command of Object.keys(config)) { - for (const _option of Object.keys((config as any)[command])) { - return `.command(['synthesize [STACKS..]', 'synth [STACKS..]'], 'Synthesizes and prints the CloudFormation template for this stack', (yargs: Argv) => yargs` + `; + + let yargsExpr = code.expr.directCode(preamble); + for (const command of Object.keys(config.commands)) { + // eslint-disable-next-line no-console + console.log('in loop'); + const commandFacts = config.commands[command]; + const commandArg = commandFacts.arg + ? ` [${commandFacts.arg?.name}${commandFacts.arg?.variadic}]` + : ''; + const aliases = commandFacts.aliases + ? commandFacts.aliases.map((alias) => `, '${alias} ${commandArg}'`) + : ''; + yargsExpr = yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description), code.expr.directCode('(yargs: Argv) => yargs')); + + for (const option of Object.keys(commandFacts.options ?? {})) { + // eslint-disable-next-line no-console + console.log('in loop 2'); + const optionFacts = commandFacts.options![option]; + const optionArgs: { [key: string]: typewriter.Expression } = {}; + for (const optionProp of Object.keys(optionFacts)) { + // eslint-disable-next-line no-console + console.log('in loop 3'); + optionArgs[optionProp] = code.expr.lit((optionFacts as any)[optionProp]); + } + + yargsExpr = yargsExpr.callMethod('option', code.expr.lit(`${option}`), code.expr.object(optionArgs)); } } + + return code.stmt.ret(yargsExpr); } main().then(() => { diff --git a/tools/@aws-cdk/yargs-gen/lib/config.ts b/tools/@aws-cdk/yargs-gen/lib/config.ts index aecb77e132acc..6563e5ed72340 100644 --- a/tools/@aws-cdk/yargs-gen/lib/config.ts +++ b/tools/@aws-cdk/yargs-gen/lib/config.ts @@ -4,19 +4,24 @@ interface YargsCommand { description: string; options?: { [optionName: string]: YargsOption }; aliases?: string[]; - args?: { [argName: string]: YargsArg }; + //args?: { [argName: string]: YargsArg }; + arg?: YargsArg; } // might need to expand interface YargsArg { + name: string; variadic: boolean; } interface YargsOption { - type: string; - description: string; - default?: string | boolean; + type: 'string' | 'array' | 'number' | 'boolean' | 'count'; + desc: string; + default?: any; alias?: string; + conflicts?: string | readonly string[] | { [key: string]: string | readonly string[] }; + nargs?: number; + requiresArg?: boolean; } export interface CliConfig { @@ -31,7 +36,7 @@ export function makeConfig(): CliConfig { options: { all: { type: 'boolean', - description: 'Deploy all available stacks', + desc: 'Deploy all available stacks', default: false, }, }, diff --git a/tools/@aws-cdk/yargs-gen/lib/index.ts b/tools/@aws-cdk/yargs-gen/lib/index.ts index bfa3f59b98314..f03c2281a9140 100644 --- a/tools/@aws-cdk/yargs-gen/lib/index.ts +++ b/tools/@aws-cdk/yargs-gen/lib/index.ts @@ -1 +1 @@ -export * from './generate'; +export * from './config'; diff --git a/tools/@aws-cdk/yargs-gen/test/cli.test.ts b/tools/@aws-cdk/yargs-gen/test/cli.test.ts index 0a2dc78d9946f..69f4567e06e7c 100644 --- a/tools/@aws-cdk/yargs-gen/test/cli.test.ts +++ b/tools/@aws-cdk/yargs-gen/test/cli.test.ts @@ -1,33 +1,5 @@ -import * as fs from 'node:fs'; -import * as os from 'node:os'; -import * as path from 'node:path'; -import { main } from '../lib/cli/cli'; - -describe('cli', () => { +describe('nothing', () => { test('can generate specific services', async () => { - await withTemporaryDirectory(async ({ testDir }) => { - await main([testDir, '--service', 'AWS::S3', '--service', 'AWS::SNS']); - - expect(fs.existsSync(path.join(testDir, 'aws-s3', 's3.generated.ts'))).toBe(true); - expect(fs.existsSync(path.join(testDir, 'aws-sns', 'sns.generated.ts'))).toBe(true); - }); + expect(2 + 2).toBe(4); }); }); - -interface TemporaryDirectoryContext { - readonly testDir: string; -} - -async function withTemporaryDirectory(block: (context: TemporaryDirectoryContext) => Promise) { - const testDir = path.join(os.tmpdir(), 'spec2cdk-test'); - fs.mkdirSync(testDir, { recursive: true }); - - try { - await block({ testDir }); - } finally { - fs.rmSync(testDir, { - recursive: true, - force: true, - }); - } -} diff --git a/tools/@aws-cdk/yargs-gen/test/history.test.ts b/tools/@aws-cdk/yargs-gen/test/history.test.ts deleted file mode 100644 index 9bc4ee0f3fab9..0000000000000 --- a/tools/@aws-cdk/yargs-gen/test/history.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { loadAwsServiceSpec } from '@aws-cdk/aws-service-spec'; -import { SpecDatabase } from '@aws-cdk/service-spec-types'; -import { IScope } from '@cdklabs/typewriter'; -import { AstBuilder } from '../lib/cdk/ast'; - -let db: SpecDatabase; - -beforeAll(async () => { - db = await loadAwsServiceSpec(); -}); - -// In the old cfn2ts implementation we render all types into the spec -// To ensure backwards compatibility we will render previous types -test('Previous types are rendered', () => { - const resource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::CloudFormation::StackSet')[0]; - const ast = AstBuilder.forResource(resource, { db }); - const stackSet = ast.module?.tryFindType('@aws-cdk/cloudformation/stackset-l1.CfnStackSet') as unknown as IScope; - - expect(stackSet.tryFindType('@aws-cdk/cloudformation/stackset-l1.CfnStackSet.ManagedExecutionProperty')).toBeTruthy(); -}); diff --git a/tools/@aws-cdk/yargs-gen/test/services.test.ts b/tools/@aws-cdk/yargs-gen/test/services.test.ts deleted file mode 100644 index 677a0cdc62eea..0000000000000 --- a/tools/@aws-cdk/yargs-gen/test/services.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { loadAwsServiceSpec } from '@aws-cdk/aws-service-spec'; -import { SpecDatabase } from '@aws-cdk/service-spec-types'; -import { TypeScriptRenderer } from '@cdklabs/typewriter'; -import { AstBuilder } from '../lib/cdk/ast'; - -const renderer = new TypeScriptRenderer(); -let db: SpecDatabase; - -beforeAll(async () => { - db = await loadAwsServiceSpec(); -}); - -test('can codegen service with arbitrary suffix', () => { - const service = db.lookup('service', 'name', 'equals', 'aws-kinesisanalyticsv2').only(); - - const ast = AstBuilder.forService(service, { db, nameSuffix: 'V2' }); - - const rendered = renderer.render(ast.module); - - // Snapshot tests will fail every time the docs get updated - // expect(rendered).toMatchSnapshot(); - expect(rendered).toContain('class CfnApplicationV2'); - expect(rendered).toContain('namespace CfnApplicationV2'); - expect(rendered).toContain('interface CfnApplicationV2Props'); - expect(rendered).toContain('function convertCfnApplicationV2PropsToCloudFormation'); - expect(rendered).toContain('function CfnApplicationV2ApplicationCodeConfigurationPropertyValidator'); -}); From e3b3d61eb5fe1a10681fe7572531b85001f1bc89 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Mon, 14 Oct 2024 15:40:05 -0700 Subject: [PATCH 03/23] args fix + the yargs inline function for the options is still wrong --- tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 2 +- tools/@aws-cdk/yargs-gen/lib/config.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index b8398f6baa1a9..6c3876b65958b 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -26,7 +26,7 @@ function makeYargs(config: CliConfig): typewriter.Statement { console.log('in loop'); const commandFacts = config.commands[command]; const commandArg = commandFacts.arg - ? ` [${commandFacts.arg?.name}${commandFacts.arg?.variadic}]` + ? ` [${commandFacts.arg?.name}${commandFacts.arg?.variadic ? '..' : ''}]` : ''; const aliases = commandFacts.aliases ? commandFacts.aliases.map((alias) => `, '${alias} ${commandArg}'`) diff --git a/tools/@aws-cdk/yargs-gen/lib/config.ts b/tools/@aws-cdk/yargs-gen/lib/config.ts index 6563e5ed72340..637ca1b8629b6 100644 --- a/tools/@aws-cdk/yargs-gen/lib/config.ts +++ b/tools/@aws-cdk/yargs-gen/lib/config.ts @@ -40,6 +40,10 @@ export function makeConfig(): CliConfig { default: false, }, }, + arg: { + name: 'STACKS', + variadic: true, + }, }, }, }; From 19e92a3478acc36d60175dde16334d95fcc2b14f Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Tue, 15 Oct 2024 15:32:32 -0700 Subject: [PATCH 04/23] middleware + more options --- tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 53 +++++++--- tools/@aws-cdk/yargs-gen/lib/config.ts | 115 +++++++++++++++++++++- 2 files changed, 148 insertions(+), 20 deletions(-) diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index 6c3876b65958b..121cc0774a041 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -1,20 +1,24 @@ -import * as typewriter from '@cdklabs/typewriter'; -import { code } from '@cdklabs/typewriter'; +import { Expression, FreeFunction, Module, Statement, TypeScriptRenderer, code } from '@cdklabs/typewriter'; import { CliConfig, makeConfig } from '../lib/config'; async function main() { - const scope = new typewriter.Module('aws-cdk'); - const parseCommandLineArguments = new typewriter.FreeFunction(scope, { + const scope = new Module('aws-cdk'); + const parseCommandLineArguments = new FreeFunction(scope, { name: 'parseCommandLineArguments', }); parseCommandLineArguments.addBody(makeYargs(makeConfig())); - const renderer = new typewriter.TypeScriptRenderer(); + const renderer = new TypeScriptRenderer(); // eslint-disable-next-line no-console console.log(renderer.render(scope)); } -function makeYargs(config: CliConfig): typewriter.Statement { +interface MiddlewareExpression { + callbacks: Expression; + applyBeforeValidation?: Expression; +} + +function makeYargs(config: CliConfig): Statement { const preamble = `yargs .env('CDK') .usage('Usage: cdk -a COMMAND') @@ -22,8 +26,6 @@ function makeYargs(config: CliConfig): typewriter.Statement { let yargsExpr = code.expr.directCode(preamble); for (const command of Object.keys(config.commands)) { - // eslint-disable-next-line no-console - console.log('in loop'); const commandFacts = config.commands[command]; const commandArg = commandFacts.arg ? ` [${commandFacts.arg?.name}${commandFacts.arg?.variadic ? '..' : ''}]` @@ -31,21 +33,40 @@ function makeYargs(config: CliConfig): typewriter.Statement { const aliases = commandFacts.aliases ? commandFacts.aliases.map((alias) => `, '${alias} ${commandArg}'`) : ''; - yargsExpr = yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description), code.expr.directCode('(yargs: Argv) => yargs')); + + // must compute options before we compute the full command, because in yargs, the options are an argument to the command call. + let optionsExpr: Expression = code.expr.directCode('(yargs: Argv) => yargs'); for (const option of Object.keys(commandFacts.options ?? {})) { - // eslint-disable-next-line no-console - console.log('in loop 2'); + // each option can define at most one middleware call; if we need more, handle a list of these instead + let middleware: MiddlewareExpression | undefined = undefined; const optionFacts = commandFacts.options![option]; - const optionArgs: { [key: string]: typewriter.Expression } = {}; + const optionArgs: { [key: string]: Expression } = {}; for (const optionProp of Object.keys(optionFacts)) { - // eslint-disable-next-line no-console - console.log('in loop 3'); - optionArgs[optionProp] = code.expr.lit((optionFacts as any)[optionProp]); + switch (optionProp) { + case 'middleware': + // middleware is a separate function call, so we can't store it with the regular option arguments: + // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) + middleware = { + callbacks: code.expr.lit(optionFacts.middleware!.callbacks.toString()), + applyBeforeValidation: code.expr.lit(optionFacts.middleware!.applyBeforeValidation), + }; + break; + + default: + optionArgs[optionProp] = code.expr.lit((optionFacts as any)[optionProp]); + } } - yargsExpr = yargsExpr.callMethod('option', code.expr.lit(`${option}`), code.expr.object(optionArgs)); + optionsExpr = optionsExpr.callMethod('option', code.expr.lit(`${option}`), code.expr.object(optionArgs)); + if (middleware) { + optionsExpr = optionsExpr.callMethod('middleware', middleware.callbacks, middleware.applyBeforeValidation ?? code.expr.UNDEFINED); + middleware = undefined; + } } + + // tail-recursive? + yargsExpr = yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description), optionsExpr); } return code.stmt.ret(yargsExpr); diff --git a/tools/@aws-cdk/yargs-gen/lib/config.ts b/tools/@aws-cdk/yargs-gen/lib/config.ts index 637ca1b8629b6..f3e7fdbd13212 100644 --- a/tools/@aws-cdk/yargs-gen/lib/config.ts +++ b/tools/@aws-cdk/yargs-gen/lib/config.ts @@ -14,31 +14,138 @@ interface YargsArg { variadic: boolean; } +enum RequireApproval { + Never = 'never', + + AnyChange = 'any-change', + + Broadening = 'broadening', +} + +/** + * Supported display modes for stack deployment activity + */ +enum StackActivityProgress { + /** + * Displays a progress bar with only the events for the resource currently being deployed + */ + BAR = 'bar', + + /** + * Displays complete history with all CloudFormation stack events + */ + EVENTS = 'events', +} + interface YargsOption { type: 'string' | 'array' | 'number' | 'boolean' | 'count'; - desc: string; + desc?: string; default?: any; + deprecated?: boolean | string; + choices?: ReadonlyArray; alias?: string; conflicts?: string | readonly string[] | { [key: string]: string | readonly string[] }; nargs?: number; requiresArg?: boolean; + hidden?: boolean; + middleware?: Middleware; +} + +export interface Middleware { + callbacks: MiddlewareFunction | ReadonlyArray; + applyBeforeValidation?: boolean; } export interface CliConfig { commands: { [commandName: string]: YargsCommand }; } +// copied from yargs +type MiddlewareFunction = (args: any) => void; +// end copied from yargs + +function yargsNegativeAlias(shortName: S, longName: L) { + return (argv: T) => { + if (shortName in argv && argv[shortName]) { + (argv as any)[longName] = false; + } + return argv; + }; +} + export function makeConfig(): CliConfig { const config: CliConfig = { commands: { deploy: { description: 'Deploys the stack(s) named STACKS into your AWS account', options: { - all: { + 'all': { type: 'boolean', desc: 'Deploy all available stacks', default: false }, + 'build-exclude': { type: 'array', alias: 'E', nargs: 1, desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', default: [] }, + 'exclusively': { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' }, + 'require-approval': { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' }, + 'notification-arns': { type: 'array', desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', nargs: 1, requiresArg: true }, + // @deprecated(v2) -- tags are part of the Cloud Assembly and tags specified here will be overwritten on the next deployment + 'tags': { type: 'array', alias: 't', desc: 'Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)', nargs: 1, requiresArg: true }, + 'execute': { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet) (deprecated)', deprecated: true }, + 'change-set-name': { type: 'string', desc: 'Name of the CloudFormation change set to create (only if method is not direct)' }, + 'method': { + alias: 'm', + type: 'string', + choices: ['direct', 'change-set', 'prepare-change-set'], + requiresArg: true, + desc: 'How to perform the deployment. Direct is a bit faster but lacks progress information', + }, + 'force': { alias: 'f', type: 'boolean', desc: 'Always deploy stack even if templates are identical', default: false }, + 'parameters': { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} }, + 'outputs-file': { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true }, + 'previous-parameters': { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }, + 'toolkit-stack-name': { type: 'string', desc: 'The name of the existing CDK toolkit stack (only used for app using legacy synthesis)', requiresArg: true }, + 'progress': { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events' }, + 'rollback': { + type: 'boolean', + desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + + 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', + }, + 'R': { + type: 'boolean', + hidden: true, + // Hack to get '-R' as an alias for '--no-rollback', suggested by: https://github.com/yargs/yargs/issues/1729 + middleware: { + callbacks: yargsNegativeAlias('R', 'rollback'), + applyBeforeValidation: true, + }, + }, + 'hotswap': { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, " + + 'but does not fall back to a full deployment if that is not possible. ' + + 'Instead, changes to any non-hotswappable properties are ignored.' + + 'Do not use this in production environments', + }, + 'hotswap-fallback': { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, " + + 'which skips CloudFormation and updates the resources directly, ' + + 'and falls back to a full deployment if that is not possible. ' + + 'Do not use this in production environments', + }, + 'watch': { + type: 'boolean', + desc: 'Continuously observe the project files, ' + + 'and deploy the given stack(s) automatically when changes are detected. ' + + 'Implies --hotswap by default', + }, + 'logs': { type: 'boolean', - desc: 'Deploy all available stacks', - default: false, + default: true, + desc: 'Show CloudWatch log events from all resources in the selected Stacks in the terminal. ' + + "'true' by default, use --no-logs to turn off. " + + "Only in effect if specified alongside the '--watch' option", }, + 'concurrency': { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }, + 'asset-parallelism': { type: 'boolean', desc: 'Whether to build/publish assets in parallel' }, + 'asset-prebuild': { type: 'boolean', desc: 'Whether to build all assets before deploying the first stack (useful for failing Docker builds)', default: true }, + 'ignore-no-stacks': { type: 'boolean', desc: 'Whether to deploy if the app contains no stacks', default: false }, }, arg: { name: 'STACKS', From 8e0421bc4be0182ec6a271a762b0030db009f635 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Tue, 15 Oct 2024 15:33:15 -0700 Subject: [PATCH 05/23] docstring --- tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index 121cc0774a041..df24bf09a7401 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -45,7 +45,7 @@ function makeYargs(config: CliConfig): Statement { for (const optionProp of Object.keys(optionFacts)) { switch (optionProp) { case 'middleware': - // middleware is a separate function call, so we can't store it with the regular option arguments: + // middleware is a separate function call, so we can't store it with the regular option arguments, as those will all be treated as parameters: // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) middleware = { callbacks: code.expr.lit(optionFacts.middleware!.callbacks.toString()), From 8ca3812c985f38f1239b44561a11eca4f78a3c99 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Wed, 16 Oct 2024 15:34:02 -0700 Subject: [PATCH 06/23] all commands + options, missing epilogue + preamble still --- tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 4 +- tools/@aws-cdk/yargs-gen/lib/config.ts | 240 ++++++++++++++++++++++ 2 files changed, 243 insertions(+), 1 deletion(-) diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index df24bf09a7401..66acd12f04ed5 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -66,7 +66,9 @@ function makeYargs(config: CliConfig): Statement { } // tail-recursive? - yargsExpr = yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description), optionsExpr); + yargsExpr = commandFacts.options + ? yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description), optionsExpr) + : yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description)); } return code.stmt.ret(yargsExpr); diff --git a/tools/@aws-cdk/yargs-gen/lib/config.ts b/tools/@aws-cdk/yargs-gen/lib/config.ts index f3e7fdbd13212..4de430b9dcf8e 100644 --- a/tools/@aws-cdk/yargs-gen/lib/config.ts +++ b/tools/@aws-cdk/yargs-gen/lib/config.ts @@ -1,3 +1,4 @@ +/* eslint-disable quote-props */ // called by a build tool to generate parse-command-line-arguments.ts interface YargsCommand { @@ -152,6 +153,245 @@ export function makeConfig(): CliConfig { variadic: true, }, }, + rollback: { + description: 'Rolls back the stack(s) named STACKS to their last stable state', + arg: { + name: 'STACKS', + variadic: true, + }, + options: { + 'all': { type: 'boolean', default: false, desc: 'Roll back all available stacks' }, + 'toolkit-stack-name': { type: 'string', desc: 'The name of the CDK toolkit stack the environment is bootstrapped with', requiresArg: true }, + 'force': { + alias: 'f', + type: 'boolean', + desc: 'Orphan all resources for which the rollback operation fails.', + }, + 'validate-bootstrap-version': { + type: 'boolean', + desc: 'Whether to validate the bootstrap stack version. Defaults to \'true\', disable with --no-validate-bootstrap-version.', + }, + 'orphan': { + // alias: 'o' conflicts with --output + type: 'array', + nargs: 1, + requiresArg: true, + desc: 'Orphan the given resources, identified by their logical ID (can be specified multiple times)', + default: [], + }, + }, + }, + import: { + description: 'Import existing resource(s) into the given STACK', + arg: { + name: 'STACK', + variadic: false, + }, + options: { + 'execute': { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }, + 'change-set-name': { type: 'string', desc: 'Name of the CloudFormation change set to create' }, + 'toolkit-stack-name': { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }, + 'rollback': { + type: 'boolean', + desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + + 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', + }, + 'force': { + alias: 'f', + type: 'boolean', + desc: 'Do not abort if the template diff includes updates or deletes. This is probably safe but we\'re not sure, let us know how it goes.', + }, + 'record-resource-mapping': { + type: 'string', + alias: 'r', + requiresArg: true, + desc: 'If specified, CDK will generate a mapping of existing physical resources to CDK resources to be imported as. The mapping ' + + 'will be written in the given file path. No actual import operation will be performed', + }, + 'resource-mapping': { + type: 'string', + alias: 'm', + requiresArg: true, + desc: 'If specified, CDK will use the given file to map physical resources to CDK resources for import, instead of interactively ' + + 'asking the user. Can be run from scripts', + }, + }, + }, + watch: { + description: "Shortcut for 'deploy --watch'", + arg: { + name: 'STACKS', + variadic: true, + }, + options: { + // I'm fairly certain none of these options, present for 'deploy', make sense for 'watch': + // .option('all', { type: 'boolean', default: false, desc: 'Deploy all available stacks' }) + // .option('ci', { type: 'boolean', desc: 'Force CI detection', default: process.env.CI !== undefined }) + // @deprecated(v2) -- tags are part of the Cloud Assembly and tags specified here will be overwritten on the next deployment + // .option('tags', { type: 'array', alias: 't', desc: 'Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)', nargs: 1, requiresArg: true }) + // .option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }) + // These options, however, are more subtle - I could be convinced some of these should also be available for 'watch': + // .option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' }) + // .option('parameters', { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} }) + // .option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }) + // .option('outputs-file', { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true }) + // .option('notification-arns', { type: 'array', desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', nargs: 1, requiresArg: true }) + 'build-exclude': { type: 'array', alias: 'E', nargs: 1, desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', default: [] }, + 'exclusively': { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' }, + 'change-set-name': { type: 'string', desc: 'Name of the CloudFormation change set to create' }, + 'force': { alias: 'f', type: 'boolean', desc: 'Always deploy stack even if templates are identical', default: false }, + 'toolkit-stack-name': { type: 'string', desc: 'The name of the existing CDK toolkit stack (only used for app using legacy synthesis)', requiresArg: true }, + 'progress': { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events' }, + 'rollback': { + type: 'boolean', + desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + + 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', + }, + // same hack for -R as above in 'deploy' + 'R': { + type: 'boolean', + hidden: true, + middleware: { + callbacks: yargsNegativeAlias('R', 'rollback'), + applyBeforeValidation: true, + }, + }, + 'hotswap': { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, " + + 'but does not fall back to a full deployment if that is not possible. ' + + 'Instead, changes to any non-hotswappable properties are ignored.' + + "'true' by default, use --no-hotswap to turn off", + }, + 'hotswap-fallback': { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, " + + 'which skips CloudFormation and updates the resources directly, ' + + 'and falls back to a full deployment if that is not possible.', + }, + 'logs': { + type: 'boolean', + default: true, + desc: 'Show CloudWatch log events from all resources in the selected Stacks in the terminal. ' + + "'true' by default, use --no-logs to turn off", + }, + 'concurrency': { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }, + }, + }, + destroy: { + description: 'Destroy the stack(s) named STACKS', + arg: { + name: 'STACKS', + variadic: true, + }, + options: { + 'all': { type: 'boolean', default: false, desc: 'Destroy all available stacks' }, + 'exclusively': { type: 'boolean', alias: 'e', desc: 'Only destroy requested stacks, don\'t include dependees' }, + 'force': { type: 'boolean', alias: 'f', desc: 'Do not ask for confirmation before destroying the stacks' }, + }, + }, + diff: { + description: 'Compares the specified stack with the deployed stack or a local template file, and returns with status 1 if any difference is found', + arg: { + name: 'STACKS', + variadic: true, + }, + options: { + 'exclusively': { type: 'boolean', alias: 'e', desc: 'Only diff requested stacks, don\'t include dependencies' }, + 'context-lines': { type: 'number', desc: 'Number of context lines to include in arbitrary JSON diff rendering', default: 3, requiresArg: true }, + 'template': { type: 'string', desc: 'The path to the CloudFormation template to compare with', requiresArg: true }, + 'strict': { type: 'boolean', desc: 'Do not filter out AWS::CDK::Metadata resources, mangled non-ASCII characters, or the CheckBootstrapVersionRule', default: false }, + 'security-only': { type: 'boolean', desc: 'Only diff for broadened security changes', default: false }, + 'fail': { type: 'boolean', desc: 'Fail with exit code 1 in case of diff' }, + 'processed': { type: 'boolean', desc: 'Whether to compare against the template with Transforms already processed', default: false }, + 'quiet': { type: 'boolean', alias: 'q', desc: 'Do not print stack name and default message when there is no diff to stdout', default: false }, + 'change-set': { type: 'boolean', alias: 'changeset', desc: 'Whether to create a changeset to analyze resource replacements. In this mode, diff will use the deploy role instead of the lookup role.', default: true }, + }, + }, + metadata: { + description: 'Returns all metadata associated with this stack', + arg: { + name: 'STACK', + variadic: false, + }, + }, + acknowledge: { + aliases: ['ack'], + description: 'Acknowledge a notice so that it does not show up anymore', + arg: { + name: 'ID', + variadic: false, + }, + }, + notices: { + description: 'Returns a list of relevant notices', + options: { + 'unacknowledged': { type: 'boolean', alias: 'u', default: false, desc: 'Returns a list of unacknowledged notices' }, + }, + }, + init: { + description: 'Create a new, empty CDK project from a template.', + arg: { + name: 'TEMPLATE', + variadic: false, + }, + options: { + 'language': { type: 'string', alias: 'l', desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)' /*, choices: initTemplateLanguages*/ }, // TODO: preamble, this initTemplateLanguages variable needs to go as a statement there. + 'list': { type: 'boolean', desc: 'List the available templates' }, + 'generate-only': { type: 'boolean', default: false, desc: 'If true, only generates project files, without executing additional operations such as setting up a git repo, installing dependencies or compiling the project' }, + }, + }, + 'migrate': { + description: false as any, + options: { + 'stack-name': { type: 'string', alias: 'n', desc: 'The name assigned to the stack created in the new project. The name of the app will be based off this name as well.', requiresArg: true }, + 'language': { type: 'string', default: 'typescript', alias: 'l', desc: 'The language to be used for the new project'/*, choices: MIGRATE_SUPPORTED_LANGUAGES*/ }, // TODO: preamble + 'account': { type: 'string', desc: 'The account to retrieve the CloudFormation stack template from' }, + 'region': { type: 'string', desc: 'The region to retrieve the CloudFormation stack template from' }, + 'from-path': { type: 'string', desc: 'The path to the CloudFormation template to migrate. Use this for locally stored templates' }, + 'from-stack': { type: 'boolean', desc: 'Use this flag to retrieve the template for an existing CloudFormation stack' }, + 'output-path': { type: 'string', desc: 'The output path for the migrated CDK app' }, + 'from-scan': { + type: 'string', + desc: 'Determines if a new scan should be created, or the last successful existing scan should be used ' + + '\n options are "new" or "most-recent"', + }, + 'filter': { + type: 'array', + desc: 'Filters the resource scan based on the provided criteria in the following format: "key1=value1,key2=value2"' + + '\n This field can be passed multiple times for OR style filtering: ' + + '\n filtering options: ' + + '\n resource-identifier: A key-value pair that identifies the target resource. i.e. {"ClusterName", "myCluster"}' + + '\n resource-type-prefix: A string that represents a type-name prefix. i.e. "AWS::DynamoDB::"' + + '\n tag-key: a string that matches resources with at least one tag with the provided key. i.e. "myTagKey"' + + '\n tag-value: a string that matches resources with at least one tag with the provided value. i.e. "myTagValue"', + }, + 'compress': { type: 'boolean', desc: 'Use this flag to zip the generated CDK app' }, + }, + }, + 'context': { + description: 'Manage cached context values', + options: { + 'reset': { alias: 'e', desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true }, + 'force': { alias: 'f', desc: 'Ignore missing key error', type: 'boolean', default: false }, + 'clear': { desc: 'Clear all context', type: 'boolean' }, + }, + }, + 'docs': { + aliases: ['doc'], + description: 'Opens the reference documentation in a browser', + options: { + 'browser': { + alias: 'b', + desc: 'the command to use to open the browser, using %u as a placeholder for the path of the file to open', + type: 'string', + //default: process.platform in defaultBrowserCommand ? defaultBrowserCommand[process.platform] : 'xdg-open %u', // TODO: preamble + }, + }, + }, + 'doctor': { + description: 'Check your set-up for potential problems', + }, }, }; From 7c04ce0a4611a0db1c74646c1d05869f36826282 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Mon, 21 Oct 2024 16:31:00 -0700 Subject: [PATCH 07/23] working w/parameters --- packages/aws-cdk/lib/cli.ts | 330 +--------------------- packages/aws-cdk/package.json | 3 + tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 99 +++++-- tools/@aws-cdk/yargs-gen/lib/config.ts | 47 +-- 4 files changed, 121 insertions(+), 358 deletions(-) diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index 0e28a41a4c106..5e1f78eea2aa2 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -3,10 +3,10 @@ import '@jsii/check-node/run'; import * as chalk from 'chalk'; import { install as enableSourceMapSupport } from 'source-map-support'; -import type { Argv } from 'yargs'; import { DeploymentMethod } from './api'; import { HotswapMode } from './api/hotswap/common'; import { ILock } from './api/util/rwlock'; +import { parseCommandLineArguments } from './parse-command-line-arguments'; import { checkForPlatformWarnings } from './platform-warnings'; import { enableTracing } from './util/tracing'; import { SdkProvider } from '../lib/api/aws-auth'; @@ -17,342 +17,36 @@ import { execProgram } from '../lib/api/cxapp/exec'; import { Deployments } from '../lib/api/deployments'; import { PluginHost } from '../lib/api/plugin'; import { ToolkitInfo } from '../lib/api/toolkit-info'; -import { StackActivityProgress } from '../lib/api/util/cloudformation/stack-activity-monitor'; import { CdkToolkit, AssetBuildTime } from '../lib/cdk-toolkit'; import { realHandler as context } from '../lib/commands/context'; import { realHandler as docs } from '../lib/commands/docs'; import { realHandler as doctor } from '../lib/commands/doctor'; import { MIGRATE_SUPPORTED_LANGUAGES, getMigrateScanType } from '../lib/commands/migrate'; -import { RequireApproval } from '../lib/diff'; import { availableInitLanguages, cliInit, printAvailableTemplates } from '../lib/init'; import { data, debug, error, print, setLogLevel, setCI } from '../lib/logging'; import { Notices } from '../lib/notices'; import { Command, Configuration, Settings } from '../lib/settings'; import * as version from '../lib/version'; -// https://github.com/yargs/yargs/issues/1929 -// https://github.com/evanw/esbuild/issues/1492 -// eslint-disable-next-line @typescript-eslint/no-require-imports -const yargs = require('yargs'); - /* eslint-disable max-len */ /* eslint-disable @typescript-eslint/no-shadow */ // yargs -async function parseCommandLineArguments(args: string[]) { - // Use the following configuration for array arguments: - // - // { type: 'array', default: [], nargs: 1, requiresArg: true } - // - // The default behavior of yargs is to eat all strings following an array argument: - // - // ./prog --arg one two positional => will parse to { arg: ['one', 'two', 'positional'], _: [] } (so no positional arguments) - // ./prog --arg one two -- positional => does not help, for reasons that I can't understand. Still gets parsed incorrectly. - // - // By using the config above, every --arg will only consume one argument, so you can do the following: - // - // ./prog --arg one --arg two position => will parse to { arg: ['one', 'two'], _: ['positional'] }. - - const defaultBrowserCommand: { [key in NodeJS.Platform]?: string } = { - darwin: 'open %u', - win32: 'start %u', - }; - - const initTemplateLanguages = await availableInitLanguages(); - return yargs - .env('CDK') - .usage('Usage: cdk -a COMMAND') - .option('app', { type: 'string', alias: 'a', desc: 'REQUIRED WHEN RUNNING APP: command-line for executing your app or a cloud assembly directory (e.g. "node bin/my-app.js"). Can also be specified in cdk.json or ~/.cdk.json', requiresArg: true }) - .option('build', { type: 'string', desc: 'Command-line for a pre-synth build' }) - .option('context', { type: 'array', alias: 'c', desc: 'Add contextual string parameter (KEY=VALUE)', nargs: 1, requiresArg: true }) - .option('plugin', { type: 'array', alias: 'p', desc: 'Name or path of a node package that extend the CDK features. Can be specified multiple times', nargs: 1 }) - .option('trace', { type: 'boolean', desc: 'Print trace for stack warnings' }) - .option('strict', { type: 'boolean', desc: 'Do not construct stacks with warnings' }) - .option('lookups', { type: 'boolean', desc: 'Perform context lookups (synthesis fails if this is disabled and context lookups need to be performed)', default: true }) - .option('ignore-errors', { type: 'boolean', default: false, desc: 'Ignores synthesis errors, which will likely produce an invalid output' }) - .option('json', { type: 'boolean', alias: 'j', desc: 'Use JSON output instead of YAML when templates are printed to STDOUT', default: false }) - .option('verbose', { type: 'boolean', alias: 'v', desc: 'Show debug logs (specify multiple times to increase verbosity)', default: false }) - .count('verbose') - .option('debug', { type: 'boolean', desc: 'Enable emission of additional debugging information, such as creation stack traces of tokens', default: false }) - .option('profile', { type: 'string', desc: 'Use the indicated AWS profile as the default environment', requiresArg: true }) - .option('proxy', { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified', requiresArg: true }) - .option('ca-bundle-path', { type: 'string', desc: 'Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified', requiresArg: true }) - .option('ec2creds', { type: 'boolean', alias: 'i', default: undefined, desc: 'Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status' }) - .option('version-reporting', { type: 'boolean', desc: 'Include the "AWS::CDK::Metadata" resource in synthesized templates (enabled by default)', default: undefined }) - .option('path-metadata', { type: 'boolean', desc: 'Include "aws:cdk:path" CloudFormation metadata for each resource (enabled by default)', default: undefined }) - .option('asset-metadata', { type: 'boolean', desc: 'Include "aws:asset:*" CloudFormation metadata for resources that uses assets (enabled by default)', default: undefined }) - .option('role-arn', { type: 'string', alias: 'r', desc: 'ARN of Role to use when invoking CloudFormation', default: undefined, requiresArg: true }) - .option('staging', { type: 'boolean', desc: 'Copy assets to the output directory (use --no-staging to disable the copy of assets which allows local debugging via the SAM CLI to reference the original source files)', default: true }) - .option('output', { type: 'string', alias: 'o', desc: 'Emits the synthesized cloud assembly into a directory (default: cdk.out)', requiresArg: true }) - .option('notices', { type: 'boolean', desc: 'Show relevant notices' }) - .option('no-color', { type: 'boolean', desc: 'Removes colors and other style from console output', default: false }) - .option('ci', { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', default: process.env.CI !== undefined }) - .command(['list [STACKS..]', 'ls [STACKS..]'], 'Lists all stacks in the app', (yargs: Argv) => yargs - .option('long', { type: 'boolean', default: false, alias: 'l', desc: 'Display environment information for each stack' }) - .option('show-dependencies', { type: 'boolean', default: false, alias: 'd', desc: 'Display stack dependency information for each stack' }), - ) - .command(['synthesize [STACKS..]', 'synth [STACKS..]'], 'Synthesizes and prints the CloudFormation template for this stack', (yargs: Argv) => yargs - .option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only synthesize requested stacks, don\'t include dependencies' }) - .option('validation', { type: 'boolean', desc: 'After synthesis, validate stacks with the "validateOnSynth" attribute set (can also be controlled with CDK_VALIDATION)', default: true }) - .option('quiet', { type: 'boolean', alias: 'q', desc: 'Do not output CloudFormation Template to stdout', default: false })) - .command('bootstrap [ENVIRONMENTS..]', 'Deploys the CDK toolkit stack into an AWS environment', (yargs: Argv) => yargs - .option('bootstrap-bucket-name', { type: 'string', alias: ['b', 'toolkit-bucket-name'], desc: 'The name of the CDK toolkit bucket; bucket will be created and must not exist', default: undefined }) - .option('bootstrap-kms-key-id', { type: 'string', desc: 'AWS KMS master key ID used for the SSE-KMS encryption', default: undefined, conflicts: 'bootstrap-customer-key' }) - .option('example-permissions-boundary', { type: 'boolean', alias: 'epb', desc: 'Use the example permissions boundary.', default: undefined, conflicts: 'custom-permissions-boundary' }) - .option('custom-permissions-boundary', { type: 'string', alias: 'cpb', desc: 'Use the permissions boundary specified by name.', default: undefined, conflicts: 'example-permissions-boundary' }) - .option('bootstrap-customer-key', { type: 'boolean', desc: 'Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)', default: undefined, conflicts: 'bootstrap-kms-key-id' }) - .option('qualifier', { type: 'string', desc: 'String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.', default: undefined }) - .option('public-access-block-configuration', { type: 'boolean', desc: 'Block public access configuration on CDK toolkit bucket (enabled by default) ', default: undefined }) - .option('tags', { type: 'array', alias: 't', desc: 'Tags to add for the stack (KEY=VALUE)', nargs: 1, requiresArg: true, default: [] }) - .option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }) - .option('trust', { type: 'array', desc: 'The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }) - .option('trust-for-lookup', { type: 'array', desc: 'The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }) - .option('cloudformation-execution-policies', { type: 'array', desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }) - .option('force', { alias: 'f', type: 'boolean', desc: 'Always bootstrap even if it would downgrade template version', default: false }) - .option('termination-protection', { type: 'boolean', default: undefined, desc: 'Toggle CloudFormation termination protection on the bootstrap stacks' }) - .option('show-template', { type: 'boolean', desc: 'Instead of actual bootstrapping, print the current CLI\'s bootstrapping template to stdout for customization', default: false }) - .option('toolkit-stack-name', { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }) - .option('template', { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)' }) - .option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }), - ) - .command('deploy [STACKS..]', 'Deploys the stack(s) named STACKS into your AWS account', (yargs: Argv) => yargs - .option('all', { type: 'boolean', default: false, desc: 'Deploy all available stacks' }) - .option('build-exclude', { type: 'array', alias: 'E', nargs: 1, desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', default: [] }) - .option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' }) - .option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' }) - .option('notification-arns', { type: 'array', desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', nargs: 1, requiresArg: true }) - // @deprecated(v2) -- tags are part of the Cloud Assembly and tags specified here will be overwritten on the next deployment - .option('tags', { type: 'array', alias: 't', desc: 'Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)', nargs: 1, requiresArg: true }) - .option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet) (deprecated)', deprecated: true }) - .option('change-set-name', { type: 'string', desc: 'Name of the CloudFormation change set to create (only if method is not direct)' }) - .options('method', { - alias: 'm', - type: 'string', - choices: ['direct', 'change-set', 'prepare-change-set'], - requiresArg: true, - desc: 'How to perform the deployment. Direct is a bit faster but lacks progress information', - }) - .option('force', { alias: 'f', type: 'boolean', desc: 'Always deploy stack even if templates are identical', default: false }) - .option('parameters', { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} }) - .option('outputs-file', { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true }) - .option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }) - .option('toolkit-stack-name', { type: 'string', desc: 'The name of the existing CDK toolkit stack (only used for app using legacy synthesis)', requiresArg: true }) - .option('progress', { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events' }) - .option('rollback', { - type: 'boolean', - desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + - 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', - }) - // Hack to get '-R' as an alias for '--no-rollback', suggested by: https://github.com/yargs/yargs/issues/1729 - .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) - .option('hotswap', { - type: 'boolean', - desc: "Attempts to perform a 'hotswap' deployment, " + - 'but does not fall back to a full deployment if that is not possible. ' + - 'Instead, changes to any non-hotswappable properties are ignored.' + - 'Do not use this in production environments', - }) - .option('hotswap-fallback', { - type: 'boolean', - desc: "Attempts to perform a 'hotswap' deployment, " + - 'which skips CloudFormation and updates the resources directly, ' + - 'and falls back to a full deployment if that is not possible. ' + - 'Do not use this in production environments', - }) - .option('watch', { - type: 'boolean', - desc: 'Continuously observe the project files, ' + - 'and deploy the given stack(s) automatically when changes are detected. ' + - 'Implies --hotswap by default', - }) - .options('logs', { - type: 'boolean', - default: true, - desc: 'Show CloudWatch log events from all resources in the selected Stacks in the terminal. ' + - "'true' by default, use --no-logs to turn off. " + - "Only in effect if specified alongside the '--watch' option", - }) - .option('concurrency', { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }) - .option('asset-parallelism', { type: 'boolean', desc: 'Whether to build/publish assets in parallel' }) - .option('asset-prebuild', { type: 'boolean', desc: 'Whether to build all assets before deploying the first stack (useful for failing Docker builds)', default: true }) - .option('ignore-no-stacks', { type: 'boolean', desc: 'Whether to deploy if the app contains no stacks', default: false }), - ) - .command('rollback [STACKS..]', 'Rolls back the stack(s) named STACKS to their last stable state', (yargs: Argv) => yargs - .option('all', { type: 'boolean', default: false, desc: 'Roll back all available stacks' }) - .option('toolkit-stack-name', { type: 'string', desc: 'The name of the CDK toolkit stack the environment is bootstrapped with', requiresArg: true }) - .option('force', { - alias: 'f', - type: 'boolean', - desc: 'Orphan all resources for which the rollback operation fails.', - }) - .option('validate-bootstrap-version', { - type: 'boolean', - desc: 'Whether to validate the bootstrap stack version. Defaults to \'true\', disable with --no-validate-bootstrap-version.', - }) - .option('orphan', { - // alias: 'o' conflicts with --output - type: 'array', - nargs: 1, - requiresArg: true, - desc: 'Orphan the given resources, identified by their logical ID (can be specified multiple times)', - default: [], - }), - ) - .command('import [STACK]', 'Import existing resource(s) into the given STACK', (yargs: Argv) => yargs - .option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }) - .option('change-set-name', { type: 'string', desc: 'Name of the CloudFormation change set to create' }) - .option('toolkit-stack-name', { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }) - .option('rollback', { - type: 'boolean', - desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + - 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', - }) - .option('force', { - alias: 'f', - type: 'boolean', - desc: 'Do not abort if the template diff includes updates or deletes. This is probably safe but we\'re not sure, let us know how it goes.', - }) - .option('record-resource-mapping', { - type: 'string', - alias: 'r', - requiresArg: true, - desc: 'If specified, CDK will generate a mapping of existing physical resources to CDK resources to be imported as. The mapping ' + - 'will be written in the given file path. No actual import operation will be performed', - }) - .option('resource-mapping', { - type: 'string', - alias: 'm', - requiresArg: true, - desc: 'If specified, CDK will use the given file to map physical resources to CDK resources for import, instead of interactively ' + - 'asking the user. Can be run from scripts', - }), - ) - .command('watch [STACKS..]', "Shortcut for 'deploy --watch'", (yargs: Argv) => yargs - // I'm fairly certain none of these options, present for 'deploy', make sense for 'watch': - // .option('all', { type: 'boolean', default: false, desc: 'Deploy all available stacks' }) - // .option('ci', { type: 'boolean', desc: 'Force CI detection', default: process.env.CI !== undefined }) - // @deprecated(v2) -- tags are part of the Cloud Assembly and tags specified here will be overwritten on the next deployment - // .option('tags', { type: 'array', alias: 't', desc: 'Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)', nargs: 1, requiresArg: true }) - // .option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }) - // These options, however, are more subtle - I could be convinced some of these should also be available for 'watch': - // .option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' }) - // .option('parameters', { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} }) - // .option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }) - // .option('outputs-file', { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true }) - // .option('notification-arns', { type: 'array', desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', nargs: 1, requiresArg: true }) - .option('build-exclude', { type: 'array', alias: 'E', nargs: 1, desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', default: [] }) - .option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' }) - .option('change-set-name', { type: 'string', desc: 'Name of the CloudFormation change set to create' }) - .option('force', { alias: 'f', type: 'boolean', desc: 'Always deploy stack even if templates are identical', default: false }) - .option('toolkit-stack-name', { type: 'string', desc: 'The name of the existing CDK toolkit stack (only used for app using legacy synthesis)', requiresArg: true }) - .option('progress', { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events' }) - .option('rollback', { - type: 'boolean', - desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + - 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', - }) - // same hack for -R as above in 'deploy' - .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) - .option('hotswap', { - type: 'boolean', - desc: "Attempts to perform a 'hotswap' deployment, " + - 'but does not fall back to a full deployment if that is not possible. ' + - 'Instead, changes to any non-hotswappable properties are ignored.' + - "'true' by default, use --no-hotswap to turn off", - }) - .option('hotswap-fallback', { - type: 'boolean', - desc: "Attempts to perform a 'hotswap' deployment, " + - 'which skips CloudFormation and updates the resources directly, ' + - 'and falls back to a full deployment if that is not possible.', - }) - .options('logs', { - type: 'boolean', - default: true, - desc: 'Show CloudWatch log events from all resources in the selected Stacks in the terminal. ' + - "'true' by default, use --no-logs to turn off", - }) - .option('concurrency', { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }), - ) - .command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', (yargs: Argv) => yargs - .option('all', { type: 'boolean', default: false, desc: 'Destroy all available stacks' }) - .option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only destroy requested stacks, don\'t include dependees' }) - .option('force', { type: 'boolean', alias: 'f', desc: 'Do not ask for confirmation before destroying the stacks' })) - .command('diff [STACKS..]', 'Compares the specified stack with the deployed stack or a local template file, and returns with status 1 if any difference is found', (yargs: Argv) => yargs - .option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only diff requested stacks, don\'t include dependencies' }) - .option('context-lines', { type: 'number', desc: 'Number of context lines to include in arbitrary JSON diff rendering', default: 3, requiresArg: true }) - .option('template', { type: 'string', desc: 'The path to the CloudFormation template to compare with', requiresArg: true }) - .option('strict', { type: 'boolean', desc: 'Do not filter out AWS::CDK::Metadata resources, mangled non-ASCII characters, or the CheckBootstrapVersionRule', default: false }) - .option('security-only', { type: 'boolean', desc: 'Only diff for broadened security changes', default: false }) - .option('fail', { type: 'boolean', desc: 'Fail with exit code 1 in case of diff' }) - .option('processed', { type: 'boolean', desc: 'Whether to compare against the template with Transforms already processed', default: false }) - .option('quiet', { type: 'boolean', alias: 'q', desc: 'Do not print stack name and default message when there is no diff to stdout', default: false }) - .option('change-set', { type: 'boolean', alias: 'changeset', desc: 'Whether to create a changeset to analyze resource replacements. In this mode, diff will use the deploy role instead of the lookup role.', default: true })) - .command('metadata [STACK]', 'Returns all metadata associated with this stack') - .command(['acknowledge [ID]', 'ack [ID]'], 'Acknowledge a notice so that it does not show up anymore') - .command('notices', 'Returns a list of relevant notices', (yargs: Argv) => yargs - .option('unacknowledged', { type: 'boolean', alias: 'u', default: false, desc: 'Returns a list of unacknowledged notices' }), - ) - .command('init [TEMPLATE]', 'Create a new, empty CDK project from a template.', (yargs: Argv) => yargs - .option('language', { type: 'string', alias: 'l', desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)', choices: initTemplateLanguages }) - .option('list', { type: 'boolean', desc: 'List the available templates' }) - .option('generate-only', { type: 'boolean', default: false, desc: 'If true, only generates project files, without executing additional operations such as setting up a git repo, installing dependencies or compiling the project' }), - ) - .command('migrate', false /* hidden from "cdk --help" */, (yargs: Argv) => yargs - .option('stack-name', { type: 'string', alias: 'n', desc: 'The name assigned to the stack created in the new project. The name of the app will be based off this name as well.', requiresArg: true }) - .option('language', { type: 'string', default: 'typescript', alias: 'l', desc: 'The language to be used for the new project', choices: MIGRATE_SUPPORTED_LANGUAGES }) - .option('account', { type: 'string', desc: 'The account to retrieve the CloudFormation stack template from' }) - .option('region', { type: 'string', desc: 'The region to retrieve the CloudFormation stack template from' }) - .option('from-path', { type: 'string', desc: 'The path to the CloudFormation template to migrate. Use this for locally stored templates' }) - .option('from-stack', { type: 'boolean', desc: 'Use this flag to retrieve the template for an existing CloudFormation stack' }) - .option('output-path', { type: 'string', desc: 'The output path for the migrated CDK app' }) - .option('from-scan', { - type: 'string', - desc: 'Determines if a new scan should be created, or the last successful existing scan should be used ' + - '\n options are "new" or "most-recent"', - }) - .option('filter', { - type: 'array', - desc: 'Filters the resource scan based on the provided criteria in the following format: "key1=value1,key2=value2"' + - '\n This field can be passed multiple times for OR style filtering: ' + - '\n filtering options: ' + - '\n resource-identifier: A key-value pair that identifies the target resource. i.e. {"ClusterName", "myCluster"}' + - '\n resource-type-prefix: A string that represents a type-name prefix. i.e. "AWS::DynamoDB::"' + - '\n tag-key: a string that matches resources with at least one tag with the provided key. i.e. "myTagKey"' + - '\n tag-value: a string that matches resources with at least one tag with the provided value. i.e. "myTagValue"', - }) - .option('compress', { type: 'boolean', desc: 'Use this flag to zip the generated CDK app' }), - ) - .command('context', 'Manage cached context values', (yargs: Argv) => yargs - .option('reset', { alias: 'e', desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true }) - .option('force', { alias: 'f', desc: 'Ignore missing key error', type: 'boolean', default: false }) - .option('clear', { desc: 'Clear all context', type: 'boolean' })) - .command(['docs', 'doc'], 'Opens the reference documentation in a browser', (yargs: Argv) => yargs - .option('browser', { - alias: 'b', - desc: 'the command to use to open the browser, using %u as a placeholder for the path of the file to open', - type: 'string', - default: process.platform in defaultBrowserCommand ? defaultBrowserCommand[process.platform] : 'xdg-open %u', - })) - .command('doctor', 'Check your set-up for potential problems') - .version(version.DISPLAY_VERSION) - .demandCommand(1, '') // just print help - .recommendCommands() - .help() - .alias('h', 'help') - .epilogue([ - 'If your app has a single stack, there is no need to specify the stack name', - 'If one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.', - ].join('\n\n')) - .parse(args); -} - if (!process.stdout.isTTY) { // Disable chalk color highlighting process.env.FORCE_COLOR = '0'; } export async function exec(args: string[], synthesizer?: Synthesizer): Promise { - const argv = await parseCommandLineArguments(args); + function makeBrowserDefault(): string | undefined { + const defaultBrowserCommand: { [key in NodeJS.Platform]?: string } = { + darwin: 'open %u', + win32: 'start %u', + }; + + return process.platform in defaultBrowserCommand ? defaultBrowserCommand[process.platform] : 'xdg-open %u'; + } + + const argv = await parseCommandLineArguments(args, makeBrowserDefault(), await availableInitLanguages(), MIGRATE_SUPPORTED_LANGUAGES as string[], version.DISPLAY_VERSION, yargsNegativeAlias); if (argv.verbose) { setLogLevel(argv.verbose); @@ -792,7 +486,7 @@ function arrayFromYargs(xs: string[]): string[] | undefined { return xs.filter(x => x !== ''); } -function yargsNegativeAlias(shortName: S, longName: L) { +function yargsNegativeAlias(shortName: S, longName: L): (argv: T) => T { return (argv: T) => { if (shortName in argv && argv[shortName]) { (argv as any)[longName] = false; diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 6fbe829021441..ac7c1483b8edd 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -28,6 +28,9 @@ "attributions:update": "yarn node-bundle validate --entrypoint lib/index.ts --dont-attribute \"^@aws-cdk/|^cdk-assets|^cdk-cli-wrapper$\" --fix" }, "cdk-build": { + "pre": [ + "yarn yargs-gen" + ], "post": [ "cp ../../node_modules/cdk-from-cfn/index_bg.wasm ./lib/", "cp ../../node_modules/@aws-cdk/aws-service-spec/db.json.gz ./" diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index 66acd12f04ed5..665d5bcba74b6 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -1,30 +1,72 @@ -import { Expression, FreeFunction, Module, Statement, TypeScriptRenderer, code } from '@cdklabs/typewriter'; +import * as fs from 'fs'; +import { Expression, FreeFunction, Module, SelectiveModuleImport, Statement, Type, TypeScriptRenderer, code } from '@cdklabs/typewriter'; import { CliConfig, makeConfig } from '../lib/config'; async function main() { const scope = new Module('aws-cdk'); + + scope.addImport(new SelectiveModuleImport(scope, 'yargs', ['Argv'])); + //scope.addImport(new SelectiveModuleImport(scope, '../lib/api/util/cloudformation/stack-activity-monitor', ['StackActivityProgress'])); + //scope.addImport(new SelectiveModuleImport(scope, '../lib/diff', ['RequireApproval'])); + + scope.addInitialization(code.comment( + 'https://github.com/yargs/yargs/issues/1929', + 'https://github.com/evanw/esbuild/issues/1492', + 'eslint-disable-next-line @typescript-eslint/no-require-imports', + )); + scope.addInitialization(code.stmt.constVar(code.expr.ident('yargs'), code.expr.directCode("require('yargs')"))); + const parseCommandLineArguments = new FreeFunction(scope, { name: 'parseCommandLineArguments', + export: true, + returnType: Type.ANY, + parameters: [ + { name: 'args', type: Type.arrayOf(Type.STRING) }, + { name: 'browserDefault', type: Type.STRING, optional: true }, + { name: 'availableInitLanguages', type: Type.arrayOf(Type.STRING) }, + { name: 'migrateSupportedLanguages', type: Type.arrayOf(Type.STRING) }, + { name: 'version', type: Type.STRING }, + { name: 'yargsNegativeAlias', type: Type.ANY }, + ], }); - parseCommandLineArguments.addBody(makeYargs(makeConfig())); + parseCommandLineArguments.addBody(makeYargs(await makeConfig()/*, scope*/)); const renderer = new TypeScriptRenderer(); // eslint-disable-next-line no-console console.log(renderer.render(scope)); + const eslintBlock = ` +/* eslint-disable comma-spacing */ +/* eslint-disable @typescript-eslint/comma-dangle */ +/* eslint-disable quote-props */ +/* eslint-disable quotes */`; + + fs.writeFileSync('./lib/parse-command-line-arguments.ts', `${eslintBlock}\n`); + fs.appendFileSync('./lib/parse-command-line-arguments.ts', renderer.render(scope)); } interface MiddlewareExpression { - callbacks: Expression; + callback: Expression; applyBeforeValidation?: Expression; } -function makeYargs(config: CliConfig): Statement { - const preamble = `yargs - .env('CDK') - .usage('Usage: cdk -a COMMAND') - `; +// Use the following configuration for array arguments: +// +// { type: 'array', default: [], nargs: 1, requiresArg: true } +// +// The default behavior of yargs is to eat all strings following an array argument: +// +// ./prog --arg one two positional => will parse to { arg: ['one', 'two', 'positional'], _: [] } (so no positional arguments) +// ./prog --arg one two -- positional => does not help, for reasons that I can't understand. Still gets parsed incorrectly. +// +// By using the config above, every --arg will only consume one argument, so you can do the following: +// +// ./prog --arg one --arg two position => will parse to { arg: ['one', 'two'], _: ['positional'] }. +function makeYargs(config: CliConfig/*, scope: ScopeImpl*/): Statement { + //const yargs = new ThingSymbol('yargs', scope); + //let yargsExpr = new SymbolReference(yargs).callMethod('env', code.expr.lit('CDK')); + let yargsExpr: Expression = code.expr.ident('yargs'); + yargsExpr = yargsExpr.callMethod('usage', code.expr.lit('Usage: cdk -a COMMAND')); - let yargsExpr = code.expr.directCode(preamble); for (const command of Object.keys(config.commands)) { const commandFacts = config.commands[command]; const commandArg = commandFacts.arg @@ -48,34 +90,55 @@ function makeYargs(config: CliConfig): Statement { // middleware is a separate function call, so we can't store it with the regular option arguments, as those will all be treated as parameters: // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) middleware = { - callbacks: code.expr.lit(optionFacts.middleware!.callbacks.toString()), + callback: code.expr.builtInFn(optionFacts.middleware!.callback, code.expr.lit(optionFacts.middleware!.args)), applyBeforeValidation: code.expr.lit(optionFacts.middleware!.applyBeforeValidation), }; break; - default: - optionArgs[optionProp] = code.expr.lit((optionFacts as any)[optionProp]); + if ((optionFacts as any)[optionProp].dynamicType === 'parameter') { + optionArgs[optionProp] = code.expr.ident((optionFacts as any)[optionProp].dynamicValue); + } else { + optionArgs[optionProp] = code.expr.lit((optionFacts as any)[optionProp]); + } } } optionsExpr = optionsExpr.callMethod('option', code.expr.lit(`${option}`), code.expr.object(optionArgs)); if (middleware) { - optionsExpr = optionsExpr.callMethod('middleware', middleware.callbacks, middleware.applyBeforeValidation ?? code.expr.UNDEFINED); + optionsExpr = optionsExpr.callMethod('middleware', middleware.callback, middleware.applyBeforeValidation ?? code.expr.UNDEFINED); middleware = undefined; } } - // tail-recursive? yargsExpr = commandFacts.options ? yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description), optionsExpr) : yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description)); } - return code.stmt.ret(yargsExpr); + return code.stmt.ret(makeEpilogue(yargsExpr)); } -main().then(() => { +function makeEpilogue(prefix: Expression) { + let completeThing = prefix.callMethod('version', code.expr.ident('version')); + completeThing = completeThing.callMethod('demandCommand', code.expr.lit(1), code.expr.lit("''")); // just print help + completeThing = completeThing.callMethod('recommendCommands'); + completeThing = completeThing.callMethod('help'); + completeThing = completeThing.callMethod('alias', code.expr.lit('h'), code.expr.lit('help')); + completeThing = completeThing.callMethod('epilogue', code.expr.lit([ + 'If your app has a single stack, there is no need to specify the stack name', + 'If one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.', + ].join('\n\n'))); -}).catch(() => { + completeThing = completeThing.callMethod('parse', code.expr.ident('args')); -}); \ No newline at end of file + return completeThing; +} + +main().then(() => { + // eslint-disable-next-line no-console + console.log('waoh 1'); + +}).catch((e) => { + // eslint-disable-next-line no-console + console.error(e); +}); diff --git a/tools/@aws-cdk/yargs-gen/lib/config.ts b/tools/@aws-cdk/yargs-gen/lib/config.ts index 4de430b9dcf8e..626e66f721564 100644 --- a/tools/@aws-cdk/yargs-gen/lib/config.ts +++ b/tools/@aws-cdk/yargs-gen/lib/config.ts @@ -5,7 +5,6 @@ interface YargsCommand { description: string; options?: { [optionName: string]: YargsOption }; aliases?: string[]; - //args?: { [argName: string]: YargsArg }; arg?: YargsArg; } @@ -53,7 +52,8 @@ interface YargsOption { } export interface Middleware { - callbacks: MiddlewareFunction | ReadonlyArray; + callback: string; + args: string[]; applyBeforeValidation?: boolean; } @@ -61,20 +61,7 @@ export interface CliConfig { commands: { [commandName: string]: YargsCommand }; } -// copied from yargs -type MiddlewareFunction = (args: any) => void; -// end copied from yargs - -function yargsNegativeAlias(shortName: S, longName: L) { - return (argv: T) => { - if (shortName in argv && argv[shortName]) { - (argv as any)[longName] = false; - } - return argv; - }; -} - -export function makeConfig(): CliConfig { +export async function makeConfig(): Promise { const config: CliConfig = { commands: { deploy: { @@ -112,7 +99,8 @@ export function makeConfig(): CliConfig { hidden: true, // Hack to get '-R' as an alias for '--no-rollback', suggested by: https://github.com/yargs/yargs/issues/1729 middleware: { - callbacks: yargsNegativeAlias('R', 'rollback'), + callback: 'yargsNegativeAlias', + args: ['R', 'rollback'], applyBeforeValidation: true, }, }, @@ -252,7 +240,8 @@ export function makeConfig(): CliConfig { type: 'boolean', hidden: true, middleware: { - callbacks: yargsNegativeAlias('R', 'rollback'), + callback: 'yargsNegativeAlias', + args: ['R', 'rollback'], applyBeforeValidation: true, }, }, @@ -336,7 +325,7 @@ export function makeConfig(): CliConfig { variadic: false, }, options: { - 'language': { type: 'string', alias: 'l', desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)' /*, choices: initTemplateLanguages*/ }, // TODO: preamble, this initTemplateLanguages variable needs to go as a statement there. + 'language': { type: 'string', alias: 'l', desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)', choices: DynamicValue.fromParameter('availableInitLanguages') } as any, // TODO: preamble, this initTemplateLanguages variable needs to go as a statement there. 'list': { type: 'boolean', desc: 'List the available templates' }, 'generate-only': { type: 'boolean', default: false, desc: 'If true, only generates project files, without executing additional operations such as setting up a git repo, installing dependencies or compiling the project' }, }, @@ -345,7 +334,7 @@ export function makeConfig(): CliConfig { description: false as any, options: { 'stack-name': { type: 'string', alias: 'n', desc: 'The name assigned to the stack created in the new project. The name of the app will be based off this name as well.', requiresArg: true }, - 'language': { type: 'string', default: 'typescript', alias: 'l', desc: 'The language to be used for the new project'/*, choices: MIGRATE_SUPPORTED_LANGUAGES*/ }, // TODO: preamble + 'language': { type: 'string', default: 'typescript', alias: 'l', desc: 'The language to be used for the new project', choices: DynamicValue.fromParameter('migrateSupportedLanguages') as any }, 'account': { type: 'string', desc: 'The account to retrieve the CloudFormation stack template from' }, 'region': { type: 'string', desc: 'The region to retrieve the CloudFormation stack template from' }, 'from-path': { type: 'string', desc: 'The path to the CloudFormation template to migrate. Use this for locally stored templates' }, @@ -385,7 +374,7 @@ export function makeConfig(): CliConfig { alias: 'b', desc: 'the command to use to open the browser, using %u as a placeholder for the path of the file to open', type: 'string', - //default: process.platform in defaultBrowserCommand ? defaultBrowserCommand[process.platform] : 'xdg-open %u', // TODO: preamble + default: DynamicValue.fromParameter('browserDefault'), }, }, }, @@ -396,4 +385,18 @@ export function makeConfig(): CliConfig { }; return config; -} \ No newline at end of file +} + +export interface DynamicResult { + dynamicType: 'parameter'; + dynamicValue: string; +} + +export class DynamicValue { + public static fromParameter(parameterName: string): DynamicResult { + return { + dynamicType: 'parameter', + dynamicValue: parameterName, + }; + } +} From 13c60eb902000e82b9df9966133e1b0d7a8f6017 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Mon, 21 Oct 2024 16:32:01 -0700 Subject: [PATCH 08/23] comment --- tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index 665d5bcba74b6..7d3f9ff0addb9 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -135,9 +135,6 @@ function makeEpilogue(prefix: Expression) { } main().then(() => { - // eslint-disable-next-line no-console - console.log('waoh 1'); - }).catch((e) => { // eslint-disable-next-line no-console console.error(e); From 4740b49268dd88ef7ae4577539856a01505f3d5b Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Tue, 22 Oct 2024 11:36:15 -0700 Subject: [PATCH 09/23] console --- tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts index 7d3f9ff0addb9..2437044d34290 100755 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts @@ -32,8 +32,6 @@ async function main() { parseCommandLineArguments.addBody(makeYargs(await makeConfig()/*, scope*/)); const renderer = new TypeScriptRenderer(); - // eslint-disable-next-line no-console - console.log(renderer.render(scope)); const eslintBlock = ` /* eslint-disable comma-spacing */ /* eslint-disable @typescript-eslint/comma-dangle */ From 37c55ee846dfa614e243f1bec9ac7006052a2679 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Tue, 22 Oct 2024 16:01:29 -0700 Subject: [PATCH 10/23] move to the CLI...using ts-node is probably too large a deviation from our standard build to not break something --- .../aws-cdk}/bin/yargs-gen | 0 packages/aws-cdk/bin/yargs-gen.ts | 20 ++++++ .../aws-cdk}/lib/config.ts | 64 ++----------------- packages/aws-cdk/package.json | 4 +- tools/@aws-cdk/yargs-gen/lib/index.ts | 3 +- .../yargs-gen/{bin => lib}/yargs-gen.ts | 26 ++------ tools/@aws-cdk/yargs-gen/lib/yargs-types.ts | 50 +++++++++++++++ tools/@aws-cdk/yargs-gen/package.json | 3 - 8 files changed, 82 insertions(+), 88 deletions(-) rename {tools/@aws-cdk/yargs-gen => packages/aws-cdk}/bin/yargs-gen (100%) create mode 100644 packages/aws-cdk/bin/yargs-gen.ts rename {tools/@aws-cdk/yargs-gen => packages/aws-cdk}/lib/config.ts (94%) rename tools/@aws-cdk/yargs-gen/{bin => lib}/yargs-gen.ts (86%) mode change 100755 => 100644 create mode 100644 tools/@aws-cdk/yargs-gen/lib/yargs-types.ts diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen b/packages/aws-cdk/bin/yargs-gen similarity index 100% rename from tools/@aws-cdk/yargs-gen/bin/yargs-gen rename to packages/aws-cdk/bin/yargs-gen diff --git a/packages/aws-cdk/bin/yargs-gen.ts b/packages/aws-cdk/bin/yargs-gen.ts new file mode 100644 index 0000000000000..8597325faa3fc --- /dev/null +++ b/packages/aws-cdk/bin/yargs-gen.ts @@ -0,0 +1,20 @@ +import * as fs from 'fs'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { renderYargs } from '@aws-cdk/yargs-gen'; +import { makeConfig } from '../lib/config'; + +async function main() { + const eslintBlock = ` +/* eslint-disable comma-spacing */ +/* eslint-disable @typescript-eslint/comma-dangle */ +/* eslint-disable quote-props */ +/* eslint-disable quotes */`; + + fs.writeFileSync('./lib/parse-command-line-arguments.ts', `${eslintBlock}\n`); + fs.appendFileSync('./lib/parse-command-line-arguments.ts', await renderYargs(await makeConfig())); +} + +main().then(() => { +}).catch((e) => { + throw e; +}); \ No newline at end of file diff --git a/tools/@aws-cdk/yargs-gen/lib/config.ts b/packages/aws-cdk/lib/config.ts similarity index 94% rename from tools/@aws-cdk/yargs-gen/lib/config.ts rename to packages/aws-cdk/lib/config.ts index 626e66f721564..fa196cb894332 100644 --- a/tools/@aws-cdk/yargs-gen/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -1,66 +1,10 @@ +import { CliConfig } from '@aws-cdk/yargs-gen'; +import { StackActivityProgress } from './api/util/cloudformation/stack-activity-monitor'; +import { RequireApproval } from './diff'; + /* eslint-disable quote-props */ // called by a build tool to generate parse-command-line-arguments.ts -interface YargsCommand { - description: string; - options?: { [optionName: string]: YargsOption }; - aliases?: string[]; - arg?: YargsArg; -} - -// might need to expand -interface YargsArg { - name: string; - variadic: boolean; -} - -enum RequireApproval { - Never = 'never', - - AnyChange = 'any-change', - - Broadening = 'broadening', -} - -/** - * Supported display modes for stack deployment activity - */ -enum StackActivityProgress { - /** - * Displays a progress bar with only the events for the resource currently being deployed - */ - BAR = 'bar', - - /** - * Displays complete history with all CloudFormation stack events - */ - EVENTS = 'events', -} - -interface YargsOption { - type: 'string' | 'array' | 'number' | 'boolean' | 'count'; - desc?: string; - default?: any; - deprecated?: boolean | string; - choices?: ReadonlyArray; - alias?: string; - conflicts?: string | readonly string[] | { [key: string]: string | readonly string[] }; - nargs?: number; - requiresArg?: boolean; - hidden?: boolean; - middleware?: Middleware; -} - -export interface Middleware { - callback: string; - args: string[]; - applyBeforeValidation?: boolean; -} - -export interface CliConfig { - commands: { [commandName: string]: YargsCommand }; -} - export async function makeConfig(): Promise { const config: CliConfig = { commands: { diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index ac7c1483b8edd..334b94e28f810 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -7,7 +7,7 @@ }, "scripts": { "build": "cdk-build", - "yargs-gen": "yargs-gen", + "yargs-gen": "yarn ts-node bin/yargs-gen.ts", "watch": "cdk-watch", "lint": "cdk-lint", "pkglint": "pkglint -f", @@ -69,7 +69,6 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/cdk-build-tools": "0.0.0", - "@aws-cdk/yargs-gen": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@octokit/rest": "^18.12.0", "@types/archiver": "^5.3.4", @@ -105,6 +104,7 @@ "@aws-cdk/cloudformation-diff": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/region-info": "0.0.0", + "@aws-cdk/yargs-gen": "0.0.0", "@jsii/check-node": "1.103.1", "archiver": "^5.3.2", "aws-sdk": "^2.1691.0", diff --git a/tools/@aws-cdk/yargs-gen/lib/index.ts b/tools/@aws-cdk/yargs-gen/lib/index.ts index f03c2281a9140..9e1623713b0a3 100644 --- a/tools/@aws-cdk/yargs-gen/lib/index.ts +++ b/tools/@aws-cdk/yargs-gen/lib/index.ts @@ -1 +1,2 @@ -export * from './config'; +export * from './yargs-gen'; +export * from './yargs-types'; diff --git a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts old mode 100755 new mode 100644 similarity index 86% rename from tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts rename to tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 2437044d34290..cf331084af6f6 --- a/tools/@aws-cdk/yargs-gen/bin/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -1,14 +1,10 @@ -import * as fs from 'fs'; import { Expression, FreeFunction, Module, SelectiveModuleImport, Statement, Type, TypeScriptRenderer, code } from '@cdklabs/typewriter'; -import { CliConfig, makeConfig } from '../lib/config'; +import { CliConfig } from './yargs-types'; -async function main() { +export async function renderYargs(config: CliConfig): Promise { const scope = new Module('aws-cdk'); scope.addImport(new SelectiveModuleImport(scope, 'yargs', ['Argv'])); - //scope.addImport(new SelectiveModuleImport(scope, '../lib/api/util/cloudformation/stack-activity-monitor', ['StackActivityProgress'])); - //scope.addImport(new SelectiveModuleImport(scope, '../lib/diff', ['RequireApproval'])); - scope.addInitialization(code.comment( 'https://github.com/yargs/yargs/issues/1929', 'https://github.com/evanw/esbuild/issues/1492', @@ -29,17 +25,9 @@ async function main() { { name: 'yargsNegativeAlias', type: Type.ANY }, ], }); - parseCommandLineArguments.addBody(makeYargs(await makeConfig()/*, scope*/)); - - const renderer = new TypeScriptRenderer(); - const eslintBlock = ` -/* eslint-disable comma-spacing */ -/* eslint-disable @typescript-eslint/comma-dangle */ -/* eslint-disable quote-props */ -/* eslint-disable quotes */`; + parseCommandLineArguments.addBody(makeYargs(config/*, scope*/)); - fs.writeFileSync('./lib/parse-command-line-arguments.ts', `${eslintBlock}\n`); - fs.appendFileSync('./lib/parse-command-line-arguments.ts', renderer.render(scope)); + return new TypeScriptRenderer().render(scope); } interface MiddlewareExpression { @@ -131,9 +119,3 @@ function makeEpilogue(prefix: Expression) { return completeThing; } - -main().then(() => { -}).catch((e) => { - // eslint-disable-next-line no-console - console.error(e); -}); diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts new file mode 100644 index 0000000000000..d158019e7b79a --- /dev/null +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts @@ -0,0 +1,50 @@ +interface YargsCommand { + description: string; + options?: { [optionName: string]: YargsOption }; + aliases?: string[]; + arg?: YargsArg; +} + +// might need to expand +interface YargsArg { + name: string; + variadic: boolean; +} + +interface YargsCommand { + description: string; + options?: { [optionName: string]: YargsOption }; + aliases?: string[]; + arg?: YargsArg; +} + +// might need to expand +interface YargsArg { + name: string; + variadic: boolean; +} + +interface YargsOption { + type: 'string' | 'array' | 'number' | 'boolean' | 'count'; + desc?: string; + default?: any; + deprecated?: boolean | string; + choices?: ReadonlyArray; + alias?: string; + conflicts?: string | readonly string[] | { [key: string]: string | readonly string[] }; + nargs?: number; + requiresArg?: boolean; + hidden?: boolean; + middleware?: Middleware; +} + +export interface Middleware { + callback: string; + args: string[]; + applyBeforeValidation?: boolean; +} + +export interface CliConfig { + commands: { [commandName: string]: YargsCommand }; +} + diff --git a/tools/@aws-cdk/yargs-gen/package.json b/tools/@aws-cdk/yargs-gen/package.json index 2ce182efaebc3..6e61839cc8d26 100644 --- a/tools/@aws-cdk/yargs-gen/package.json +++ b/tools/@aws-cdk/yargs-gen/package.json @@ -8,9 +8,6 @@ "url": "https://github.com/aws/aws-cdk.git", "directory": "tools/@aws-cdk/yargs-gen" }, - "bin": { - "yargs-gen": "bin/yargs-gen" - }, "main": "./lib/index.js", "types": "./lib/index.d.ts", "scripts": { From 5353bb74301637795d73a19532e9f8100a1e1eb2 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Tue, 22 Oct 2024 16:21:17 -0700 Subject: [PATCH 11/23] comments : --- packages/aws-cdk/bin/yargs-gen.ts | 1 + packages/aws-cdk/lib/config.ts | 2 +- tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 2 -- tools/@aws-cdk/yargs-gen/lib/yargs-types.ts | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk/bin/yargs-gen.ts b/packages/aws-cdk/bin/yargs-gen.ts index 8597325faa3fc..9e0ef6a4f528b 100644 --- a/packages/aws-cdk/bin/yargs-gen.ts +++ b/packages/aws-cdk/bin/yargs-gen.ts @@ -4,6 +4,7 @@ import { renderYargs } from '@aws-cdk/yargs-gen'; import { makeConfig } from '../lib/config'; async function main() { + // TODO: once the typewriter changes are in, we can remove this block of eslint const eslintBlock = ` /* eslint-disable comma-spacing */ /* eslint-disable @typescript-eslint/comma-dangle */ diff --git a/packages/aws-cdk/lib/config.ts b/packages/aws-cdk/lib/config.ts index fa196cb894332..0453aaed2d997 100644 --- a/packages/aws-cdk/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -3,7 +3,7 @@ import { StackActivityProgress } from './api/util/cloudformation/stack-activity- import { RequireApproval } from './diff'; /* eslint-disable quote-props */ -// called by a build tool to generate parse-command-line-arguments.ts +// called by yargs-gen to generate parse-command-line-arguments.ts export async function makeConfig(): Promise { const config: CliConfig = { diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index cf331084af6f6..b5119b8cfa0aa 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -48,8 +48,6 @@ interface MiddlewareExpression { // // ./prog --arg one --arg two position => will parse to { arg: ['one', 'two'], _: ['positional'] }. function makeYargs(config: CliConfig/*, scope: ScopeImpl*/): Statement { - //const yargs = new ThingSymbol('yargs', scope); - //let yargsExpr = new SymbolReference(yargs).callMethod('env', code.expr.lit('CDK')); let yargsExpr: Expression = code.expr.ident('yargs'); yargsExpr = yargsExpr.callMethod('usage', code.expr.lit('Usage: cdk -a COMMAND')); diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts index d158019e7b79a..e5689693be3f9 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts @@ -5,7 +5,6 @@ interface YargsCommand { arg?: YargsArg; } -// might need to expand interface YargsArg { name: string; variadic: boolean; From 814e36fc1e6693d8aef6d628bae064a55d43fdc9 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Tue, 22 Oct 2024 16:48:30 -0700 Subject: [PATCH 12/23] var-naming --- tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index b5119b8cfa0aa..2207d941148bb 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -103,17 +103,17 @@ function makeYargs(config: CliConfig/*, scope: ScopeImpl*/): Statement { } function makeEpilogue(prefix: Expression) { - let completeThing = prefix.callMethod('version', code.expr.ident('version')); - completeThing = completeThing.callMethod('demandCommand', code.expr.lit(1), code.expr.lit("''")); // just print help - completeThing = completeThing.callMethod('recommendCommands'); - completeThing = completeThing.callMethod('help'); - completeThing = completeThing.callMethod('alias', code.expr.lit('h'), code.expr.lit('help')); - completeThing = completeThing.callMethod('epilogue', code.expr.lit([ + let completeDefinition = prefix.callMethod('version', code.expr.ident('version')); + completeDefinition = completeDefinition.callMethod('demandCommand', code.expr.lit(1), code.expr.lit("''")); // just print help + completeDefinition = completeDefinition.callMethod('recommendCommands'); + completeDefinition = completeDefinition.callMethod('help'); + completeDefinition = completeDefinition.callMethod('alias', code.expr.lit('h'), code.expr.lit('help')); + completeDefinition = completeDefinition.callMethod('epilogue', code.expr.lit([ 'If your app has a single stack, there is no need to specify the stack name', 'If one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.', ].join('\n\n'))); - completeThing = completeThing.callMethod('parse', code.expr.ident('args')); + completeDefinition = completeDefinition.callMethod('parse', code.expr.ident('args')); - return completeThing; + return completeDefinition; } From 545128c611ad5f6d89364462996be3de2572ad05 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Wed, 23 Oct 2024 14:14:23 -0700 Subject: [PATCH 13/23] un-ignore --- packages/aws-cdk/.gitignore | 4 +- .../lib/parse-command-line-arguments.ts | 332 ++++++++++++++++++ 2 files changed, 333 insertions(+), 3 deletions(-) create mode 100644 packages/aws-cdk/lib/parse-command-line-arguments.ts diff --git a/packages/aws-cdk/.gitignore b/packages/aws-cdk/.gitignore index 771239ed8ce39..a50bbd2b6f9d6 100644 --- a/packages/aws-cdk/.gitignore +++ b/packages/aws-cdk/.gitignore @@ -41,6 +41,4 @@ test/integ/cli/*.d.ts junit.xml lib/**/*.wasm -db.json.gz - -lib/parse-command-line-arguments.ts \ No newline at end of file +db.json.gz \ No newline at end of file diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts new file mode 100644 index 0000000000000..a8f4ebd517f2d --- /dev/null +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -0,0 +1,332 @@ + +/* eslint-disable @typescript-eslint/comma-dangle, comma-spacing, max-len, quotes, quote-props*/ +import { Argv } from "yargs"; + +// @ts-ignore TS6133 +function parseCommandLineArguments(args: Array, browserDefault?: string, availableInitLanguages: Array, migrateSupportedLanguages: Array, version: string, yargsNegativeAlias: any): any { + return yargs.usage("Usage: cdk -a COMMAND").command(['deploy [STACKS..]'], "Deploys the stack(s) named STACKS into your AWS account", (yargs: Argv) => yargs.option("all", { + "type": "boolean", + "desc": "Deploy all available stacks", + "default": false + }).option("build-exclude", { + "type": "array", + "alias": "E", + "nargs": 1, + "desc": "Do not rebuild asset with the given ID. Can be specified multiple times", + "default": [] + }).option("exclusively", { + "type": "boolean", + "alias": "e", + "desc": "Only deploy requested stacks, don't include dependencies" + }).option("require-approval", { + "type": "string", + "choices": ["never","any-change","broadening"], + "desc": "What security-sensitive changes need manual approval" + }).option("notification-arns", { + "type": "array", + "desc": "ARNs of SNS topics that CloudFormation will notify with stack related events", + "nargs": 1, + "requiresArg": true + }).option("tags", { + "type": "array", + "alias": "t", + "desc": "Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)", + "nargs": 1, + "requiresArg": true + }).option("execute", { + "type": "boolean", + "desc": "Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet) (deprecated)", + "deprecated": true + }).option("change-set-name", { + "type": "string", + "desc": "Name of the CloudFormation change set to create (only if method is not direct)" + }).option("method", { + "alias": "m", + "type": "string", + "choices": ["direct","change-set","prepare-change-set"], + "requiresArg": true, + "desc": "How to perform the deployment. Direct is a bit faster but lacks progress information" + }).option("force", { + "alias": "f", + "type": "boolean", + "desc": "Always deploy stack even if templates are identical", + "default": false + }).option("parameters", { + "type": "array", + "desc": "Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)", + "nargs": 1, + "requiresArg": true, + "default": {} + }).option("outputs-file", { + "type": "string", + "alias": "O", + "desc": "Path to file where stack outputs will be written as JSON", + "requiresArg": true + }).option("previous-parameters", { + "type": "boolean", + "default": true, + "desc": "Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)" + }).option("toolkit-stack-name", { + "type": "string", + "desc": "The name of the existing CDK toolkit stack (only used for app using legacy synthesis)", + "requiresArg": true + }).option("progress", { + "type": "string", + "choices": ["bar","events"], + "desc": "Display mode for stack activity events" + }).option("rollback", { + "type": "boolean", + "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" + }).option("R", { + "type": "boolean", + "hidden": true + }).middleware(yargsNegativeAlias(["R","rollback"]), true).option("hotswap", { + "type": "boolean", + "desc": "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.Do not use this in production environments" + }).option("hotswap-fallback", { + "type": "boolean", + "desc": "Attempts to perform a 'hotswap' deployment, which skips CloudFormation and updates the resources directly, and falls back to a full deployment if that is not possible. Do not use this in production environments" + }).option("watch", { + "type": "boolean", + "desc": "Continuously observe the project files, and deploy the given stack(s) automatically when changes are detected. Implies --hotswap by default" + }).option("logs", { + "type": "boolean", + "default": true, + "desc": "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off. Only in effect if specified alongside the '--watch' option" + }).option("concurrency", { + "type": "number", + "desc": "Maximum number of simultaneous deployments (dependency permitting) to execute.", + "default": 1, + "requiresArg": true + }).option("asset-parallelism", { + "type": "boolean", + "desc": "Whether to build/publish assets in parallel" + }).option("asset-prebuild", { + "type": "boolean", + "desc": "Whether to build all assets before deploying the first stack (useful for failing Docker builds)", + "default": true + }).option("ignore-no-stacks", { + "type": "boolean", + "desc": "Whether to deploy if the app contains no stacks", + "default": false + })).command(['rollback [STACKS..]'], "Rolls back the stack(s) named STACKS to their last stable state", (yargs: Argv) => yargs.option("all", { + "type": "boolean", + "default": false, + "desc": "Roll back all available stacks" + }).option("toolkit-stack-name", { + "type": "string", + "desc": "The name of the CDK toolkit stack the environment is bootstrapped with", + "requiresArg": true + }).option("force", { + "alias": "f", + "type": "boolean", + "desc": "Orphan all resources for which the rollback operation fails." + }).option("validate-bootstrap-version", { + "type": "boolean", + "desc": "Whether to validate the bootstrap stack version. Defaults to 'true', disable with --no-validate-bootstrap-version." + }).option("orphan", { + "type": "array", + "nargs": 1, + "requiresArg": true, + "desc": "Orphan the given resources, identified by their logical ID (can be specified multiple times)", + "default": [] + })).command(['import [STACK]'], "Import existing resource(s) into the given STACK", (yargs: Argv) => yargs.option("execute", { + "type": "boolean", + "desc": "Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)", + "default": true + }).option("change-set-name", { + "type": "string", + "desc": "Name of the CloudFormation change set to create" + }).option("toolkit-stack-name", { + "type": "string", + "desc": "The name of the CDK toolkit stack to create", + "requiresArg": true + }).option("rollback", { + "type": "boolean", + "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" + }).option("force", { + "alias": "f", + "type": "boolean", + "desc": "Do not abort if the template diff includes updates or deletes. This is probably safe but we're not sure, let us know how it goes." + }).option("record-resource-mapping", { + "type": "string", + "alias": "r", + "requiresArg": true, + "desc": "If specified, CDK will generate a mapping of existing physical resources to CDK resources to be imported as. The mapping will be written in the given file path. No actual import operation will be performed" + }).option("resource-mapping", { + "type": "string", + "alias": "m", + "requiresArg": true, + "desc": "If specified, CDK will use the given file to map physical resources to CDK resources for import, instead of interactively asking the user. Can be run from scripts" + })).command(['watch [STACKS..]'], "Shortcut for 'deploy --watch'", (yargs: Argv) => yargs.option("build-exclude", { + "type": "array", + "alias": "E", + "nargs": 1, + "desc": "Do not rebuild asset with the given ID. Can be specified multiple times", + "default": [] + }).option("exclusively", { + "type": "boolean", + "alias": "e", + "desc": "Only deploy requested stacks, don't include dependencies" + }).option("change-set-name", { + "type": "string", + "desc": "Name of the CloudFormation change set to create" + }).option("force", { + "alias": "f", + "type": "boolean", + "desc": "Always deploy stack even if templates are identical", + "default": false + }).option("toolkit-stack-name", { + "type": "string", + "desc": "The name of the existing CDK toolkit stack (only used for app using legacy synthesis)", + "requiresArg": true + }).option("progress", { + "type": "string", + "choices": ["bar","events"], + "desc": "Display mode for stack activity events" + }).option("rollback", { + "type": "boolean", + "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" + }).option("R", { + "type": "boolean", + "hidden": true + }).middleware(yargsNegativeAlias(["R","rollback"]), true).option("hotswap", { + "type": "boolean", + "desc": "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.'true' by default, use --no-hotswap to turn off" + }).option("hotswap-fallback", { + "type": "boolean", + "desc": "Attempts to perform a 'hotswap' deployment, which skips CloudFormation and updates the resources directly, and falls back to a full deployment if that is not possible." + }).option("logs", { + "type": "boolean", + "default": true, + "desc": "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off" + }).option("concurrency", { + "type": "number", + "desc": "Maximum number of simultaneous deployments (dependency permitting) to execute.", + "default": 1, + "requiresArg": true + })).command(['destroy [STACKS..]'], "Destroy the stack(s) named STACKS", (yargs: Argv) => yargs.option("all", { + "type": "boolean", + "default": false, + "desc": "Destroy all available stacks" + }).option("exclusively", { + "type": "boolean", + "alias": "e", + "desc": "Only destroy requested stacks, don't include dependees" + }).option("force", { + "type": "boolean", + "alias": "f", + "desc": "Do not ask for confirmation before destroying the stacks" + })).command(['diff [STACKS..]'], "Compares the specified stack with the deployed stack or a local template file, and returns with status 1 if any difference is found", (yargs: Argv) => yargs.option("exclusively", { + "type": "boolean", + "alias": "e", + "desc": "Only diff requested stacks, don't include dependencies" + }).option("context-lines", { + "type": "number", + "desc": "Number of context lines to include in arbitrary JSON diff rendering", + "default": 3, + "requiresArg": true + }).option("template", { + "type": "string", + "desc": "The path to the CloudFormation template to compare with", + "requiresArg": true + }).option("strict", { + "type": "boolean", + "desc": "Do not filter out AWS::CDK::Metadata resources, mangled non-ASCII characters, or the CheckBootstrapVersionRule", + "default": false + }).option("security-only", { + "type": "boolean", + "desc": "Only diff for broadened security changes", + "default": false + }).option("fail", { + "type": "boolean", + "desc": "Fail with exit code 1 in case of diff" + }).option("processed", { + "type": "boolean", + "desc": "Whether to compare against the template with Transforms already processed", + "default": false + }).option("quiet", { + "type": "boolean", + "alias": "q", + "desc": "Do not print stack name and default message when there is no diff to stdout", + "default": false + }).option("change-set", { + "type": "boolean", + "alias": "changeset", + "desc": "Whether to create a changeset to analyze resource replacements. In this mode, diff will use the deploy role instead of the lookup role.", + "default": true + })).command(['metadata [STACK]'], "Returns all metadata associated with this stack").command(['acknowledge [ID]', 'ack [ID]'], "Acknowledge a notice so that it does not show up anymore").command(['notices'], "Returns a list of relevant notices", (yargs: Argv) => yargs.option("unacknowledged", { + "type": "boolean", + "alias": "u", + "default": false, + "desc": "Returns a list of unacknowledged notices" + })).command(['init [TEMPLATE]'], "Create a new, empty CDK project from a template.", (yargs: Argv) => yargs.option("language", { + "type": "string", + "alias": "l", + "desc": "The language to be used for the new project (default can be configured in ~/.cdk.json)", + "choices": availableInitLanguages + }).option("list", { + "type": "boolean", + "desc": "List the available templates" + }).option("generate-only", { + "type": "boolean", + "default": false, + "desc": "If true, only generates project files, without executing additional operations such as setting up a git repo, installing dependencies or compiling the project" + })).command(['migrate'], false, (yargs: Argv) => yargs.option("stack-name", { + "type": "string", + "alias": "n", + "desc": "The name assigned to the stack created in the new project. The name of the app will be based off this name as well.", + "requiresArg": true + }).option("language", { + "type": "string", + "default": "typescript", + "alias": "l", + "desc": "The language to be used for the new project", + "choices": migrateSupportedLanguages + }).option("account", { + "type": "string", + "desc": "The account to retrieve the CloudFormation stack template from" + }).option("region", { + "type": "string", + "desc": "The region to retrieve the CloudFormation stack template from" + }).option("from-path", { + "type": "string", + "desc": "The path to the CloudFormation template to migrate. Use this for locally stored templates" + }).option("from-stack", { + "type": "boolean", + "desc": "Use this flag to retrieve the template for an existing CloudFormation stack" + }).option("output-path", { + "type": "string", + "desc": "The output path for the migrated CDK app" + }).option("from-scan", { + "type": "string", + "desc": "Determines if a new scan should be created, or the last successful existing scan should be used \n options are \"new\" or \"most-recent\"" + }).option("filter", { + "type": "array", + "desc": "Filters the resource scan based on the provided criteria in the following format: \"key1=value1,key2=value2\"\n This field can be passed multiple times for OR style filtering: \n filtering options: \n resource-identifier: A key-value pair that identifies the target resource. i.e. {\"ClusterName\", \"myCluster\"}\n resource-type-prefix: A string that represents a type-name prefix. i.e. \"AWS::DynamoDB::\"\n tag-key: a string that matches resources with at least one tag with the provided key. i.e. \"myTagKey\"\n tag-value: a string that matches resources with at least one tag with the provided value. i.e. \"myTagValue\"" + }).option("compress", { + "type": "boolean", + "desc": "Use this flag to zip the generated CDK app" + })).command(['context'], "Manage cached context values", (yargs: Argv) => yargs.option("reset", { + "alias": "e", + "desc": "The context key (or its index) to reset", + "type": "string", + "requiresArg": true + }).option("force", { + "alias": "f", + "desc": "Ignore missing key error", + "type": "boolean", + "default": false + }).option("clear", { + "desc": "Clear all context", + "type": "boolean" + })).command(['docs', 'doc '], "Opens the reference documentation in a browser", (yargs: Argv) => yargs.option("browser", { + "alias": "b", + "desc": "the command to use to open the browser, using %u as a placeholder for the path of the file to open", + "type": "string", + "default": browserDefault + })).command(['doctor'], "Check your set-up for potential problems").version(version).demandCommand(1, "''").recommendCommands().help().alias("h", "help").epilogue("If your app has a single stack, there is no need to specify the stack name\n\nIf one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.").parse(args); +}// https://github.com/yargs/yargs/issues/1929 +// https://github.com/evanw/esbuild/issues/1492 +// eslint-disable-next-line @typescript-eslint/no-require-imports +const yargs = require('yargs'); \ No newline at end of file From 869765bf41facd2f2573db3d3b1b0a1a951295ed Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Wed, 23 Oct 2024 14:16:11 -0700 Subject: [PATCH 14/23] \n --- packages/aws-cdk/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk/.gitignore b/packages/aws-cdk/.gitignore index a50bbd2b6f9d6..b4917729cbcb0 100644 --- a/packages/aws-cdk/.gitignore +++ b/packages/aws-cdk/.gitignore @@ -41,4 +41,4 @@ test/integ/cli/*.d.ts junit.xml lib/**/*.wasm -db.json.gz \ No newline at end of file +db.json.gz From f88ce55a48f2ff6ca4cee341cbb10fdbebec3e0c Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Wed, 23 Oct 2024 15:39:08 -0700 Subject: [PATCH 15/23] docs --- packages/aws-cdk/CONTRIBUTING.md | 23 ++++++++ packages/aws-cdk/bin/yargs-gen.ts | 10 +--- packages/aws-cdk/lib/config.ts | 19 ++++-- tools/@aws-cdk/yargs-gen/README.md | 70 ++++------------------- tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 15 +++-- 5 files changed, 59 insertions(+), 78 deletions(-) diff --git a/packages/aws-cdk/CONTRIBUTING.md b/packages/aws-cdk/CONTRIBUTING.md index 59792a73c40b1..fb915d28cb39c 100644 --- a/packages/aws-cdk/CONTRIBUTING.md +++ b/packages/aws-cdk/CONTRIBUTING.md @@ -1,3 +1,26 @@ +## CLI Commands + +All CDK CLI Commands are defined in `lib/config.ts`. This file is translated +into a valid `yargs` configuration by `bin/yargs-gen`, which is generated by `@aws-cdk/yargs-gen`. +The `yargs` configuration is generated into the function `parseCommandLineArguments()`, +in `lib/parse-command-line-arguments.ts`, and is checked into git for readability and +inspectability; do not edit this file by hand, as every subsequent `yarn build` will +overwrite any manual edits. If you need to leverage a `yargs` feature not used by +the CLI, you must add support for it to `@aws-cdk/yargs-gen`. + +Note that `bin/yargs-gen` is executed by `ts-node`, which allows `config.ts` to +reference functions and other identifiers defined in the CLI before the CLI is +built. + +### Dynamic Values + +Some values, such as the user's platform, cannot be computed at build time. +Some commands depend on these values, and thus `yargs-gen` must generate the +code to compute these values at build time. + +The only way to do this today is to reference a parameter with `DynamicValue.fromParameter`. +The caller of `parseCommandLineArguments()` must pass the parameter. + ## Integration Tests Unit tests are automatically run as part of the regular build. Integration tests diff --git a/packages/aws-cdk/bin/yargs-gen.ts b/packages/aws-cdk/bin/yargs-gen.ts index 9e0ef6a4f528b..93ed5d10196e6 100644 --- a/packages/aws-cdk/bin/yargs-gen.ts +++ b/packages/aws-cdk/bin/yargs-gen.ts @@ -4,15 +4,7 @@ import { renderYargs } from '@aws-cdk/yargs-gen'; import { makeConfig } from '../lib/config'; async function main() { - // TODO: once the typewriter changes are in, we can remove this block of eslint - const eslintBlock = ` -/* eslint-disable comma-spacing */ -/* eslint-disable @typescript-eslint/comma-dangle */ -/* eslint-disable quote-props */ -/* eslint-disable quotes */`; - - fs.writeFileSync('./lib/parse-command-line-arguments.ts', `${eslintBlock}\n`); - fs.appendFileSync('./lib/parse-command-line-arguments.ts', await renderYargs(await makeConfig())); + fs.writeFileSync('./lib/parse-command-line-arguments.ts', await renderYargs(await makeConfig())); } main().then(() => { diff --git a/packages/aws-cdk/lib/config.ts b/packages/aws-cdk/lib/config.ts index 0453aaed2d997..a20b4721d4b7f 100644 --- a/packages/aws-cdk/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -3,10 +3,13 @@ import { StackActivityProgress } from './api/util/cloudformation/stack-activity- import { RequireApproval } from './diff'; /* eslint-disable quote-props */ -// called by yargs-gen to generate parse-command-line-arguments.ts +/** + * Source of truth for all CDK CLI commands. `yargs-gen` translates this into the `yargs` definition + * in `lib/parse-command-line-arguments.ts`. + */ export async function makeConfig(): Promise { - const config: CliConfig = { + return { commands: { deploy: { description: 'Deploys the stack(s) named STACKS into your AWS account', @@ -327,16 +330,24 @@ export async function makeConfig(): Promise { }, }, }; - - return config; } +/** + * The result of a DynamicValue call + */ export interface DynamicResult { dynamicType: 'parameter'; dynamicValue: string; } +/** + * Informs the code library, `@aws-cdk/yargs-gen`, that + * this value references an entity not defined in this configuration file. + */ export class DynamicValue { + /** + * Instructs `yargs-gen` to retrieve this value from the parameter with passed name. + */ public static fromParameter(parameterName: string): DynamicResult { return { dynamicType: 'parameter', diff --git a/tools/@aws-cdk/yargs-gen/README.md b/tools/@aws-cdk/yargs-gen/README.md index b2aeac3be97cd..224b207afb367 100644 --- a/tools/@aws-cdk/yargs-gen/README.md +++ b/tools/@aws-cdk/yargs-gen/README.md @@ -1,71 +1,21 @@ -# spec2cdk +# yargs-gen -Generates AWS CDK L1s in TypeScript from `@aws-cdk/aws-service-spec`. +Generates CDK CLI `yargs` configuration from the source of truth in `packages/aws-cdk/lib/config.ts` ## Usage ```ts -import { generateAll } from '@aws-cdk/spec2cdk'; +import { renderYargs } from '@aws-cdk/yargs-gen'; -declare const outputDir: string; +declare const config: CliConfig; -// Generate all modules -await generateAll(outputPath, { outputPath }); - -// Generate modules with specific instructions -await generate({ - 'aws-lambda': { services: ['AWS::Lambda'] }, - 'aws-s3': { services: ['AWS::S3'] }, -}, { outputPath }); +fs.writeFileSync('./lib/parse-command-line-arguments.ts', await renderYargs(config)); ``` -Refer to code autocompletion for all options. - -### Use as @aws-cdk/cfn2ts replacement - -The package provides a binary that can be used as a drop-in replacement of the legacy `@aws-cdk/cfn2ts` package. -At a code level, import `@aws-cdk/spec2cdk/lib/cfn2ts` for a drop-in replacement. - -## Temporary Schemas - -You can import additional, temporary CloudFormation Registry Schemas to test new functionality that is not yet published in `@aws-cdk/aws-service-spec`. -To do this, drop the schema file into `temporary-schemas/us-east-1` and it will be imported on top of the default model. - -## CLI +This package exports `renderYargs()`, which accepts the CLI command config as input and returns the yargs definition for it as a string. -A CLI is available for testing and ad-hoc usage. -However its API is limited and you should use the programmatic interface for implementations. +### Dynamic Values -```console -Usage: - spec2cdk [--option=value] - -Arguments: - OUTPUT-PATH The directory the generated code will be written to - -Options: - --augmentations [string] [default: %moduleName%/%serviceShortName%-augmentations.generated.ts] - File and path pattern for generated augmentations files - --augmentations-support [boolean] - Generates additional files required for augmentation files to compile. Use for testing only - --clear-output [boolean] - Completely delete the output path before generating new files - --debug [boolean] - Show additional debug output - -h, --help [boolean] - Show this help - --metrics [string] [default: %moduleName%/%serviceShortName%-canned-metrics.generated.ts] - File and path pattern for generated canned metrics files - --pattern [string] [default: %moduleName%/%serviceShortName%.generated.ts] - File and path pattern for generated files - -s, --service [array] - Generate files only for a specific service, e.g. AWS::S3 - -Path patterns can use the following variables: - - %moduleName% The name of the module, e.g. aws-lambda - %serviceName% The full name of the service, e.g. aws-lambda - %serviceShortName% The short name of the service, e.g. lambda - -Note that %moduleName% and %serviceName% can be different if multiple services are generated into a single module. -``` +Some values must be computed at runtime, when a command is run. This is achieved with dynamic values; +if the framework sees a CLI option with a `dynamicValue`, then the framework will reference the corresponding parameter. +We should automatically generate the parameter definitions, instead of manually adding them, in the future. diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 2207d941148bb..6e5c0e4d629dc 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -5,11 +5,16 @@ export async function renderYargs(config: CliConfig): Promise { const scope = new Module('aws-cdk'); scope.addImport(new SelectiveModuleImport(scope, 'yargs', ['Argv'])); - scope.addInitialization(code.comment( - 'https://github.com/yargs/yargs/issues/1929', - 'https://github.com/evanw/esbuild/issues/1492', - 'eslint-disable-next-line @typescript-eslint/no-require-imports', - )); + + scope.addInitialization( + code.comment('-------------------------------------------------------------------------------------------'), + code.comment('GENERATED FROM packages/aws-cdk/lib/config.ts.'), + code.comment('Do not edit by hand; all changes will be overwritten at build time from the config file.'), + code.comment('-------------------------------------------------------------------------------------------'), + ); + // 'https://github.com/yargs/yargs/issues/1929', + // 'https://github.com/evanw/esbuild/issues/1492', + // 'eslint-disable-next-line @typescript-eslint/no-require-imports', scope.addInitialization(code.stmt.constVar(code.expr.ident('yargs'), code.expr.directCode("require('yargs')"))); const parseCommandLineArguments = new FreeFunction(scope, { From f5a6eec077b53ef967e2e0d571d1240df65903f7 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Thu, 24 Oct 2024 14:53:19 -0700 Subject: [PATCH 16/23] dynamic expressions-ish + global options --- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 20 ++++ packages/aws-cdk/lib/config.ts | 82 +++++-------- .../lib/parse-command-line-arguments.ts | 109 +++++++++++++++++- tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 99 +++++++--------- tools/@aws-cdk/yargs-gen/lib/yargs-types.ts | 30 +++++ 5 files changed, 228 insertions(+), 112 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index 0e959c0a2a5ee..34f5825b5ff4e 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -80,6 +80,7 @@ Flags come in three types: | [@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId](#aws-cdkaws-rdssetcorrectvaluefordatabaseinstancereadreplicainstanceresourceid) | When enabled, the value of property `instanceResourceId` in construct `DatabaseInstanceReadReplica` will be set to the correct value which is `DbiResourceId` instead of currently `DbInstanceArn` | 2.161.0 | (fix) | | [@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics](#aws-cdkcorecfnincluderejectcomplexresourceupdatecreatepolicyintrinsics) | When enabled, CFN templates added with `cfn-include` will error if the template contains Resource Update or Create policies with CFN Intrinsics that include non-primitive values. | 2.161.0 | (fix) | | [@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy](#aws-cdkaws-stepfunctions-tasksfixrunecstaskpolicy) | When enabled, the resource of IAM Run Ecs policy generated by SFN EcsRunTask will reference the definition, instead of constructing ARN. | 2.163.0 | (fix) | +| [@aws-cdk/aws-dynamodb:resourcePolicyPerReplica](#aws-cdkaws-dynamodbresourcepolicyperreplica) | When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas | V2NEXT | (fix) | @@ -143,6 +144,7 @@ The following json shows the current recommended set of flags, as `cdk init` wou "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, @@ -1509,4 +1511,22 @@ When this feature flag is enabled, if the task definition is created in the stac | 2.163.0 | `false` | `true` | +### @aws-cdk/aws-dynamodb:resourcePolicyPerReplica + +*When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas* (fix) + +If this flag is not set, the default behavior for `TableV2` is to use a different `resourcePolicy` for each replica. + +If this flag is set to false, the behavior is that each replica shares the same `resourcePolicy` as the source table. +This will prevent you from creating a new table which has an additional replica and a resource policy. + +This is a feature flag as the old behavior was technically incorrect but users may have come to depend on it. + + +| Since | Default | Recommended | +| ----- | ----- | ----- | +| (not in v1) | | | +| V2NEXT | `false` | `true` | + + diff --git a/packages/aws-cdk/lib/config.ts b/packages/aws-cdk/lib/config.ts index 1d55ebc480b2c..63c8280290fde 100644 --- a/packages/aws-cdk/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -1,4 +1,4 @@ -import { CliConfig } from '@aws-cdk/yargs-gen'; +import { CliConfig, DynamicValue } from '@aws-cdk/yargs-gen'; import { StackActivityProgress } from './api/util/cloudformation/stack-activity-monitor'; import { RequireApproval } from './diff'; @@ -34,7 +34,7 @@ export async function makeConfig(): Promise { 'output': { type: 'string', alias: 'o', desc: 'Emits the synthesized cloud assembly into a directory (default: cdk.out)', requiresArg: true }, 'notices': { type: 'boolean', desc: 'Show relevant notices' }, 'no-color': { type: 'boolean', desc: 'Removes colors and other style from console output', default: false }, - 'ci': { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', default: process.env.CI !== undefined }, + 'ci': { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', default: DynamicValue.fromInline(() => process.env.CI !== undefined) }, 'unstable': { type: 'array', desc: 'Opt in to specific unstable features. Can be specified multiple times.', default: [] }, }, commands: { @@ -63,33 +63,33 @@ export async function makeConfig(): Promise { 'quiet': { type: 'boolean', alias: 'q', desc: 'Do not output CloudFormation Template to stdout', default: false }, }, }, - bootstrap: { - arg: { - name: 'ENVIRONMENTS', - variadic: true, - }, - description: 'Deploys the CDK toolkit stack into an AWS environment', - options: { - 'bootstrap-bucket-name': { type: 'string', alias: ['b', 'toolkit-bucket-name'], desc: 'The name of the CDK toolkit bucket; bucket will be created and must not exist', default: undefined }, - 'bootstrap-kms-key-id': { type: 'string', desc: 'AWS KMS master key ID used for the SSE-KMS encryption', default: undefined, conflicts: 'bootstrap-customer-key' }, - 'example-permissions-boundary': { type: 'boolean', alias: 'epb', desc: 'Use the example permissions boundary.', default: undefined, conflicts: 'custom-permissions-boundary' }, - 'custom-permissions-boundary': { type: 'string', alias: 'cpb', desc: 'Use the permissions boundary specified by name.', default: undefined, conflicts: 'example-permissions-boundary' }, - 'bootstrap-customer-key': { type: 'boolean', desc: 'Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)', default: undefined, conflicts: 'bootstrap-kms-key-id' }, - 'qualifier': { type: 'string', desc: 'String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.', default: undefined }, - 'public-access-block-configuration': { type: 'boolean', desc: 'Block public access configuration on CDK toolkit bucket (enabled by default) ', default: undefined }, - 'tags': { type: 'array', alias: 't', desc: 'Tags to add for the stack (KEY=VALUE)', nargs: 1, requiresArg: true, default: [] }, - 'execute': { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }, - 'trust': { type: 'array', desc: 'The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, - 'trust-for-lookup': { type: 'array', desc: 'The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, - 'cloudformation-execution-policies': { type: 'array', desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, - 'force': { alias: 'f', type: 'boolean', desc: 'Always bootstrap even if it would downgrade template version', default: false }, - 'termination-protection': { type: 'boolean', default: undefined, desc: 'Toggle CloudFormation termination protection on the bootstrap stacks' }, - 'show-template': { type: 'boolean', desc: 'Instead of actual bootstrapping, print the current CLI\'s bootstrapping template to stdout for customization', default: false }, - 'toolkit-stack-name': { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }, - 'template': { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)' }, - 'previous-parameters': { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }, - }, - }, + //bootstrap: { + //arg: { + //name: 'ENVIRONMENTS', + //variadic: true, + //}, + //description: 'Deploys the CDK toolkit stack into an AWS environment', + //options: { + //'bootstrap-bucket-name': { type: 'string', alias: ['b', 'toolkit-bucket-name'], desc: 'The name of the CDK toolkit bucket; bucket will be created and must not exist', default: undefined }, + //'bootstrap-kms-key-id': { type: 'string', desc: 'AWS KMS master key ID used for the SSE-KMS encryption', default: undefined, conflicts: 'bootstrap-customer-key' }, + //'example-permissions-boundary': { type: 'boolean', alias: 'epb', desc: 'Use the example permissions boundary.', default: undefined, conflicts: 'custom-permissions-boundary' }, + //'custom-permissions-boundary': { type: 'string', alias: 'cpb', desc: 'Use the permissions boundary specified by name.', default: undefined, conflicts: 'example-permissions-boundary' }, + //'bootstrap-customer-key': { type: 'boolean', desc: 'Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)', default: undefined, conflicts: 'bootstrap-kms-key-id' }, + //'qualifier': { type: 'string', desc: 'String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.', default: undefined }, + //'public-access-block-configuration': { type: 'boolean', desc: 'Block public access configuration on CDK toolkit bucket (enabled by default) ', default: undefined }, + //'tags': { type: 'array', alias: 't', desc: 'Tags to add for the stack (KEY=VALUE)', nargs: 1, requiresArg: true, default: [] }, + //'execute': { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }, + //'trust': { type: 'array', desc: 'The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, + //'trust-for-lookup': { type: 'array', desc: 'The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, + //'cloudformation-execution-policies': { type: 'array', desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, + //'force': { alias: 'f', type: 'boolean', desc: 'Always bootstrap even if it would downgrade template version', default: false }, + //'termination-protection': { type: 'boolean', default: undefined, desc: 'Toggle CloudFormation termination protection on the bootstrap stacks' }, + //'show-template': { type: 'boolean', desc: 'Instead of actual bootstrapping, print the current CLI\'s bootstrapping template to stdout for customization', default: false }, + //'toolkit-stack-name': { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }, + //'template': { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)' }, + //'previous-parameters': { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }, + //}, + //}, gc: { description: 'Garbage collect assets', arg: { @@ -425,27 +425,3 @@ export async function makeConfig(): Promise { }, }; } - -/** - * The result of a DynamicValue call - */ -export interface DynamicResult { - dynamicType: 'parameter'; - dynamicValue: string; -} - -/** - * Informs the code library, `@aws-cdk/yargs-gen`, that - * this value references an entity not defined in this configuration file. - */ -export class DynamicValue { - /** - * Instructs `yargs-gen` to retrieve this value from the parameter with passed name. - */ - public static fromParameter(parameterName: string): DynamicResult { - return { - dynamicType: 'parameter', - dynamicValue: parameterName, - }; - } -} diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index 0e1797eef8247..033bf2085c519 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -3,7 +3,114 @@ import { Argv } from "yargs"; // @ts-ignore TS6133 function parseCommandLineArguments(args: Array, browserDefault?: string, availableInitLanguages: Array, migrateSupportedLanguages: Array, version: string, yargsNegativeAlias: any): any { - return yargs.usage("Usage: cdk -a COMMAND").command(['list [STACKS..]', 'ls [STACKS..]'], "Lists all stacks in the app", (yargs: Argv) => yargs.option("long", { + return yargs.usage("Usage: cdk -a COMMAND").option("app", { + "type": "string", + "alias": "a", + "desc": "REQUIRED WHEN RUNNING APP: command-line for executing your app or a cloud assembly directory (e.g. \"node bin/my-app.js\"). Can also be specified in cdk.json or ~/.cdk.json", + "requiresArg": true + }).option("build", { + "type": "string", + "desc": "Command-line for a pre-synth build" + }).option("context", { + "type": "array", + "alias": "c", + "desc": "Add contextual string parameter (KEY=VALUE)", + "nargs": 1, + "requiresArg": true + }).option("plugin", { + "type": "array", + "alias": "p", + "desc": "Name or path of a node package that extend the CDK features. Can be specified multiple times", + "nargs": 1 + }).option("trace", { + "type": "boolean", + "desc": "Print trace for stack warnings" + }).option("strict", { + "type": "boolean", + "desc": "Do not construct stacks with warnings" + }).option("lookups", { + "type": "boolean", + "desc": "Perform context lookups (synthesis fails if this is disabled and context lookups need to be performed)", + "default": true + }).option("ignore-errors", { + "type": "boolean", + "default": false, + "desc": "Ignores synthesis errors, which will likely produce an invalid output" + }).option("json", { + "type": "boolean", + "alias": "j", + "desc": "Use JSON output instead of YAML when templates are printed to STDOUT", + "default": false + }).option("verbose", { + "type": "boolean", + "alias": "v", + "desc": "Show debug logs (specify multiple times to increase verbosity)", + "default": false, + "count": true + }).option("debug", { + "type": "boolean", + "desc": "Enable emission of additional debugging information, such as creation stack traces of tokens", + "default": false + }).option("profile", { + "type": "string", + "desc": "Use the indicated AWS profile as the default environment", + "requiresArg": true + }).option("proxy", { + "type": "string", + "desc": "Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified", + "requiresArg": true + }).option("ca-bundle-path", { + "type": "string", + "desc": "Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified", + "requiresArg": true + }).option("ec2creds", { + "type": "boolean", + "alias": "i", + "default": undefined, + "desc": "Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status" + }).option("version-reporting", { + "type": "boolean", + "desc": "Include the \"AWS::CDK::Metadata\" resource in synthesized templates (enabled by default)", + "default": undefined + }).option("path-metadata", { + "type": "boolean", + "desc": "Include \"aws:cdk:path\" CloudFormation metadata for each resource (enabled by default)", + "default": undefined + }).option("asset-metadata", { + "type": "boolean", + "desc": "Include \"aws:asset:*\" CloudFormation metadata for resources that uses assets (enabled by default)", + "default": undefined + }).option("role-arn", { + "type": "string", + "alias": "r", + "desc": "ARN of Role to use when invoking CloudFormation", + "default": undefined, + "requiresArg": true + }).option("staging", { + "type": "boolean", + "desc": "Copy assets to the output directory (use --no-staging to disable the copy of assets which allows local debugging via the SAM CLI to reference the original source files)", + "default": true + }).option("output", { + "type": "string", + "alias": "o", + "desc": "Emits the synthesized cloud assembly into a directory (default: cdk.out)", + "requiresArg": true + }).option("notices", { + "type": "boolean", + "desc": "Show relevant notices" + }).option("no-color", { + "type": "boolean", + "desc": "Removes colors and other style from console output", + "default": false + }).option("ci", { + "type": "boolean", + "desc": "Force CI detection. If CI=true then logs will be sent to stdout instead of stderr", + "default": process.env.CI !== undefined + }).option("unstable", { + "type": "array", + "desc": "Opt in to specific unstable features. Can be specified multiple times.", + "default": [] + }).command(['list [STACKS..]', 'ls [STACKS..]'], "Lists all stacks in the app", (yargs: Argv) => yargs.option("long", { "type": "boolean", "default": false, "alias": "l", diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 1ae9a758cd99c..41daa07b1758c 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -1,4 +1,5 @@ import { Expression, FreeFunction, Module, SelectiveModuleImport, Statement, Type, TypeScriptRenderer, code } from '@cdklabs/typewriter'; +import { NULL } from '@cdklabs/typewriter/lib/expressions/builder'; import { CliConfig, YargsOption } from './yargs-types'; export async function renderYargs(config: CliConfig): Promise { @@ -54,11 +55,10 @@ interface MiddlewareExpression { // ./prog --arg one --arg two position => will parse to { arg: ['one', 'two'], _: ['positional'] }. function makeYargs(config: CliConfig/*, scope: ScopeImpl*/): Statement { let yargsExpr: Expression = code.expr.ident('yargs'); - yargsExpr = yargsExpr.callMethod('usage', code.expr.lit('Usage: cdk -a COMMAND')); + yargsExpr = yargsExpr.callMethod('usage', lit('Usage: cdk -a COMMAND')); - //for (const option of Object.keys(config.globalOptions)) { - - //} + // we must compute global options first, as they are not part of an argument to a command call + yargsExpr = makeOptions(yargsExpr, config.globalOptions); for (const command of Object.keys(config.commands)) { const commandFacts = config.commands[command]; @@ -71,44 +71,11 @@ function makeYargs(config: CliConfig/*, scope: ScopeImpl*/): Statement { // must compute options before we compute the full command, because in yargs, the options are an argument to the command call. let optionsExpr: Expression = code.expr.directCode('(yargs: Argv) => yargs'); - - /* - for (const option of Object.keys(commandFacts.options ?? {})) { - // each option can define at most one middleware call; if we need more, handle a list of these instead - let middleware: MiddlewareExpression | undefined = undefined; - const optionFacts = commandFacts.options![option]; - const optionArgs: { [key: string]: Expression } = {}; - for (const optionProp of Object.keys(optionFacts)) { - switch (optionProp) { - case 'middleware': - // middleware is a separate function call, so we can't store it with the regular option arguments, as those will all be treated as parameters: - // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) - middleware = { - callback: code.expr.builtInFn(optionFacts.middleware!.callback, code.expr.lit(optionFacts.middleware!.args)), - applyBeforeValidation: code.expr.lit(optionFacts.middleware!.applyBeforeValidation), - }; - break; - default: - if ( (optionFacts as any)[optionProp] && (optionFacts as any)[optionProp].dynamicType === 'parameter') { - optionArgs[optionProp] = code.expr.ident((optionFacts as any)[optionProp].dynamicValue); - } else { - optionArgs[optionProp] = code.expr.lit((optionFacts as any)[optionProp]); - } - } - } - - optionsExpr = optionsExpr.callMethod('option', code.expr.lit(`${option}`), code.expr.object(optionArgs)); - if (middleware) { - optionsExpr = optionsExpr.callMethod('middleware', middleware.callback, middleware.applyBeforeValidation ?? code.expr.UNDEFINED); - middleware = undefined; - } - } - */ optionsExpr = makeOptions(optionsExpr, commandFacts.options ?? {}); yargsExpr = commandFacts.options - ? yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description), optionsExpr) - : yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), code.expr.lit(commandFacts.description)); + ? yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), lit(commandFacts.description), optionsExpr) + : yargsExpr.callMethod('command', code.expr.directCode(`['${command}${commandArg}'${aliases}]`), lit(commandFacts.description)); } return code.stmt.ret(makeEpilogue(yargsExpr)); @@ -122,25 +89,30 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: YargsO const optionProps = options[option]; const optionArgs: { [key: string]: Expression } = {}; for (const optionProp of Object.keys(optionProps)) { - switch (optionProp) { - case 'middleware': - // middleware is a separate function call, so we can't store it with the regular option arguments, as those will all be treated as parameters: - // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) - middleware = { - callback: code.expr.builtInFn(optionProps.middleware!.callback, code.expr.lit(optionProps.middleware!.args)), - applyBeforeValidation: code.expr.lit(optionProps.middleware!.applyBeforeValidation), - }; - break; - default: - if ((optionProps as any)[optionProp] && (optionProps as any)[optionProp].dynamicType === 'parameter') { - optionArgs[optionProp] = code.expr.ident((optionProps as any)[optionProp].dynamicValue); - } else { - optionArgs[optionProp] = code.expr.lit((optionProps as any)[optionProp]); - } + if (optionProp === 'middleware') { + // middleware is a separate function call, so we can't store it with the regular option arguments, as those will all be treated as parameters: + // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) + middleware = { + callback: code.expr.builtInFn(optionProps.middleware!.callback, lit(optionProps.middleware!.args)), + applyBeforeValidation: lit(optionProps.middleware!.applyBeforeValidation), + }; + break; + } else { + const optionValue = (optionProps as any)[optionProp]; + if (optionValue && optionValue.dynamicType === 'parameter') { + optionArgs[optionProp] = code.expr.ident(optionValue.dynamicValue); + } else if (optionValue && optionValue.dynamicType === 'function') { + const inlineFunction: string = optionValue.dynamicValue.toString(); + const NUMBER_OF_SPACES_BETWEEN_ARROW_AND_CODE = 3; + // this only works with arrow functions, like () => + optionArgs[optionProp] = code.expr.directCode(inlineFunction.substring(inlineFunction.indexOf('=>') + NUMBER_OF_SPACES_BETWEEN_ARROW_AND_CODE)); + } else { + optionArgs[optionProp] = lit(optionValue); + } } } - optionsExpr = optionsExpr.callMethod('option', code.expr.lit(`${option}`), code.expr.object(optionArgs)); + optionsExpr = optionsExpr.callMethod('option', lit(option), code.expr.object(optionArgs)); if (middleware) { optionsExpr = optionsExpr.callMethod('middleware', middleware.callback, middleware.applyBeforeValidation ?? code.expr.UNDEFINED); middleware = undefined; @@ -152,11 +124,11 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: YargsO function makeEpilogue(prefix: Expression) { let completeDefinition = prefix.callMethod('version', code.expr.ident('version')); - completeDefinition = completeDefinition.callMethod('demandCommand', code.expr.lit(1), code.expr.lit("''")); // just print help + completeDefinition = completeDefinition.callMethod('demandCommand', lit(1), lit("''")); // just print help completeDefinition = completeDefinition.callMethod('recommendCommands'); completeDefinition = completeDefinition.callMethod('help'); - completeDefinition = completeDefinition.callMethod('alias', code.expr.lit('h'), code.expr.lit('help')); - completeDefinition = completeDefinition.callMethod('epilogue', code.expr.lit([ + completeDefinition = completeDefinition.callMethod('alias', lit('h'), lit('help')); + completeDefinition = completeDefinition.callMethod('epilogue', lit([ 'If your app has a single stack, there is no need to specify the stack name', 'If one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.', ].join('\n\n'))); @@ -165,3 +137,14 @@ function makeEpilogue(prefix: Expression) { return completeDefinition; } + +function lit(value: any): Expression { + switch (value) { + case undefined: + return code.expr.UNDEFINED; + case NULL: + return code.expr.NULL; + default: + return code.expr.lit(value); + } +} \ No newline at end of file diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts index d79a563d6905a..63a5a76de2728 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts @@ -49,3 +49,33 @@ export interface CliConfig { commands: { [commandName: string]: YargsCommand }; } +/** + * The result of a DynamicValue call + */ +export interface DynamicResult { + dynamicType: 'parameter' | 'function'; + dynamicValue: string | (() => any); +} + +/** + * Informs the code library, `@aws-cdk/yargs-gen`, that + * this value references an entity not defined in this configuration file. + */ +export class DynamicValue { + /** + * Instructs `yargs-gen` to retrieve this value from the parameter with passed name. + */ + public static fromParameter(parameterName: string): DynamicResult { + return { + dynamicType: 'parameter', + dynamicValue: parameterName, + }; + } + + public static fromInline(f: () => any): DynamicResult { + return { + dynamicType: 'function', + dynamicValue: f, + }; + } +} From dad04795d802dbccaf661b0920a720458235167d Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Thu, 24 Oct 2024 14:56:49 -0700 Subject: [PATCH 17/23] bootstrap --- packages/aws-cdk/lib/config.ts | 54 +++++------ .../lib/parse-command-line-arguments.ts | 89 +++++++++++++++++++ 2 files changed, 116 insertions(+), 27 deletions(-) diff --git a/packages/aws-cdk/lib/config.ts b/packages/aws-cdk/lib/config.ts index 63c8280290fde..04a44d12b9b90 100644 --- a/packages/aws-cdk/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -63,33 +63,33 @@ export async function makeConfig(): Promise { 'quiet': { type: 'boolean', alias: 'q', desc: 'Do not output CloudFormation Template to stdout', default: false }, }, }, - //bootstrap: { - //arg: { - //name: 'ENVIRONMENTS', - //variadic: true, - //}, - //description: 'Deploys the CDK toolkit stack into an AWS environment', - //options: { - //'bootstrap-bucket-name': { type: 'string', alias: ['b', 'toolkit-bucket-name'], desc: 'The name of the CDK toolkit bucket; bucket will be created and must not exist', default: undefined }, - //'bootstrap-kms-key-id': { type: 'string', desc: 'AWS KMS master key ID used for the SSE-KMS encryption', default: undefined, conflicts: 'bootstrap-customer-key' }, - //'example-permissions-boundary': { type: 'boolean', alias: 'epb', desc: 'Use the example permissions boundary.', default: undefined, conflicts: 'custom-permissions-boundary' }, - //'custom-permissions-boundary': { type: 'string', alias: 'cpb', desc: 'Use the permissions boundary specified by name.', default: undefined, conflicts: 'example-permissions-boundary' }, - //'bootstrap-customer-key': { type: 'boolean', desc: 'Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)', default: undefined, conflicts: 'bootstrap-kms-key-id' }, - //'qualifier': { type: 'string', desc: 'String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.', default: undefined }, - //'public-access-block-configuration': { type: 'boolean', desc: 'Block public access configuration on CDK toolkit bucket (enabled by default) ', default: undefined }, - //'tags': { type: 'array', alias: 't', desc: 'Tags to add for the stack (KEY=VALUE)', nargs: 1, requiresArg: true, default: [] }, - //'execute': { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }, - //'trust': { type: 'array', desc: 'The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, - //'trust-for-lookup': { type: 'array', desc: 'The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, - //'cloudformation-execution-policies': { type: 'array', desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, - //'force': { alias: 'f', type: 'boolean', desc: 'Always bootstrap even if it would downgrade template version', default: false }, - //'termination-protection': { type: 'boolean', default: undefined, desc: 'Toggle CloudFormation termination protection on the bootstrap stacks' }, - //'show-template': { type: 'boolean', desc: 'Instead of actual bootstrapping, print the current CLI\'s bootstrapping template to stdout for customization', default: false }, - //'toolkit-stack-name': { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }, - //'template': { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)' }, - //'previous-parameters': { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }, - //}, - //}, + bootstrap: { + arg: { + name: 'ENVIRONMENTS', + variadic: true, + }, + description: 'Deploys the CDK toolkit stack into an AWS environment', + options: { + 'bootstrap-bucket-name': { type: 'string', alias: ['b', 'toolkit-bucket-name'], desc: 'The name of the CDK toolkit bucket; bucket will be created and must not exist', default: undefined }, + 'bootstrap-kms-key-id': { type: 'string', desc: 'AWS KMS master key ID used for the SSE-KMS encryption', default: undefined, conflicts: 'bootstrap-customer-key' }, + 'example-permissions-boundary': { type: 'boolean', alias: 'epb', desc: 'Use the example permissions boundary.', default: undefined, conflicts: 'custom-permissions-boundary' }, + 'custom-permissions-boundary': { type: 'string', alias: 'cpb', desc: 'Use the permissions boundary specified by name.', default: undefined, conflicts: 'example-permissions-boundary' }, + 'bootstrap-customer-key': { type: 'boolean', desc: 'Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)', default: undefined, conflicts: 'bootstrap-kms-key-id' }, + 'qualifier': { type: 'string', desc: 'String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.', default: undefined }, + 'public-access-block-configuration': { type: 'boolean', desc: 'Block public access configuration on CDK toolkit bucket (enabled by default) ', default: undefined }, + 'tags': { type: 'array', alias: 't', desc: 'Tags to add for the stack (KEY=VALUE)', nargs: 1, requiresArg: true, default: [] }, + 'execute': { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }, + 'trust': { type: 'array', desc: 'The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, + 'trust-for-lookup': { type: 'array', desc: 'The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, + 'cloudformation-execution-policies': { type: 'array', desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }, + 'force': { alias: 'f', type: 'boolean', desc: 'Always bootstrap even if it would downgrade template version', default: false }, + 'termination-protection': { type: 'boolean', default: undefined, desc: 'Toggle CloudFormation termination protection on the bootstrap stacks' }, + 'show-template': { type: 'boolean', desc: 'Instead of actual bootstrapping, print the current CLI\'s bootstrapping template to stdout for customization', default: false }, + 'toolkit-stack-name': { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }, + 'template': { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)' }, + 'previous-parameters': { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }, + }, + }, gc: { description: 'Garbage collect assets', arg: { diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index 033bf2085c519..80d7bf4f8e090 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -133,6 +133,95 @@ function parseCommandLineArguments(args: Array, browserDefault?: string, "alias": "q", "desc": "Do not output CloudFormation Template to stdout", "default": false + })).command(['bootstrap [ENVIRONMENTS..]'], "Deploys the CDK toolkit stack into an AWS environment", (yargs: Argv) => yargs.option("bootstrap-bucket-name", { + "type": "string", + "alias": ["b","toolkit-bucket-name"], + "desc": "The name of the CDK toolkit bucket; bucket will be created and must not exist", + "default": undefined + }).option("bootstrap-kms-key-id", { + "type": "string", + "desc": "AWS KMS master key ID used for the SSE-KMS encryption", + "default": undefined, + "conflicts": "bootstrap-customer-key" + }).option("example-permissions-boundary", { + "type": "boolean", + "alias": "epb", + "desc": "Use the example permissions boundary.", + "default": undefined, + "conflicts": "custom-permissions-boundary" + }).option("custom-permissions-boundary", { + "type": "string", + "alias": "cpb", + "desc": "Use the permissions boundary specified by name.", + "default": undefined, + "conflicts": "example-permissions-boundary" + }).option("bootstrap-customer-key", { + "type": "boolean", + "desc": "Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)", + "default": undefined, + "conflicts": "bootstrap-kms-key-id" + }).option("qualifier", { + "type": "string", + "desc": "String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.", + "default": undefined + }).option("public-access-block-configuration", { + "type": "boolean", + "desc": "Block public access configuration on CDK toolkit bucket (enabled by default) ", + "default": undefined + }).option("tags", { + "type": "array", + "alias": "t", + "desc": "Tags to add for the stack (KEY=VALUE)", + "nargs": 1, + "requiresArg": true, + "default": [] + }).option("execute", { + "type": "boolean", + "desc": "Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)", + "default": true + }).option("trust", { + "type": "array", + "desc": "The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)", + "default": [], + "nargs": 1, + "requiresArg": true + }).option("trust-for-lookup", { + "type": "array", + "desc": "The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)", + "default": [], + "nargs": 1, + "requiresArg": true + }).option("cloudformation-execution-policies", { + "type": "array", + "desc": "The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)", + "default": [], + "nargs": 1, + "requiresArg": true + }).option("force", { + "alias": "f", + "type": "boolean", + "desc": "Always bootstrap even if it would downgrade template version", + "default": false + }).option("termination-protection", { + "type": "boolean", + "default": undefined, + "desc": "Toggle CloudFormation termination protection on the bootstrap stacks" + }).option("show-template", { + "type": "boolean", + "desc": "Instead of actual bootstrapping, print the current CLI's bootstrapping template to stdout for customization", + "default": false + }).option("toolkit-stack-name", { + "type": "string", + "desc": "The name of the CDK toolkit stack to create", + "requiresArg": true + }).option("template", { + "type": "string", + "requiresArg": true, + "desc": "Use the template from the given file instead of the built-in one (use --show-template to obtain an example)" + }).option("previous-parameters", { + "type": "boolean", + "default": true, + "desc": "Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)" })).command(['gc [ENVIRONMENTS..]'], "Garbage collect assets", (yargs: Argv) => yargs.option("action", { "type": "string", "desc": "The action (or sub-action) you want to perform. Valid entires are \"print\", \"tag\", \"delete-tagged\", \"full\".", From 0c08bf33e54125d868108ccf885c98c0b7653c11 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Thu, 24 Oct 2024 15:31:42 -0700 Subject: [PATCH 18/23] docstrings --- .../aws-cdk/lib/parse-command-line-arguments.ts | 9 +++++---- tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 17 ++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index 80d7bf4f8e090..eb0ab6c0985c4 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -1,3 +1,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. +// ------------------------------------------------------------------------------------------- /* eslint-disable prettier/prettier,max-len */ import { Argv } from "yargs"; @@ -568,8 +572,5 @@ function parseCommandLineArguments(args: Array, browserDefault?: string, "type": "string", "default": browserDefault })).command(['doctor'], "Check your set-up for potential problems").version(version).demandCommand(1, "''").recommendCommands().help().alias("h", "help").epilogue("If your app has a single stack, there is no need to specify the stack name\n\nIf one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.").parse(args); -}// ------------------------------------------------------------------------------------------- -// 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. -// ------------------------------------------------------------------------------------------- +}// eslint-disable-next-line @typescript-eslint/no-require-imports const yargs = require('yargs'); \ No newline at end of file diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 41daa07b1758c..8bba91a1cf50e 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -5,17 +5,16 @@ import { CliConfig, YargsOption } from './yargs-types'; export async function renderYargs(config: CliConfig): Promise { const scope = new Module('aws-cdk'); + scope.documentation.push( '-------------------------------------------------------------------------------------------'); + scope.documentation.push('GENERATED FROM packages/aws-cdk/lib/config.ts.'); + scope.documentation.push('Do not edit by hand; all changes will be overwritten at build time from the config file.'); + scope.documentation.push('-------------------------------------------------------------------------------------------'); + scope.addImport(new SelectiveModuleImport(scope, 'yargs', ['Argv'])); - scope.addInitialization( - code.comment('-------------------------------------------------------------------------------------------'), - code.comment('GENERATED FROM packages/aws-cdk/lib/config.ts.'), - code.comment('Do not edit by hand; all changes will be overwritten at build time from the config file.'), - code.comment('-------------------------------------------------------------------------------------------'), - ); - // 'https://github.com/yargs/yargs/issues/1929', - // 'https://github.com/evanw/esbuild/issues/1492', - // 'eslint-disable-next-line @typescript-eslint/no-require-imports', + // 'https://github.com/yargs/yargs/issues/1929', + // 'https://github.com/evanw/esbuild/issues/1492', + scope.addInitialization(code.comment('eslint-disable-next-line @typescript-eslint/no-require-imports')); scope.addInitialization(code.stmt.constVar(code.expr.ident('yargs'), code.expr.directCode("require('yargs')"))); const parseCommandLineArguments = new FreeFunction(scope, { From 16f1c0dfe78dfabae62b78965610f3bb25a6bab7 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Fri, 25 Oct 2024 14:13:02 -0700 Subject: [PATCH 19/23] ooh man --- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 4 -- .../lib/parse-command-line-arguments.ts | 4 +- tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 19 +++++++--- tools/@aws-cdk/yargs-gen/package.json | 2 +- yarn.lock | 38 +++++-------------- 5 files changed, 26 insertions(+), 41 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index d2cb73dca0542..ff008f4ea1225 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -1526,11 +1526,7 @@ This is a feature flag as the old behavior was technically incorrect but users m | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | -<<<<<<< HEAD -| V2NEXT | `false` | `true` | -======= | 2.164.0 | `false` | `true` | ->>>>>>> a1eef1dbabec261cac5e69d761cc9c839de629f6 diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index eb0ab6c0985c4..c1a1993ad6489 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -2,11 +2,11 @@ // 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. // ------------------------------------------------------------------------------------------- -/* eslint-disable prettier/prettier,max-len */ +/* eslint-disable @typescript-eslint/comma-dangle, comma-spacing, max-len, quotes, quote-props */ import { Argv } from "yargs"; // @ts-ignore TS6133 -function parseCommandLineArguments(args: Array, browserDefault?: string, availableInitLanguages: Array, migrateSupportedLanguages: Array, version: string, yargsNegativeAlias: any): any { +export function parseCommandLineArguments(args: Array, browserDefault?: string, availableInitLanguages: Array, migrateSupportedLanguages: Array, version: string, yargsNegativeAlias: any): any { return yargs.usage("Usage: cdk -a COMMAND").option("app", { "type": "string", "alias": "a", diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 8bba91a1cf50e..6a0cc817d54be 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -1,4 +1,5 @@ import { Expression, FreeFunction, Module, SelectiveModuleImport, Statement, Type, TypeScriptRenderer, code } from '@cdklabs/typewriter'; +import { EsLintRules } from '@cdklabs/typewriter/lib/eslint-rules'; import { NULL } from '@cdklabs/typewriter/lib/expressions/builder'; import { CliConfig, YargsOption } from './yargs-types'; @@ -30,9 +31,17 @@ export async function renderYargs(config: CliConfig): Promise { { name: 'yargsNegativeAlias', type: Type.ANY }, ], }); - parseCommandLineArguments.addBody(makeYargs(config/*, scope*/)); - - return new TypeScriptRenderer().render(scope); + parseCommandLineArguments.addBody(makeYargs(config)); + + return new TypeScriptRenderer({ + disabledEsLintRules: [ + EsLintRules.COMMA_DANGLE, + EsLintRules.COMMA_SPACING, + EsLintRules.MAX_LEN, + EsLintRules.QUOTES, + EsLintRules.QUOTE_PROPS, + ], + }).render(scope); } interface MiddlewareExpression { @@ -52,7 +61,7 @@ interface MiddlewareExpression { // By using the config above, every --arg will only consume one argument, so you can do the following: // // ./prog --arg one --arg two position => will parse to { arg: ['one', 'two'], _: ['positional'] }. -function makeYargs(config: CliConfig/*, scope: ScopeImpl*/): Statement { +function makeYargs(config: CliConfig): Statement { let yargsExpr: Expression = code.expr.ident('yargs'); yargsExpr = yargsExpr.callMethod('usage', lit('Usage: cdk -a COMMAND')); @@ -146,4 +155,4 @@ function lit(value: any): Expression { default: return code.expr.lit(value); } -} \ No newline at end of file +} diff --git a/tools/@aws-cdk/yargs-gen/package.json b/tools/@aws-cdk/yargs-gen/package.json index 6e61839cc8d26..0f84a9c46c144 100644 --- a/tools/@aws-cdk/yargs-gen/package.json +++ b/tools/@aws-cdk/yargs-gen/package.json @@ -28,7 +28,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@cdklabs/typewriter": "^0.0.3", + "@cdklabs/typewriter": "^0.0.4", "yargs": "^16.2.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index e3d2e43dbccff..ef727b8562454 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2996,6 +2996,11 @@ resolved "https://registry.npmjs.org/@cdklabs/typewriter/-/typewriter-0.0.3.tgz#37143d4cf004085bce7d1bbc9139bbf4bf4403a8" integrity sha512-dymXkqVKZLLQJGxZGvmCn9ZIDCiPM5hC1P7dABob8C0m5P0bf91W7HsPUu3yHomdFxoHAWFaXAZ9i3Q+uVeJ5g== +"@cdklabs/typewriter@^0.0.4": + version "0.0.4" + resolved "https://registry.npmjs.org/@cdklabs/typewriter/-/typewriter-0.0.4.tgz#4c2ae97c05eec921131549de08e37e5ecda80e43" + integrity sha512-FAcF8k0nNo3VmlGP3UHi4h2K5sohY/7Gcv4p7epMGwT4U3PbAsc3xWL42IAD1a/1g/rvrtIaRHbuGUp1O1VNvw== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -5278,7 +5283,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.14": +"@types/jest@^29.5.12", "@types/jest@^29.5.14": version "29.5.14" resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== @@ -14563,16 +14568,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: +"string-width-cjs@npm:string-width@^4.2.0", string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -14637,7 +14633,7 @@ stringify-package@^1.0.1: resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -14651,13 +14647,6 @@ strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -15660,7 +15649,7 @@ workerpool@^6.5.1: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -15678,15 +15667,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From b0cb288f9a2360fa690f086bb7444ce4d593af0e Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Fri, 1 Nov 2024 08:40:30 -0700 Subject: [PATCH 20/23] scripts dir --- packages/aws-cdk/package.json | 2 +- packages/aws-cdk/{bin => scripts}/yargs-gen | 0 packages/aws-cdk/{bin => scripts}/yargs-gen.ts | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename packages/aws-cdk/{bin => scripts}/yargs-gen (100%) rename packages/aws-cdk/{bin => scripts}/yargs-gen.ts (100%) diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 9f1466c340550..a9045af68fb82 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -7,7 +7,7 @@ }, "scripts": { "build": "cdk-build", - "yargs-gen": "yarn ts-node bin/yargs-gen.ts", + "yargs-gen": "yarn ts-node scripts/yargs-gen.ts", "watch": "cdk-watch", "lint": "cdk-lint", "pkglint": "pkglint -f", diff --git a/packages/aws-cdk/bin/yargs-gen b/packages/aws-cdk/scripts/yargs-gen similarity index 100% rename from packages/aws-cdk/bin/yargs-gen rename to packages/aws-cdk/scripts/yargs-gen diff --git a/packages/aws-cdk/bin/yargs-gen.ts b/packages/aws-cdk/scripts/yargs-gen.ts similarity index 100% rename from packages/aws-cdk/bin/yargs-gen.ts rename to packages/aws-cdk/scripts/yargs-gen.ts From 72759ca26289d6499c5361c4a8426153d2c78a02 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Fri, 1 Nov 2024 13:05:28 -0700 Subject: [PATCH 21/23] fixed the middleware --- .../cli-lib-alpha/THIRD_PARTY_LICENSES | 4 ++-- packages/aws-cdk/lib/config.ts | 12 ++--------- .../lib/parse-command-line-arguments.ts | 8 +++---- tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 21 ++++++------------- tools/@aws-cdk/yargs-gen/lib/yargs-types.ts | 2 +- 5 files changed, 15 insertions(+), 32 deletions(-) diff --git a/packages/@aws-cdk/cli-lib-alpha/THIRD_PARTY_LICENSES b/packages/@aws-cdk/cli-lib-alpha/THIRD_PARTY_LICENSES index df020abc2aac7..fbf6e87a533bd 100644 --- a/packages/@aws-cdk/cli-lib-alpha/THIRD_PARTY_LICENSES +++ b/packages/@aws-cdk/cli-lib-alpha/THIRD_PARTY_LICENSES @@ -207,7 +207,7 @@ The @aws-cdk/cli-lib-alpha package includes the following third-party software/l ---------------- -** @jsii/check-node@1.103.1 - https://www.npmjs.com/package/@jsii/check-node/v/1.103.1 | Apache-2.0 +** @jsii/check-node@1.104.0 - https://www.npmjs.com/package/@jsii/check-node/v/1.104.0 | Apache-2.0 jsii Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -3562,7 +3562,7 @@ THE SOFTWARE. ---------------- -** tslib@2.7.0 - https://www.npmjs.com/package/tslib/v/2.7.0 | 0BSD +** tslib@2.8.0 - https://www.npmjs.com/package/tslib/v/2.8.0 | 0BSD Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any diff --git a/packages/aws-cdk/lib/config.ts b/packages/aws-cdk/lib/config.ts index 04a44d12b9b90..49300ea71b494 100644 --- a/packages/aws-cdk/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -134,16 +134,12 @@ export async function makeConfig(): Promise { type: 'boolean', desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', + negativeAlias: 'R', }, 'R': { type: 'boolean', hidden: true, // Hack to get '-R' as an alias for '--no-rollback', suggested by: https://github.com/yargs/yargs/issues/1729 - middleware: { - callback: 'yargsNegativeAlias', - args: ['R', 'rollback'], - applyBeforeValidation: true, - }, }, 'hotswap': { type: 'boolean', @@ -275,16 +271,12 @@ export async function makeConfig(): Promise { type: 'boolean', desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. " + 'Note: do **not** disable this flag for deployments with resource replacements, as that will always fail', + negativeAlias: '-R', }, // same hack for -R as above in 'deploy' 'R': { type: 'boolean', hidden: true, - middleware: { - callback: 'yargsNegativeAlias', - args: ['R', 'rollback'], - applyBeforeValidation: true, - }, }, 'hotswap': { type: 'boolean', diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index c1a1993ad6489..03c40812fcd3d 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -323,10 +323,10 @@ export function parseCommandLineArguments(args: Array, browserDefault?: }).option("rollback", { "type": "boolean", "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" - }).option("R", { + }).middleware(yargsNegativeAlias("rollback", "R"), true).option("R", { "type": "boolean", "hidden": true - }).middleware(yargsNegativeAlias(["R","rollback"]), true).option("hotswap", { + }).option("hotswap", { "type": "boolean", "desc": "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.Do not use this in production environments" }).option("hotswap-fallback", { @@ -433,10 +433,10 @@ export function parseCommandLineArguments(args: Array, browserDefault?: }).option("rollback", { "type": "boolean", "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" - }).option("R", { + }).middleware(yargsNegativeAlias("rollback", "-R"), true).option("R", { "type": "boolean", "hidden": true - }).middleware(yargsNegativeAlias(["R","rollback"]), true).option("hotswap", { + }).option("hotswap", { "type": "boolean", "desc": "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.'true' by default, use --no-hotswap to turn off" }).option("hotswap-fallback", { diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 6a0cc817d54be..44609acb55470 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -44,11 +44,6 @@ export async function renderYargs(config: CliConfig): Promise { }).render(scope); } -interface MiddlewareExpression { - callback: Expression; - applyBeforeValidation?: Expression; -} - // Use the following configuration for array arguments: // // { type: 'array', default: [], nargs: 1, requiresArg: true } @@ -93,18 +88,14 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: YargsO let optionsExpr = prefix; for (const option of Object.keys(options)) { // each option can define at most one middleware call; if we need more, handle a list of these instead - let middleware: MiddlewareExpression | undefined = undefined; + let middlewareCallback: Expression | undefined = undefined; const optionProps = options[option]; const optionArgs: { [key: string]: Expression } = {}; for (const optionProp of Object.keys(optionProps)) { - if (optionProp === 'middleware') { + if (optionProp === 'negativeAlias') { // middleware is a separate function call, so we can't store it with the regular option arguments, as those will all be treated as parameters: // .option('R', { type: 'boolean', hidden: true }).middleware(yargsNegativeAlias('R', 'rollback'), true) - middleware = { - callback: code.expr.builtInFn(optionProps.middleware!.callback, lit(optionProps.middleware!.args)), - applyBeforeValidation: lit(optionProps.middleware!.applyBeforeValidation), - }; - break; + middlewareCallback = code.expr.builtInFn('yargsNegativeAlias', lit(option), lit(optionProps.negativeAlias)); } else { const optionValue = (optionProps as any)[optionProp]; if (optionValue && optionValue.dynamicType === 'parameter') { @@ -121,9 +112,9 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: YargsO } optionsExpr = optionsExpr.callMethod('option', lit(option), code.expr.object(optionArgs)); - if (middleware) { - optionsExpr = optionsExpr.callMethod('middleware', middleware.callback, middleware.applyBeforeValidation ?? code.expr.UNDEFINED); - middleware = undefined; + if (middlewareCallback) { + optionsExpr = optionsExpr.callMethod('middleware', middlewareCallback, lit(true)); + middlewareCallback = undefined; } } diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts index 63a5a76de2728..825fefd16d11e 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts @@ -35,7 +35,7 @@ export interface YargsOption { requiresArg?: boolean; hidden?: boolean; count?: boolean; - middleware?: Middleware; + negativeAlias?: string; } export interface Middleware { From bf013e7633a97d0e672682c0c07a23855051811c Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Tue, 5 Nov 2024 11:34:54 +0000 Subject: [PATCH 22/23] fix formatting --- packages/aws-cdk/lib/cli.ts | 7 +- packages/aws-cdk/lib/config.ts | 2 +- packages/aws-cdk/lib/notices.ts | 4 +- .../lib/parse-command-line-arguments.ts | 1339 ++++++++++------- packages/aws-cdk/scripts/yargs-gen.ts | 2 +- packages/aws-cdk/test/notices.test.ts | 4 +- tools/@aws-cdk/yargs-gen/jest.config.js | 1 - tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts | 14 +- tools/@aws-cdk/yargs-gen/lib/yargs-types.ts | 1 - tools/@aws-cdk/yargs-gen/package.json | 2 +- tools/@aws-cdk/yargs-gen/test/cli.test.ts | 76 +- yarn.lock | 36 +- 12 files changed, 897 insertions(+), 591 deletions(-) diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index 3abee84159934..e779defa9ca35 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -37,13 +37,14 @@ if (!process.stdout.isTTY) { } export async function exec(args: string[], synthesizer?: Synthesizer): Promise { - function makeBrowserDefault(): string | undefined { + function makeBrowserDefault(): string { const defaultBrowserCommand: { [key in NodeJS.Platform]?: string } = { darwin: 'open %u', win32: 'start %u', }; - return process.platform in defaultBrowserCommand ? defaultBrowserCommand[process.platform] : 'xdg-open %u'; + const cmd = defaultBrowserCommand[process.platform]; + return cmd ?? 'xdg-open %u'; } const argv = await parseCommandLineArguments(args, makeBrowserDefault(), await availableInitLanguages(), MIGRATE_SUPPORTED_LANGUAGES as string[], version.DISPLAY_VERSION, yargsNegativeAlias); @@ -84,7 +85,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise { +export function makeConfig(): CliConfig { return { globalOptions: { 'app': { type: 'string', alias: 'a', desc: 'REQUIRED WHEN RUNNING APP: command-line for executing your app or a cloud assembly directory (e.g. "node bin/my-app.js"). Can also be specified in cdk.json or ~/.cdk.json', requiresArg: true }, diff --git a/packages/aws-cdk/lib/notices.ts b/packages/aws-cdk/lib/notices.ts index 7976a9c4ed448..39b793d6c8572 100644 --- a/packages/aws-cdk/lib/notices.ts +++ b/packages/aws-cdk/lib/notices.ts @@ -24,7 +24,7 @@ export interface NoticesProps { * * @default false */ - readonly includeAcknowlegded?: boolean; + readonly includeAcknowledged?: boolean; } @@ -223,7 +223,7 @@ export class Notices { private constructor(props: NoticesProps) { this.configuration = props.configuration; this.acknowledgedIssueNumbers = new Set(this.configuration.context.get('acknowledged-issue-numbers') ?? []); - this.includeAcknowlegded = props.includeAcknowlegded ?? false; + this.includeAcknowlegded = props.includeAcknowledged ?? false; } /** diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index 03c40812fcd3d..e570f0d972ed0 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -3,574 +3,775 @@ // Do not edit by hand; all changes will be overwritten at build time from the config file. // ------------------------------------------------------------------------------------------- /* eslint-disable @typescript-eslint/comma-dangle, comma-spacing, max-len, quotes, quote-props */ -import { Argv } from "yargs"; +import { Argv } from 'yargs'; // @ts-ignore TS6133 -export function parseCommandLineArguments(args: Array, browserDefault?: string, availableInitLanguages: Array, migrateSupportedLanguages: Array, version: string, yargsNegativeAlias: any): any { - return yargs.usage("Usage: cdk -a COMMAND").option("app", { - "type": "string", - "alias": "a", - "desc": "REQUIRED WHEN RUNNING APP: command-line for executing your app or a cloud assembly directory (e.g. \"node bin/my-app.js\"). Can also be specified in cdk.json or ~/.cdk.json", - "requiresArg": true - }).option("build", { - "type": "string", - "desc": "Command-line for a pre-synth build" - }).option("context", { - "type": "array", - "alias": "c", - "desc": "Add contextual string parameter (KEY=VALUE)", - "nargs": 1, - "requiresArg": true - }).option("plugin", { - "type": "array", - "alias": "p", - "desc": "Name or path of a node package that extend the CDK features. Can be specified multiple times", - "nargs": 1 - }).option("trace", { - "type": "boolean", - "desc": "Print trace for stack warnings" - }).option("strict", { - "type": "boolean", - "desc": "Do not construct stacks with warnings" - }).option("lookups", { - "type": "boolean", - "desc": "Perform context lookups (synthesis fails if this is disabled and context lookups need to be performed)", - "default": true - }).option("ignore-errors", { - "type": "boolean", - "default": false, - "desc": "Ignores synthesis errors, which will likely produce an invalid output" - }).option("json", { - "type": "boolean", - "alias": "j", - "desc": "Use JSON output instead of YAML when templates are printed to STDOUT", - "default": false - }).option("verbose", { - "type": "boolean", - "alias": "v", - "desc": "Show debug logs (specify multiple times to increase verbosity)", - "default": false, - "count": true - }).option("debug", { - "type": "boolean", - "desc": "Enable emission of additional debugging information, such as creation stack traces of tokens", - "default": false - }).option("profile", { - "type": "string", - "desc": "Use the indicated AWS profile as the default environment", - "requiresArg": true - }).option("proxy", { - "type": "string", - "desc": "Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified", - "requiresArg": true - }).option("ca-bundle-path", { - "type": "string", - "desc": "Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified", - "requiresArg": true - }).option("ec2creds", { - "type": "boolean", - "alias": "i", - "default": undefined, - "desc": "Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status" - }).option("version-reporting", { - "type": "boolean", - "desc": "Include the \"AWS::CDK::Metadata\" resource in synthesized templates (enabled by default)", - "default": undefined - }).option("path-metadata", { - "type": "boolean", - "desc": "Include \"aws:cdk:path\" CloudFormation metadata for each resource (enabled by default)", - "default": undefined - }).option("asset-metadata", { - "type": "boolean", - "desc": "Include \"aws:asset:*\" CloudFormation metadata for resources that uses assets (enabled by default)", - "default": undefined - }).option("role-arn", { - "type": "string", - "alias": "r", - "desc": "ARN of Role to use when invoking CloudFormation", - "default": undefined, - "requiresArg": true - }).option("staging", { - "type": "boolean", - "desc": "Copy assets to the output directory (use --no-staging to disable the copy of assets which allows local debugging via the SAM CLI to reference the original source files)", - "default": true - }).option("output", { - "type": "string", - "alias": "o", - "desc": "Emits the synthesized cloud assembly into a directory (default: cdk.out)", - "requiresArg": true - }).option("notices", { - "type": "boolean", - "desc": "Show relevant notices" - }).option("no-color", { - "type": "boolean", - "desc": "Removes colors and other style from console output", - "default": false - }).option("ci", { - "type": "boolean", - "desc": "Force CI detection. If CI=true then logs will be sent to stdout instead of stderr", - "default": process.env.CI !== undefined - }).option("unstable", { - "type": "array", - "desc": "Opt in to specific unstable features. Can be specified multiple times.", - "default": [] - }).command(['list [STACKS..]', 'ls [STACKS..]'], "Lists all stacks in the app", (yargs: Argv) => yargs.option("long", { - "type": "boolean", - "default": false, - "alias": "l", - "desc": "Display environment information for each stack" - }).option("show-dependencies", { - "type": "boolean", - "default": false, - "alias": "d", - "desc": "Display stack dependency information for each stack" - })).command(['synthesize [STACKS..]', 'synth [STACKS..]'], "Synthesizes and prints the CloudFormation template for this stack", (yargs: Argv) => yargs.option("exclusively", { - "type": "boolean", - "alias": "e", - "desc": "Only synthesize requested stacks, don't include dependencies" - }).option("validation", { - "type": "boolean", - "desc": "After synthesis, validate stacks with the \"validateOnSynth\" attribute set (can also be controlled with CDK_VALIDATION)", - "default": true - }).option("quiet", { - "type": "boolean", - "alias": "q", - "desc": "Do not output CloudFormation Template to stdout", - "default": false - })).command(['bootstrap [ENVIRONMENTS..]'], "Deploys the CDK toolkit stack into an AWS environment", (yargs: Argv) => yargs.option("bootstrap-bucket-name", { - "type": "string", - "alias": ["b","toolkit-bucket-name"], - "desc": "The name of the CDK toolkit bucket; bucket will be created and must not exist", - "default": undefined - }).option("bootstrap-kms-key-id", { - "type": "string", - "desc": "AWS KMS master key ID used for the SSE-KMS encryption", - "default": undefined, - "conflicts": "bootstrap-customer-key" - }).option("example-permissions-boundary", { - "type": "boolean", - "alias": "epb", - "desc": "Use the example permissions boundary.", - "default": undefined, - "conflicts": "custom-permissions-boundary" - }).option("custom-permissions-boundary", { - "type": "string", - "alias": "cpb", - "desc": "Use the permissions boundary specified by name.", - "default": undefined, - "conflicts": "example-permissions-boundary" - }).option("bootstrap-customer-key", { - "type": "boolean", - "desc": "Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)", - "default": undefined, - "conflicts": "bootstrap-kms-key-id" - }).option("qualifier", { - "type": "string", - "desc": "String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.", - "default": undefined - }).option("public-access-block-configuration", { - "type": "boolean", - "desc": "Block public access configuration on CDK toolkit bucket (enabled by default) ", - "default": undefined - }).option("tags", { - "type": "array", - "alias": "t", - "desc": "Tags to add for the stack (KEY=VALUE)", - "nargs": 1, - "requiresArg": true, - "default": [] - }).option("execute", { - "type": "boolean", - "desc": "Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)", - "default": true - }).option("trust", { - "type": "array", - "desc": "The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)", - "default": [], - "nargs": 1, - "requiresArg": true - }).option("trust-for-lookup", { - "type": "array", - "desc": "The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)", - "default": [], - "nargs": 1, - "requiresArg": true - }).option("cloudformation-execution-policies", { - "type": "array", - "desc": "The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)", - "default": [], - "nargs": 1, - "requiresArg": true - }).option("force", { - "alias": "f", - "type": "boolean", - "desc": "Always bootstrap even if it would downgrade template version", - "default": false - }).option("termination-protection", { - "type": "boolean", - "default": undefined, - "desc": "Toggle CloudFormation termination protection on the bootstrap stacks" - }).option("show-template", { - "type": "boolean", - "desc": "Instead of actual bootstrapping, print the current CLI's bootstrapping template to stdout for customization", - "default": false - }).option("toolkit-stack-name", { - "type": "string", - "desc": "The name of the CDK toolkit stack to create", - "requiresArg": true - }).option("template", { - "type": "string", - "requiresArg": true, - "desc": "Use the template from the given file instead of the built-in one (use --show-template to obtain an example)" - }).option("previous-parameters", { - "type": "boolean", - "default": true, - "desc": "Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)" - })).command(['gc [ENVIRONMENTS..]'], "Garbage collect assets", (yargs: Argv) => yargs.option("action", { - "type": "string", - "desc": "The action (or sub-action) you want to perform. Valid entires are \"print\", \"tag\", \"delete-tagged\", \"full\".", - "default": "full" - }).option("type", { - "type": "string", - "desc": "Specify either ecr, s3, or all", - "default": "all" - }).option("rollback-buffer-days", { - "type": "number", - "desc": "Delete assets that have been marked as isolated for this many days", - "default": 0 - }).option("created-buffer-days", { - "type": "number", - "desc": "Never delete assets younger than this (in days)", - "default": 1 - }).option("confirm", { - "type": "boolean", - "desc": "Confirm via manual prompt before deletion", - "default": true - }).option("bootstrap-stack-name", { - "type": "string", - "desc": "The name of the CDK toolkit stack, if different from the default \"CDKToolkit\"", - "requiresArg": true - })).command(['deploy [STACKS..]'], "Deploys the stack(s) named STACKS into your AWS account", (yargs: Argv) => yargs.option("all", { - "type": "boolean", - "desc": "Deploy all available stacks", - "default": false - }).option("build-exclude", { - "type": "array", - "alias": "E", - "nargs": 1, - "desc": "Do not rebuild asset with the given ID. Can be specified multiple times", - "default": [] - }).option("exclusively", { - "type": "boolean", - "alias": "e", - "desc": "Only deploy requested stacks, don't include dependencies" - }).option("require-approval", { - "type": "string", - "choices": ["never","any-change","broadening"], - "desc": "What security-sensitive changes need manual approval" - }).option("notification-arns", { - "type": "array", - "desc": "ARNs of SNS topics that CloudFormation will notify with stack related events", - "nargs": 1, - "requiresArg": true - }).option("tags", { - "type": "array", - "alias": "t", - "desc": "Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)", - "nargs": 1, - "requiresArg": true - }).option("execute", { - "type": "boolean", - "desc": "Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet) (deprecated)", - "deprecated": true - }).option("change-set-name", { - "type": "string", - "desc": "Name of the CloudFormation change set to create (only if method is not direct)" - }).option("method", { - "alias": "m", - "type": "string", - "choices": ["direct","change-set","prepare-change-set"], - "requiresArg": true, - "desc": "How to perform the deployment. Direct is a bit faster but lacks progress information" - }).option("force", { - "alias": "f", - "type": "boolean", - "desc": "Always deploy stack even if templates are identical", - "default": false - }).option("parameters", { - "type": "array", - "desc": "Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)", - "nargs": 1, - "requiresArg": true, - "default": {} - }).option("outputs-file", { - "type": "string", - "alias": "O", - "desc": "Path to file where stack outputs will be written as JSON", - "requiresArg": true - }).option("previous-parameters", { - "type": "boolean", - "default": true, - "desc": "Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)" - }).option("toolkit-stack-name", { - "type": "string", - "desc": "The name of the existing CDK toolkit stack (only used for app using legacy synthesis)", - "requiresArg": true - }).option("progress", { - "type": "string", - "choices": ["bar","events"], - "desc": "Display mode for stack activity events" - }).option("rollback", { - "type": "boolean", - "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" - }).middleware(yargsNegativeAlias("rollback", "R"), true).option("R", { - "type": "boolean", - "hidden": true - }).option("hotswap", { - "type": "boolean", - "desc": "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.Do not use this in production environments" - }).option("hotswap-fallback", { - "type": "boolean", - "desc": "Attempts to perform a 'hotswap' deployment, which skips CloudFormation and updates the resources directly, and falls back to a full deployment if that is not possible. Do not use this in production environments" - }).option("watch", { - "type": "boolean", - "desc": "Continuously observe the project files, and deploy the given stack(s) automatically when changes are detected. Implies --hotswap by default" - }).option("logs", { - "type": "boolean", - "default": true, - "desc": "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off. Only in effect if specified alongside the '--watch' option" - }).option("concurrency", { - "type": "number", - "desc": "Maximum number of simultaneous deployments (dependency permitting) to execute.", - "default": 1, - "requiresArg": true - }).option("asset-parallelism", { - "type": "boolean", - "desc": "Whether to build/publish assets in parallel" - }).option("asset-prebuild", { - "type": "boolean", - "desc": "Whether to build all assets before deploying the first stack (useful for failing Docker builds)", - "default": true - }).option("ignore-no-stacks", { - "type": "boolean", - "desc": "Whether to deploy if the app contains no stacks", - "default": false - })).command(['rollback [STACKS..]'], "Rolls back the stack(s) named STACKS to their last stable state", (yargs: Argv) => yargs.option("all", { - "type": "boolean", - "default": false, - "desc": "Roll back all available stacks" - }).option("toolkit-stack-name", { - "type": "string", - "desc": "The name of the CDK toolkit stack the environment is bootstrapped with", - "requiresArg": true - }).option("force", { - "alias": "f", - "type": "boolean", - "desc": "Orphan all resources for which the rollback operation fails." - }).option("validate-bootstrap-version", { - "type": "boolean", - "desc": "Whether to validate the bootstrap stack version. Defaults to 'true', disable with --no-validate-bootstrap-version." - }).option("orphan", { - "type": "array", - "nargs": 1, - "requiresArg": true, - "desc": "Orphan the given resources, identified by their logical ID (can be specified multiple times)", - "default": [] - })).command(['import [STACK]'], "Import existing resource(s) into the given STACK", (yargs: Argv) => yargs.option("execute", { - "type": "boolean", - "desc": "Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)", - "default": true - }).option("change-set-name", { - "type": "string", - "desc": "Name of the CloudFormation change set to create" - }).option("toolkit-stack-name", { - "type": "string", - "desc": "The name of the CDK toolkit stack to create", - "requiresArg": true - }).option("rollback", { - "type": "boolean", - "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" - }).option("force", { - "alias": "f", - "type": "boolean", - "desc": "Do not abort if the template diff includes updates or deletes. This is probably safe but we're not sure, let us know how it goes." - }).option("record-resource-mapping", { - "type": "string", - "alias": "r", - "requiresArg": true, - "desc": "If specified, CDK will generate a mapping of existing physical resources to CDK resources to be imported as. The mapping will be written in the given file path. No actual import operation will be performed" - }).option("resource-mapping", { - "type": "string", - "alias": "m", - "requiresArg": true, - "desc": "If specified, CDK will use the given file to map physical resources to CDK resources for import, instead of interactively asking the user. Can be run from scripts" - })).command(['watch [STACKS..]'], "Shortcut for 'deploy --watch'", (yargs: Argv) => yargs.option("build-exclude", { - "type": "array", - "alias": "E", - "nargs": 1, - "desc": "Do not rebuild asset with the given ID. Can be specified multiple times", - "default": [] - }).option("exclusively", { - "type": "boolean", - "alias": "e", - "desc": "Only deploy requested stacks, don't include dependencies" - }).option("change-set-name", { - "type": "string", - "desc": "Name of the CloudFormation change set to create" - }).option("force", { - "alias": "f", - "type": "boolean", - "desc": "Always deploy stack even if templates are identical", - "default": false - }).option("toolkit-stack-name", { - "type": "string", - "desc": "The name of the existing CDK toolkit stack (only used for app using legacy synthesis)", - "requiresArg": true - }).option("progress", { - "type": "string", - "choices": ["bar","events"], - "desc": "Display mode for stack activity events" - }).option("rollback", { - "type": "boolean", - "desc": "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail" - }).middleware(yargsNegativeAlias("rollback", "-R"), true).option("R", { - "type": "boolean", - "hidden": true - }).option("hotswap", { - "type": "boolean", - "desc": "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.'true' by default, use --no-hotswap to turn off" - }).option("hotswap-fallback", { - "type": "boolean", - "desc": "Attempts to perform a 'hotswap' deployment, which skips CloudFormation and updates the resources directly, and falls back to a full deployment if that is not possible." - }).option("logs", { - "type": "boolean", - "default": true, - "desc": "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off" - }).option("concurrency", { - "type": "number", - "desc": "Maximum number of simultaneous deployments (dependency permitting) to execute.", - "default": 1, - "requiresArg": true - })).command(['destroy [STACKS..]'], "Destroy the stack(s) named STACKS", (yargs: Argv) => yargs.option("all", { - "type": "boolean", - "default": false, - "desc": "Destroy all available stacks" - }).option("exclusively", { - "type": "boolean", - "alias": "e", - "desc": "Only destroy requested stacks, don't include dependees" - }).option("force", { - "type": "boolean", - "alias": "f", - "desc": "Do not ask for confirmation before destroying the stacks" - })).command(['diff [STACKS..]'], "Compares the specified stack with the deployed stack or a local template file, and returns with status 1 if any difference is found", (yargs: Argv) => yargs.option("exclusively", { - "type": "boolean", - "alias": "e", - "desc": "Only diff requested stacks, don't include dependencies" - }).option("context-lines", { - "type": "number", - "desc": "Number of context lines to include in arbitrary JSON diff rendering", - "default": 3, - "requiresArg": true - }).option("template", { - "type": "string", - "desc": "The path to the CloudFormation template to compare with", - "requiresArg": true - }).option("strict", { - "type": "boolean", - "desc": "Do not filter out AWS::CDK::Metadata resources, mangled non-ASCII characters, or the CheckBootstrapVersionRule", - "default": false - }).option("security-only", { - "type": "boolean", - "desc": "Only diff for broadened security changes", - "default": false - }).option("fail", { - "type": "boolean", - "desc": "Fail with exit code 1 in case of diff" - }).option("processed", { - "type": "boolean", - "desc": "Whether to compare against the template with Transforms already processed", - "default": false - }).option("quiet", { - "type": "boolean", - "alias": "q", - "desc": "Do not print stack name and default message when there is no diff to stdout", - "default": false - }).option("change-set", { - "type": "boolean", - "alias": "changeset", - "desc": "Whether to create a changeset to analyze resource replacements. In this mode, diff will use the deploy role instead of the lookup role.", - "default": true - })).command(['metadata [STACK]'], "Returns all metadata associated with this stack").command(['acknowledge [ID]', 'ack [ID]'], "Acknowledge a notice so that it does not show up anymore").command(['notices'], "Returns a list of relevant notices", (yargs: Argv) => yargs.option("unacknowledged", { - "type": "boolean", - "alias": "u", - "default": false, - "desc": "Returns a list of unacknowledged notices" - })).command(['init [TEMPLATE]'], "Create a new, empty CDK project from a template.", (yargs: Argv) => yargs.option("language", { - "type": "string", - "alias": "l", - "desc": "The language to be used for the new project (default can be configured in ~/.cdk.json)", - "choices": availableInitLanguages - }).option("list", { - "type": "boolean", - "desc": "List the available templates" - }).option("generate-only", { - "type": "boolean", - "default": false, - "desc": "If true, only generates project files, without executing additional operations such as setting up a git repo, installing dependencies or compiling the project" - })).command(['migrate'], false, (yargs: Argv) => yargs.option("stack-name", { - "type": "string", - "alias": "n", - "desc": "The name assigned to the stack created in the new project. The name of the app will be based off this name as well.", - "requiresArg": true - }).option("language", { - "type": "string", - "default": "typescript", - "alias": "l", - "desc": "The language to be used for the new project", - "choices": migrateSupportedLanguages - }).option("account", { - "type": "string", - "desc": "The account to retrieve the CloudFormation stack template from" - }).option("region", { - "type": "string", - "desc": "The region to retrieve the CloudFormation stack template from" - }).option("from-path", { - "type": "string", - "desc": "The path to the CloudFormation template to migrate. Use this for locally stored templates" - }).option("from-stack", { - "type": "boolean", - "desc": "Use this flag to retrieve the template for an existing CloudFormation stack" - }).option("output-path", { - "type": "string", - "desc": "The output path for the migrated CDK app" - }).option("from-scan", { - "type": "string", - "desc": "Determines if a new scan should be created, or the last successful existing scan should be used \n options are \"new\" or \"most-recent\"" - }).option("filter", { - "type": "array", - "desc": "Filters the resource scan based on the provided criteria in the following format: \"key1=value1,key2=value2\"\n This field can be passed multiple times for OR style filtering: \n filtering options: \n resource-identifier: A key-value pair that identifies the target resource. i.e. {\"ClusterName\", \"myCluster\"}\n resource-type-prefix: A string that represents a type-name prefix. i.e. \"AWS::DynamoDB::\"\n tag-key: a string that matches resources with at least one tag with the provided key. i.e. \"myTagKey\"\n tag-value: a string that matches resources with at least one tag with the provided value. i.e. \"myTagValue\"" - }).option("compress", { - "type": "boolean", - "desc": "Use this flag to zip the generated CDK app" - })).command(['context'], "Manage cached context values", (yargs: Argv) => yargs.option("reset", { - "alias": "e", - "desc": "The context key (or its index) to reset", - "type": "string", - "requiresArg": true - }).option("force", { - "alias": "f", - "desc": "Ignore missing key error", - "type": "boolean", - "default": false - }).option("clear", { - "desc": "Clear all context", - "type": "boolean" - })).command(['docs', 'doc '], "Opens the reference documentation in a browser", (yargs: Argv) => yargs.option("browser", { - "alias": "b", - "desc": "the command to use to open the browser, using %u as a placeholder for the path of the file to open", - "type": "string", - "default": browserDefault - })).command(['doctor'], "Check your set-up for potential problems").version(version).demandCommand(1, "''").recommendCommands().help().alias("h", "help").epilogue("If your app has a single stack, there is no need to specify the stack name\n\nIf one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.").parse(args); -}// eslint-disable-next-line @typescript-eslint/no-require-imports -const yargs = require('yargs'); \ No newline at end of file +export function parseCommandLineArguments( + args: Array, + browserDefault: string, + availableInitLanguages: Array, + migrateSupportedLanguages: Array, + version: string, + yargsNegativeAlias: any +): any { + return yargs + .usage('Usage: cdk -a COMMAND') + .option('app', { + type: 'string', + alias: 'a', + desc: 'REQUIRED WHEN RUNNING APP: command-line for executing your app or a cloud assembly directory (e.g. "node bin/my-app.js"). Can also be specified in cdk.json or ~/.cdk.json', + requiresArg: true, + }) + .option('build', { + type: 'string', + desc: 'Command-line for a pre-synth build', + }) + .option('context', { + type: 'array', + alias: 'c', + desc: 'Add contextual string parameter (KEY=VALUE)', + nargs: 1, + requiresArg: true, + }) + .option('plugin', { + type: 'array', + alias: 'p', + desc: 'Name or path of a node package that extend the CDK features. Can be specified multiple times', + nargs: 1, + }) + .option('trace', { + type: 'boolean', + desc: 'Print trace for stack warnings', + }) + .option('strict', { + type: 'boolean', + desc: 'Do not construct stacks with warnings', + }) + .option('lookups', { + type: 'boolean', + desc: 'Perform context lookups (synthesis fails if this is disabled and context lookups need to be performed)', + default: true, + }) + .option('ignore-errors', { + type: 'boolean', + default: false, + desc: 'Ignores synthesis errors, which will likely produce an invalid output', + }) + .option('json', { + type: 'boolean', + alias: 'j', + desc: 'Use JSON output instead of YAML when templates are printed to STDOUT', + default: false, + }) + .option('verbose', { + type: 'boolean', + alias: 'v', + desc: 'Show debug logs (specify multiple times to increase verbosity)', + default: false, + count: true, + }) + .option('debug', { + type: 'boolean', + desc: 'Enable emission of additional debugging information, such as creation stack traces of tokens', + default: false, + }) + .option('profile', { + type: 'string', + desc: 'Use the indicated AWS profile as the default environment', + requiresArg: true, + }) + .option('proxy', { + type: 'string', + desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified', + requiresArg: true, + }) + .option('ca-bundle-path', { + type: 'string', + desc: 'Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified', + requiresArg: true, + }) + .option('ec2creds', { + type: 'boolean', + alias: 'i', + default: undefined, + desc: 'Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status', + }) + .option('version-reporting', { + type: 'boolean', + desc: 'Include the "AWS::CDK::Metadata" resource in synthesized templates (enabled by default)', + default: undefined, + }) + .option('path-metadata', { + type: 'boolean', + desc: 'Include "aws:cdk:path" CloudFormation metadata for each resource (enabled by default)', + default: undefined, + }) + .option('asset-metadata', { + type: 'boolean', + desc: 'Include "aws:asset:*" CloudFormation metadata for resources that uses assets (enabled by default)', + default: undefined, + }) + .option('role-arn', { + type: 'string', + alias: 'r', + desc: 'ARN of Role to use when invoking CloudFormation', + default: undefined, + requiresArg: true, + }) + .option('staging', { + type: 'boolean', + desc: 'Copy assets to the output directory (use --no-staging to disable the copy of assets which allows local debugging via the SAM CLI to reference the original source files)', + default: true, + }) + .option('output', { + type: 'string', + alias: 'o', + desc: 'Emits the synthesized cloud assembly into a directory (default: cdk.out)', + requiresArg: true, + }) + .option('notices', { + type: 'boolean', + desc: 'Show relevant notices', + }) + .option('no-color', { + type: 'boolean', + desc: 'Removes colors and other style from console output', + default: false, + }) + .option('ci', { + type: 'boolean', + desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', + default: process.env.CI !== undefined, + }) + .option('unstable', { + type: 'array', + desc: 'Opt in to specific unstable features. Can be specified multiple times.', + default: [], + }) + .command(['list [STACKS..]', 'ls [STACKS..]'], 'Lists all stacks in the app', (yargs: Argv) => + yargs + .option('long', { + type: 'boolean', + default: false, + alias: 'l', + desc: 'Display environment information for each stack', + }) + .option('show-dependencies', { + type: 'boolean', + default: false, + alias: 'd', + desc: 'Display stack dependency information for each stack', + }) + ) + .command(['synthesize [STACKS..]', 'synth [STACKS..]'], 'Synthesizes and prints the CloudFormation template for this stack', (yargs: Argv) => + yargs + .option('exclusively', { + type: 'boolean', + alias: 'e', + desc: "Only synthesize requested stacks, don't include dependencies", + }) + .option('validation', { + type: 'boolean', + desc: 'After synthesis, validate stacks with the "validateOnSynth" attribute set (can also be controlled with CDK_VALIDATION)', + default: true, + }) + .option('quiet', { + type: 'boolean', + alias: 'q', + desc: 'Do not output CloudFormation Template to stdout', + default: false, + }) + ) + .command(['bootstrap [ENVIRONMENTS..]'], 'Deploys the CDK toolkit stack into an AWS environment', (yargs: Argv) => + yargs + .option('bootstrap-bucket-name', { + type: 'string', + alias: ['b', 'toolkit-bucket-name'], + desc: 'The name of the CDK toolkit bucket; bucket will be created and must not exist', + default: undefined, + }) + .option('bootstrap-kms-key-id', { + type: 'string', + desc: 'AWS KMS master key ID used for the SSE-KMS encryption', + default: undefined, + conflicts: 'bootstrap-customer-key', + }) + .option('example-permissions-boundary', { + type: 'boolean', + alias: 'epb', + desc: 'Use the example permissions boundary.', + default: undefined, + conflicts: 'custom-permissions-boundary', + }) + .option('custom-permissions-boundary', { + type: 'string', + alias: 'cpb', + desc: 'Use the permissions boundary specified by name.', + default: undefined, + conflicts: 'example-permissions-boundary', + }) + .option('bootstrap-customer-key', { + type: 'boolean', + desc: 'Create a Customer Master Key (CMK) for the bootstrap bucket (you will be charged but can customize permissions, modern bootstrapping only)', + default: undefined, + conflicts: 'bootstrap-kms-key-id', + }) + .option('qualifier', { + type: 'string', + desc: 'String which must be unique for each bootstrap stack. You must configure it on your CDK app if you change this from the default.', + default: undefined, + }) + .option('public-access-block-configuration', { + type: 'boolean', + desc: 'Block public access configuration on CDK toolkit bucket (enabled by default) ', + default: undefined, + }) + .option('tags', { + type: 'array', + alias: 't', + desc: 'Tags to add for the stack (KEY=VALUE)', + nargs: 1, + requiresArg: true, + default: [], + }) + .option('execute', { + type: 'boolean', + desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', + default: true, + }) + .option('trust', { + type: 'array', + desc: 'The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)', + default: [], + nargs: 1, + requiresArg: true, + }) + .option('trust-for-lookup', { + type: 'array', + desc: 'The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)', + default: [], + nargs: 1, + requiresArg: true, + }) + .option('cloudformation-execution-policies', { + type: 'array', + desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', + default: [], + nargs: 1, + requiresArg: true, + }) + .option('force', { + alias: 'f', + type: 'boolean', + desc: 'Always bootstrap even if it would downgrade template version', + default: false, + }) + .option('termination-protection', { + type: 'boolean', + default: undefined, + desc: 'Toggle CloudFormation termination protection on the bootstrap stacks', + }) + .option('show-template', { + type: 'boolean', + desc: "Instead of actual bootstrapping, print the current CLI's bootstrapping template to stdout for customization", + default: false, + }) + .option('toolkit-stack-name', { + type: 'string', + desc: 'The name of the CDK toolkit stack to create', + requiresArg: true, + }) + .option('template', { + type: 'string', + requiresArg: true, + desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)', + }) + .option('previous-parameters', { + type: 'boolean', + default: true, + desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)', + }) + ) + .command(['gc [ENVIRONMENTS..]'], 'Garbage collect assets', (yargs: Argv) => + yargs + .option('action', { + type: 'string', + desc: 'The action (or sub-action) you want to perform. Valid entires are "print", "tag", "delete-tagged", "full".', + default: 'full', + }) + .option('type', { + type: 'string', + desc: 'Specify either ecr, s3, or all', + default: 'all', + }) + .option('rollback-buffer-days', { + type: 'number', + desc: 'Delete assets that have been marked as isolated for this many days', + default: 0, + }) + .option('created-buffer-days', { + type: 'number', + desc: 'Never delete assets younger than this (in days)', + default: 1, + }) + .option('confirm', { + type: 'boolean', + desc: 'Confirm via manual prompt before deletion', + default: true, + }) + .option('bootstrap-stack-name', { + type: 'string', + desc: 'The name of the CDK toolkit stack, if different from the default "CDKToolkit"', + requiresArg: true, + }) + ) + .command(['deploy [STACKS..]'], 'Deploys the stack(s) named STACKS into your AWS account', (yargs: Argv) => + yargs + .option('all', { + type: 'boolean', + desc: 'Deploy all available stacks', + default: false, + }) + .option('build-exclude', { + type: 'array', + alias: 'E', + nargs: 1, + desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', + default: [], + }) + .option('exclusively', { + type: 'boolean', + alias: 'e', + desc: "Only deploy requested stacks, don't include dependencies", + }) + .option('require-approval', { + type: 'string', + choices: ['never', 'any-change', 'broadening'], + desc: 'What security-sensitive changes need manual approval', + }) + .option('notification-arns', { + type: 'array', + desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', + nargs: 1, + requiresArg: true, + }) + .option('tags', { + type: 'array', + alias: 't', + desc: 'Tags to add to the stack (KEY=VALUE), overrides tags from Cloud Assembly (deprecated)', + nargs: 1, + requiresArg: true, + }) + .option('execute', { + type: 'boolean', + desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet) (deprecated)', + deprecated: true, + }) + .option('change-set-name', { + type: 'string', + desc: 'Name of the CloudFormation change set to create (only if method is not direct)', + }) + .option('method', { + alias: 'm', + type: 'string', + choices: ['direct', 'change-set', 'prepare-change-set'], + requiresArg: true, + desc: 'How to perform the deployment. Direct is a bit faster but lacks progress information', + }) + .option('force', { + alias: 'f', + type: 'boolean', + desc: 'Always deploy stack even if templates are identical', + default: false, + }) + .option('parameters', { + type: 'array', + desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', + nargs: 1, + requiresArg: true, + default: {}, + }) + .option('outputs-file', { + type: 'string', + alias: 'O', + desc: 'Path to file where stack outputs will be written as JSON', + requiresArg: true, + }) + .option('previous-parameters', { + type: 'boolean', + default: true, + desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)', + }) + .option('toolkit-stack-name', { + type: 'string', + desc: 'The name of the existing CDK toolkit stack (only used for app using legacy synthesis)', + requiresArg: true, + }) + .option('progress', { + type: 'string', + choices: ['bar', 'events'], + desc: 'Display mode for stack activity events', + }) + .option('rollback', { + type: 'boolean', + desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail", + }) + .middleware(yargsNegativeAlias('rollback', 'R'), true) + .option('R', { + type: 'boolean', + hidden: true, + }) + .option('hotswap', { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.Do not use this in production environments", + }) + .option('hotswap-fallback', { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, which skips CloudFormation and updates the resources directly, and falls back to a full deployment if that is not possible. Do not use this in production environments", + }) + .option('watch', { + type: 'boolean', + desc: 'Continuously observe the project files, and deploy the given stack(s) automatically when changes are detected. Implies --hotswap by default', + }) + .option('logs', { + type: 'boolean', + default: true, + desc: "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off. Only in effect if specified alongside the '--watch' option", + }) + .option('concurrency', { + type: 'number', + desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', + default: 1, + requiresArg: true, + }) + .option('asset-parallelism', { + type: 'boolean', + desc: 'Whether to build/publish assets in parallel', + }) + .option('asset-prebuild', { + type: 'boolean', + desc: 'Whether to build all assets before deploying the first stack (useful for failing Docker builds)', + default: true, + }) + .option('ignore-no-stacks', { + type: 'boolean', + desc: 'Whether to deploy if the app contains no stacks', + default: false, + }) + ) + .command(['rollback [STACKS..]'], 'Rolls back the stack(s) named STACKS to their last stable state', (yargs: Argv) => + yargs + .option('all', { + type: 'boolean', + default: false, + desc: 'Roll back all available stacks', + }) + .option('toolkit-stack-name', { + type: 'string', + desc: 'The name of the CDK toolkit stack the environment is bootstrapped with', + requiresArg: true, + }) + .option('force', { + alias: 'f', + type: 'boolean', + desc: 'Orphan all resources for which the rollback operation fails.', + }) + .option('validate-bootstrap-version', { + type: 'boolean', + desc: "Whether to validate the bootstrap stack version. Defaults to 'true', disable with --no-validate-bootstrap-version.", + }) + .option('orphan', { + type: 'array', + nargs: 1, + requiresArg: true, + desc: 'Orphan the given resources, identified by their logical ID (can be specified multiple times)', + default: [], + }) + ) + .command(['import [STACK]'], 'Import existing resource(s) into the given STACK', (yargs: Argv) => + yargs + .option('execute', { + type: 'boolean', + desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', + default: true, + }) + .option('change-set-name', { + type: 'string', + desc: 'Name of the CloudFormation change set to create', + }) + .option('toolkit-stack-name', { + type: 'string', + desc: 'The name of the CDK toolkit stack to create', + requiresArg: true, + }) + .option('rollback', { + type: 'boolean', + desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail", + }) + .option('force', { + alias: 'f', + type: 'boolean', + desc: "Do not abort if the template diff includes updates or deletes. This is probably safe but we're not sure, let us know how it goes.", + }) + .option('record-resource-mapping', { + type: 'string', + alias: 'r', + requiresArg: true, + desc: 'If specified, CDK will generate a mapping of existing physical resources to CDK resources to be imported as. The mapping will be written in the given file path. No actual import operation will be performed', + }) + .option('resource-mapping', { + type: 'string', + alias: 'm', + requiresArg: true, + desc: 'If specified, CDK will use the given file to map physical resources to CDK resources for import, instead of interactively asking the user. Can be run from scripts', + }) + ) + .command(['watch [STACKS..]'], "Shortcut for 'deploy --watch'", (yargs: Argv) => + yargs + .option('build-exclude', { + type: 'array', + alias: 'E', + nargs: 1, + desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', + default: [], + }) + .option('exclusively', { + type: 'boolean', + alias: 'e', + desc: "Only deploy requested stacks, don't include dependencies", + }) + .option('change-set-name', { + type: 'string', + desc: 'Name of the CloudFormation change set to create', + }) + .option('force', { + alias: 'f', + type: 'boolean', + desc: 'Always deploy stack even if templates are identical', + default: false, + }) + .option('toolkit-stack-name', { + type: 'string', + desc: 'The name of the existing CDK toolkit stack (only used for app using legacy synthesis)', + requiresArg: true, + }) + .option('progress', { + type: 'string', + choices: ['bar', 'events'], + desc: 'Display mode for stack activity events', + }) + .option('rollback', { + type: 'boolean', + desc: "Rollback stack to stable state on failure. Defaults to 'true', iterate more rapidly with --no-rollback or -R. Note: do **not** disable this flag for deployments with resource replacements, as that will always fail", + }) + .middleware(yargsNegativeAlias('rollback', '-R'), true) + .option('R', { + type: 'boolean', + hidden: true, + }) + .option('hotswap', { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, but does not fall back to a full deployment if that is not possible. Instead, changes to any non-hotswappable properties are ignored.'true' by default, use --no-hotswap to turn off", + }) + .option('hotswap-fallback', { + type: 'boolean', + desc: "Attempts to perform a 'hotswap' deployment, which skips CloudFormation and updates the resources directly, and falls back to a full deployment if that is not possible.", + }) + .option('logs', { + type: 'boolean', + default: true, + desc: "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off", + }) + .option('concurrency', { + type: 'number', + desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', + default: 1, + requiresArg: true, + }) + ) + .command(['destroy [STACKS..]'], 'Destroy the stack(s) named STACKS', (yargs: Argv) => + yargs + .option('all', { + type: 'boolean', + default: false, + desc: 'Destroy all available stacks', + }) + .option('exclusively', { + type: 'boolean', + alias: 'e', + desc: "Only destroy requested stacks, don't include dependees", + }) + .option('force', { + type: 'boolean', + alias: 'f', + desc: 'Do not ask for confirmation before destroying the stacks', + }) + ) + .command( + ['diff [STACKS..]'], + 'Compares the specified stack with the deployed stack or a local template file, and returns with status 1 if any difference is found', + (yargs: Argv) => + yargs + .option('exclusively', { + type: 'boolean', + alias: 'e', + desc: "Only diff requested stacks, don't include dependencies", + }) + .option('context-lines', { + type: 'number', + desc: 'Number of context lines to include in arbitrary JSON diff rendering', + default: 3, + requiresArg: true, + }) + .option('template', { + type: 'string', + desc: 'The path to the CloudFormation template to compare with', + requiresArg: true, + }) + .option('strict', { + type: 'boolean', + desc: 'Do not filter out AWS::CDK::Metadata resources, mangled non-ASCII characters, or the CheckBootstrapVersionRule', + default: false, + }) + .option('security-only', { + type: 'boolean', + desc: 'Only diff for broadened security changes', + default: false, + }) + .option('fail', { + type: 'boolean', + desc: 'Fail with exit code 1 in case of diff', + }) + .option('processed', { + type: 'boolean', + desc: 'Whether to compare against the template with Transforms already processed', + default: false, + }) + .option('quiet', { + type: 'boolean', + alias: 'q', + desc: 'Do not print stack name and default message when there is no diff to stdout', + default: false, + }) + .option('change-set', { + type: 'boolean', + alias: 'changeset', + desc: 'Whether to create a changeset to analyze resource replacements. In this mode, diff will use the deploy role instead of the lookup role.', + default: true, + }) + ) + .command(['metadata [STACK]'], 'Returns all metadata associated with this stack') + .command(['acknowledge [ID]', 'ack [ID]'], 'Acknowledge a notice so that it does not show up anymore') + .command(['notices'], 'Returns a list of relevant notices', (yargs: Argv) => + yargs.option('unacknowledged', { + type: 'boolean', + alias: 'u', + default: false, + desc: 'Returns a list of unacknowledged notices', + }) + ) + .command(['init [TEMPLATE]'], 'Create a new, empty CDK project from a template.', (yargs: Argv) => + yargs + .option('language', { + type: 'string', + alias: 'l', + desc: 'The language to be used for the new project (default can be configured in ~/.cdk.json)', + choices: availableInitLanguages, + }) + .option('list', { + type: 'boolean', + desc: 'List the available templates', + }) + .option('generate-only', { + type: 'boolean', + default: false, + desc: 'If true, only generates project files, without executing additional operations such as setting up a git repo, installing dependencies or compiling the project', + }) + ) + .command(['migrate'], false, (yargs: Argv) => + yargs + .option('stack-name', { + type: 'string', + alias: 'n', + desc: 'The name assigned to the stack created in the new project. The name of the app will be based off this name as well.', + requiresArg: true, + }) + .option('language', { + type: 'string', + default: 'typescript', + alias: 'l', + desc: 'The language to be used for the new project', + choices: migrateSupportedLanguages, + }) + .option('account', { + type: 'string', + desc: 'The account to retrieve the CloudFormation stack template from', + }) + .option('region', { + type: 'string', + desc: 'The region to retrieve the CloudFormation stack template from', + }) + .option('from-path', { + type: 'string', + desc: 'The path to the CloudFormation template to migrate. Use this for locally stored templates', + }) + .option('from-stack', { + type: 'boolean', + desc: 'Use this flag to retrieve the template for an existing CloudFormation stack', + }) + .option('output-path', { + type: 'string', + desc: 'The output path for the migrated CDK app', + }) + .option('from-scan', { + type: 'string', + desc: 'Determines if a new scan should be created, or the last successful existing scan should be used \n options are "new" or "most-recent"', + }) + .option('filter', { + type: 'array', + desc: 'Filters the resource scan based on the provided criteria in the following format: "key1=value1,key2=value2"\n This field can be passed multiple times for OR style filtering: \n filtering options: \n resource-identifier: A key-value pair that identifies the target resource. i.e. {"ClusterName", "myCluster"}\n resource-type-prefix: A string that represents a type-name prefix. i.e. "AWS::DynamoDB::"\n tag-key: a string that matches resources with at least one tag with the provided key. i.e. "myTagKey"\n tag-value: a string that matches resources with at least one tag with the provided value. i.e. "myTagValue"', + }) + .option('compress', { + type: 'boolean', + desc: 'Use this flag to zip the generated CDK app', + }) + ) + .command(['context'], 'Manage cached context values', (yargs: Argv) => + yargs + .option('reset', { + alias: 'e', + desc: 'The context key (or its index) to reset', + type: 'string', + requiresArg: true, + }) + .option('force', { + alias: 'f', + desc: 'Ignore missing key error', + type: 'boolean', + default: false, + }) + .option('clear', { + desc: 'Clear all context', + type: 'boolean', + }) + ) + .command(['docs', 'doc '], 'Opens the reference documentation in a browser', (yargs: Argv) => + yargs.option('browser', { + alias: 'b', + desc: 'the command to use to open the browser, using %u as a placeholder for the path of the file to open', + type: 'string', + default: browserDefault, + }) + ) + .command(['doctor'], 'Check your set-up for potential problems') + .version(version) + .demandCommand(1, "''") + .recommendCommands() + .help() + .alias('h', 'help') + .epilogue( + 'If your app has a single stack, there is no need to specify the stack name\n\nIf one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.' + ) + .parse(args); +} // eslint-disable-next-line @typescript-eslint/no-require-imports +const yargs = require('yargs'); diff --git a/packages/aws-cdk/scripts/yargs-gen.ts b/packages/aws-cdk/scripts/yargs-gen.ts index 93ed5d10196e6..f7b03c705a18e 100644 --- a/packages/aws-cdk/scripts/yargs-gen.ts +++ b/packages/aws-cdk/scripts/yargs-gen.ts @@ -4,7 +4,7 @@ import { renderYargs } from '@aws-cdk/yargs-gen'; import { makeConfig } from '../lib/config'; async function main() { - fs.writeFileSync('./lib/parse-command-line-arguments.ts', await renderYargs(await makeConfig())); + fs.writeFileSync('./lib/parse-command-line-arguments.ts', await renderYargs(makeConfig())); } main().then(() => { diff --git a/packages/aws-cdk/test/notices.test.ts b/packages/aws-cdk/test/notices.test.ts index b1a9cdf8bf31b..acc1fae841173 100644 --- a/packages/aws-cdk/test/notices.test.ts +++ b/packages/aws-cdk/test/notices.test.ts @@ -679,7 +679,7 @@ describe(Notices, () => { const configuration = new Configuration(); (configuration.context as any) = { get: (key: string) => context[key] }; - const notices = Notices.create({ configuration, includeAcknowlegded: true }); + const notices = Notices.create({ configuration, includeAcknowledged: true }); await notices.refresh({ dataSource: { fetch: async () => [BASIC_NOTICE, MULTIPLE_AFFECTED_VERSIONS_NOTICE] }, }); @@ -849,7 +849,7 @@ describe(Notices, () => { const configuration = new Configuration(); (configuration.context as any) = { get: (key: string) => context[key] }; - const notices = Notices.create({ configuration, includeAcknowlegded: true }); + const notices = Notices.create({ configuration, includeAcknowledged: true }); await notices.refresh({ dataSource: { fetch: async () => [BASIC_NOTICE, MULTIPLE_AFFECTED_VERSIONS_NOTICE] }, }); diff --git a/tools/@aws-cdk/yargs-gen/jest.config.js b/tools/@aws-cdk/yargs-gen/jest.config.js index 1a75d3f3b0092..696b3d5b6e281 100644 --- a/tools/@aws-cdk/yargs-gen/jest.config.js +++ b/tools/@aws-cdk/yargs-gen/jest.config.js @@ -4,7 +4,6 @@ module.exports = { ...baseConfig, coverageThreshold: { global: { - // Pretty bad but we disabled snapshots branches: 30, }, }, diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts index 44609acb55470..333d5da25d217 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-gen.ts @@ -1,6 +1,6 @@ import { Expression, FreeFunction, Module, SelectiveModuleImport, Statement, Type, TypeScriptRenderer, code } from '@cdklabs/typewriter'; import { EsLintRules } from '@cdklabs/typewriter/lib/eslint-rules'; -import { NULL } from '@cdklabs/typewriter/lib/expressions/builder'; +import * as prettier from 'prettier'; import { CliConfig, YargsOption } from './yargs-types'; export async function renderYargs(config: CliConfig): Promise { @@ -24,7 +24,7 @@ export async function renderYargs(config: CliConfig): Promise { returnType: Type.ANY, parameters: [ { name: 'args', type: Type.arrayOf(Type.STRING) }, - { name: 'browserDefault', type: Type.STRING, optional: true }, + { name: 'browserDefault', type: Type.STRING }, { name: 'availableInitLanguages', type: Type.arrayOf(Type.STRING) }, { name: 'migrateSupportedLanguages', type: Type.arrayOf(Type.STRING) }, { name: 'version', type: Type.STRING }, @@ -33,7 +33,7 @@ export async function renderYargs(config: CliConfig): Promise { }); parseCommandLineArguments.addBody(makeYargs(config)); - return new TypeScriptRenderer({ + const ts = new TypeScriptRenderer({ disabledEsLintRules: [ EsLintRules.COMMA_DANGLE, EsLintRules.COMMA_SPACING, @@ -42,6 +42,12 @@ export async function renderYargs(config: CliConfig): Promise { EsLintRules.QUOTE_PROPS, ], }).render(scope); + + return prettier.format(ts, { + parser: 'typescript', + printWidth: 150, + singleQuote: true, + }); } // Use the following configuration for array arguments: @@ -141,7 +147,7 @@ function lit(value: any): Expression { switch (value) { case undefined: return code.expr.UNDEFINED; - case NULL: + case null: return code.expr.NULL; default: return code.expr.lit(value); diff --git a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts index 825fefd16d11e..3ab73594bd557 100644 --- a/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts +++ b/tools/@aws-cdk/yargs-gen/lib/yargs-types.ts @@ -17,7 +17,6 @@ interface YargsCommand { arg?: YargsArg; } -// might need to expand interface YargsArg { name: string; variadic: boolean; diff --git a/tools/@aws-cdk/yargs-gen/package.json b/tools/@aws-cdk/yargs-gen/package.json index 0f84a9c46c144..7a794db607fa3 100644 --- a/tools/@aws-cdk/yargs-gen/package.json +++ b/tools/@aws-cdk/yargs-gen/package.json @@ -29,7 +29,7 @@ "license": "Apache-2.0", "dependencies": { "@cdklabs/typewriter": "^0.0.4", - "yargs": "^16.2.0" + "prettier": "^2.8.8" }, "devDependencies": { "@aws-cdk/cdk-build-tools": "0.0.0", diff --git a/tools/@aws-cdk/yargs-gen/test/cli.test.ts b/tools/@aws-cdk/yargs-gen/test/cli.test.ts index 69f4567e06e7c..bdcea845d222d 100644 --- a/tools/@aws-cdk/yargs-gen/test/cli.test.ts +++ b/tools/@aws-cdk/yargs-gen/test/cli.test.ts @@ -1,5 +1,75 @@ -describe('nothing', () => { - test('can generate specific services', async () => { - expect(2 + 2).toBe(4); +import { CliConfig, renderYargs } from '../lib'; + +describe('render', () => { + test('can generate global options', async () => { + const config: CliConfig = { + globalOptions: { + one: { + type: 'string', + alias: 'o', + desc: 'text for one', + requiresArg: true, + }, + two: { type: 'number', desc: 'text for two' }, + three: { + type: 'array', + alias: 't', + desc: 'text for three', + nargs: 1, + requiresArg: true, + }, + }, + commands: {}, + }; + + expect(await renderYargs(config)).toMatchInlineSnapshot(` + "// ------------------------------------------------------------------------------------------- + // 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. + // ------------------------------------------------------------------------------------------- + /* eslint-disable @typescript-eslint/comma-dangle, comma-spacing, max-len, quotes, quote-props */ + import { Argv } from 'yargs'; + + // @ts-ignore TS6133 + export function parseCommandLineArguments( + args: Array, + browserDefault: string, + availableInitLanguages: Array, + migrateSupportedLanguages: Array, + version: string, + yargsNegativeAlias: any + ): any { + return yargs + .usage('Usage: cdk -a COMMAND') + .option('one', { + type: 'string', + alias: 'o', + desc: 'text for one', + requiresArg: true, + }) + .option('two', { + type: 'number', + desc: 'text for two', + }) + .option('three', { + type: 'array', + alias: 't', + desc: 'text for three', + nargs: 1, + requiresArg: true, + }) + .version(version) + .demandCommand(1, "''") + .recommendCommands() + .help() + .alias('h', 'help') + .epilogue( + 'If your app has a single stack, there is no need to specify the stack name\\n\\nIf one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence.' + ) + .parse(args); + } // eslint-disable-next-line @typescript-eslint/no-require-imports + const yargs = require('yargs'); + " + `); }); }); diff --git a/yarn.lock b/yarn.lock index 84f708223af3e..9e01d269b8845 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13380,6 +13380,11 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -14587,7 +14592,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -14652,7 +14666,7 @@ stringify-package@^1.0.1: resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -14666,6 +14680,13 @@ strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -15668,7 +15689,7 @@ workerpool@^6.5.1: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -15686,6 +15707,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From f7a09b03ad0e9c2645f1325c3b8ea5de7754179a Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Tue, 5 Nov 2024 11:50:36 +0000 Subject: [PATCH 23/23] latest updates --- packages/aws-cdk/lib/config.ts | 4 ++-- packages/aws-cdk/lib/parse-command-line-arguments.ts | 2 +- packages/aws-cdk/package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk/lib/config.ts b/packages/aws-cdk/lib/config.ts index f69dcd0cba70d..5606e65fd22f3 100644 --- a/packages/aws-cdk/lib/config.ts +++ b/packages/aws-cdk/lib/config.ts @@ -35,7 +35,7 @@ export function makeConfig(): CliConfig { 'notices': { type: 'boolean', desc: 'Show relevant notices' }, 'no-color': { type: 'boolean', desc: 'Removes colors and other style from console output', default: false }, 'ci': { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', default: DynamicValue.fromInline(() => process.env.CI !== undefined) }, - 'unstable': { type: 'array', desc: 'Opt in to specific unstable features. Can be specified multiple times.', default: [] }, + 'unstable': { type: 'array', desc: 'Opt in to unstable features. The flag indicates that the scope and API of a feature might still change. Otherwise the feature is generally production ready and fully supported. Can be specified multiple times.', default: [] }, }, commands: { 'list': { @@ -91,7 +91,7 @@ export function makeConfig(): CliConfig { }, }, gc: { - description: 'Garbage collect assets', + description: 'Garbage collect assets. Options detailed here: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk/README.md#cdk-gc', arg: { name: 'ENVIRONMENTS', variadic: true, diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index e570f0d972ed0..30379f899b104 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -145,7 +145,7 @@ export function parseCommandLineArguments( }) .option('unstable', { type: 'array', - desc: 'Opt in to specific unstable features. Can be specified multiple times.', + desc: 'Opt in to unstable features. The flag indicates that the scope and API of a feature might still change. Otherwise the feature is generally production ready and fully supported. Can be specified multiple times.', default: [], }) .command(['list [STACKS..]', 'ls [STACKS..]'], 'Lists all stacks in the app', (yargs: Argv) => diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index bfcc16e1ca09d..85db0da1aa673 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -7,7 +7,7 @@ }, "scripts": { "build": "cdk-build", - "yargs-gen": "yarn ts-node scripts/yargs-gen.ts", + "yargs-gen": "yarn ts-node --preferTsExts scripts/yargs-gen.ts", "watch": "cdk-watch", "lint": "cdk-lint", "pkglint": "pkglint -f",