UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

741 lines 146 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.cli = exports.exec = void 0; const cxapi = require("@aws-cdk/cx-api"); require("@jsii/check-node/run"); const chalk = require("chalk"); const source_map_support_1 = require("source-map-support"); const common_1 = require("./api/hotswap/common"); const platform_warnings_1 = require("./platform-warnings"); const tracing_1 = require("./util/tracing"); const aws_auth_1 = require("../lib/api/aws-auth"); const bootstrap_1 = require("../lib/api/bootstrap"); const cloud_executable_1 = require("../lib/api/cxapp/cloud-executable"); const exec_1 = require("../lib/api/cxapp/exec"); const deployments_1 = require("../lib/api/deployments"); const plugin_1 = require("../lib/api/plugin"); const toolkit_info_1 = require("../lib/api/toolkit-info"); const stack_activity_monitor_1 = require("../lib/api/util/cloudformation/stack-activity-monitor"); const cdk_toolkit_1 = require("../lib/cdk-toolkit"); const context_1 = require("../lib/commands/context"); const docs_1 = require("../lib/commands/docs"); const doctor_1 = require("../lib/commands/doctor"); const migrate_1 = require("../lib/commands/migrate"); const diff_1 = require("../lib/diff"); const init_1 = require("../lib/init"); const logging_1 = require("../lib/logging"); const notices_1 = require("../lib/notices"); const settings_1 = require("../lib/settings"); const version = require("../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) { // 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 = { darwin: 'open %u', win32: 'start %u', }; const initTemplateLanguages = await (0, init_1.availableInitLanguages)(); return yargs .env('CDK') .usage('Usage: cdk -a <cdk-app> 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) => yargs .option('long', { type: 'boolean', default: false, alias: 'l', desc: 'Display environment information for each stack' })) .command(['synthesize [STACKS..]', 'synth [STACKS..]'], 'Synthesizes and prints the CloudFormation template for this stack', (yargs) => 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) => 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', 'example-permissions-boundary'], desc: 'Use the example permissions boundary.', default: undefined, conflicts: 'custom-permissions-boundary' }) .option('custom-permissions-boundary', { type: 'string', alias: ['cpb', 'custom-permissions-boundary'], 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) => 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: [diff_1.RequireApproval.Never, diff_1.RequireApproval.AnyChange, diff_1.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: [stack_activity_monitor_1.StackActivityProgress.BAR, stack_activity_monitor_1.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 })) .command('import [STACK]', 'Import existing resource(s) into the given STACK', (yargs) => 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) => 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: [stack_activity_monitor_1.StackActivityProgress.BAR, stack_activity_monitor_1.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) => 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) => 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 or mangled non-ASCII characters', 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 })) .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') .command('init [TEMPLATE]', 'Create a new, empty CDK project from a template.', (yargs) => 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) => 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_1.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('compress', { type: 'boolean', desc: 'Use this flag to zip the generated CDK app' })) .command('context', 'Manage cached context values', (yargs) => 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) => 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'; } async function exec(args, synthesizer) { const argv = await parseCommandLineArguments(args); if (argv.debug) { (0, source_map_support_1.install)(); } if (argv.verbose) { (0, logging_1.setLogLevel)(argv.verbose); if (argv.verbose > 2) { (0, tracing_1.enableTracing)(true); } } if (argv.ci) { (0, logging_1.setCI)(true); } try { await (0, platform_warnings_1.checkForPlatformWarnings)(); } catch (e) { (0, logging_1.debug)(`Error while checking for platform warnings: ${e}`); } (0, logging_1.debug)('CDK toolkit version:', version.DISPLAY_VERSION); (0, logging_1.debug)('Command line arguments:', argv); const configuration = new settings_1.Configuration({ commandLineArguments: { ...argv, _: argv._, // TypeScript at its best }, }); await configuration.load(); if (shouldDisplayNotices()) { void (0, notices_1.refreshNotices)() .catch(e => (0, logging_1.debug)(`Could not refresh notices: ${e}`)); } const sdkProvider = await aws_auth_1.SdkProvider.withAwsCliCompatibleDefaults({ profile: configuration.settings.get(['profile']), ec2creds: argv.ec2creds, httpOptions: { proxyAddress: argv.proxy, caBundlePath: argv['ca-bundle-path'], }, }); let outDirLock; const cloudExecutable = new cloud_executable_1.CloudExecutable({ configuration, sdkProvider, synthesizer: synthesizer ?? (async (aws, config) => { // Invoke 'execProgram', and copy the lock for the directory in the global // variable here. It will be released when the CLI exits. Locks are not re-entrant // so release it if we have to synthesize more than once (because of context lookups). await outDirLock?.release(); const { assembly, lock } = await (0, exec_1.execProgram)(aws, config); outDirLock = lock; return assembly; }), }); /** Function to load plug-ins, using configurations additively. */ function loadPlugins(...settings) { const loaded = new Set(); for (const source of settings) { const plugins = source.get(['plugin']) || []; for (const plugin of plugins) { const resolved = tryResolve(plugin); if (loaded.has(resolved)) { continue; } (0, logging_1.debug)(`Loading plug-in: ${chalk.green(plugin)} from ${chalk.blue(resolved)}`); plugin_1.PluginHost.instance.load(plugin); loaded.add(resolved); } } function tryResolve(plugin) { try { return require.resolve(plugin); } catch (e) { (0, logging_1.error)(`Unable to resolve plugin ${chalk.green(plugin)}: ${e.stack}`); throw new Error(`Unable to resolve plug-in: ${plugin}`); } } } loadPlugins(configuration.settings); const cmd = argv._[0]; if (typeof (cmd) !== 'string') { throw new Error(`First argument should be a string. Got: ${cmd} (${typeof (cmd)})`); } // Bundle up global objects so the commands have access to them const commandOptions = { args: argv, configuration, aws: sdkProvider }; try { return await main(cmd, argv); } finally { // If we locked the 'cdk.out' directory, release it here. await outDirLock?.release(); // Do PSAs here await version.displayVersionMessage(); if (shouldDisplayNotices()) { if (cmd === 'notices') { await (0, notices_1.displayNotices)({ outdir: configuration.settings.get(['output']) ?? 'cdk.out', acknowledgedIssueNumbers: [], ignoreCache: true, }); } else if (cmd !== 'version') { await (0, notices_1.displayNotices)({ outdir: configuration.settings.get(['output']) ?? 'cdk.out', acknowledgedIssueNumbers: configuration.context.get('acknowledged-issue-numbers') ?? [], ignoreCache: false, }); } } } function shouldDisplayNotices() { return configuration.settings.get(['notices']) ?? true; } async function main(command, args) { const toolkitStackName = toolkit_info_1.ToolkitInfo.determineName(configuration.settings.get(['toolkitStackName'])); (0, logging_1.debug)(`Toolkit stack: ${chalk.bold(toolkitStackName)}`); const cloudFormation = new deployments_1.Deployments({ sdkProvider, toolkitStackName }); if (args.all && args.STACKS) { throw new Error('You must either specify a list of Stacks or the `--all` argument'); } args.STACKS = args.STACKS ?? (args.STACK ? [args.STACK] : []); args.ENVIRONMENTS = args.ENVIRONMENTS ?? []; const selector = { allTopLevel: args.all, patterns: args.STACKS, }; const cli = new cdk_toolkit_1.CdkToolkit({ cloudExecutable, deployments: cloudFormation, verbose: argv.trace || argv.verbose > 0, ignoreErrors: argv['ignore-errors'], strict: argv.strict, configuration, sdkProvider, }); switch (command) { case 'context': return (0, context_1.realHandler)(commandOptions); case 'docs': return (0, docs_1.realHandler)(commandOptions); case 'doctor': return (0, doctor_1.realHandler)(commandOptions); case 'ls': case 'list': return cli.list(args.STACKS, { long: args.long, json: argv.json }); case 'diff': const enableDiffNoFail = isFeatureEnabled(configuration, cxapi.ENABLE_DIFF_NO_FAIL_CONTEXT); return cli.diff({ stackNames: args.STACKS, exclusively: args.exclusively, templatePath: args.template, strict: args.strict, contextLines: args.contextLines, securityOnly: args.securityOnly, fail: args.fail != null ? args.fail : !enableDiffNoFail, stream: args.ci ? process.stdout : undefined, compareAgainstProcessedTemplate: args.processed, quiet: args.quiet, }); case 'bootstrap': const source = determineBootstrapVersion(args, configuration); const bootstrapper = new bootstrap_1.Bootstrapper(source); if (args.showTemplate) { return bootstrapper.showTemplate(args.json); } return cli.bootstrap(args.ENVIRONMENTS, bootstrapper, { roleArn: args.roleArn, force: argv.force, toolkitStackName: toolkitStackName, execute: args.execute, tags: configuration.settings.get(['tags']), terminationProtection: args.terminationProtection, usePreviousParameters: args['previous-parameters'], parameters: { bucketName: configuration.settings.get(['toolkitBucket', 'bucketName']), kmsKeyId: configuration.settings.get(['toolkitBucket', 'kmsKeyId']), createCustomerMasterKey: args.bootstrapCustomerKey, qualifier: args.qualifier, publicAccessBlockConfiguration: args.publicAccessBlockConfiguration, examplePermissionsBoundary: argv.examplePermissionsBoundary, customPermissionsBoundary: argv.customPermissionsBoundary, trustedAccounts: arrayFromYargs(args.trust), trustedAccountsForLookup: arrayFromYargs(args.trustForLookup), cloudFormationExecutionPolicies: arrayFromYargs(args.cloudformationExecutionPolicies), }, }); case 'deploy': const parameterMap = {}; for (const parameter of args.parameters) { if (typeof parameter === 'string') { const keyValue = parameter.split('='); parameterMap[keyValue[0]] = keyValue.slice(1).join('='); } } if (args.execute !== undefined && args.method !== undefined) { throw new Error('Can not supply both --[no-]execute and --method at the same time'); } let deploymentMethod; switch (args.method) { case 'direct': if (args.changeSetName) { throw new Error('--change-set-name cannot be used with method=direct'); } deploymentMethod = { method: 'direct' }; break; case 'change-set': deploymentMethod = { method: 'change-set', execute: true, changeSetName: args.changeSetName }; break; case 'prepare-change-set': deploymentMethod = { method: 'change-set', execute: false, changeSetName: args.changeSetName }; break; case undefined: deploymentMethod = { method: 'change-set', execute: args.execute ?? true, changeSetName: args.changeSetName }; break; } return cli.deploy({ selector, exclusively: args.exclusively, toolkitStackName, roleArn: args.roleArn, notificationArns: args.notificationArns, requireApproval: configuration.settings.get(['requireApproval']), reuseAssets: args['build-exclude'], tags: configuration.settings.get(['tags']), deploymentMethod, force: args.force, parameters: parameterMap, usePreviousParameters: args['previous-parameters'], outputsFile: configuration.settings.get(['outputsFile']), progress: configuration.settings.get(['progress']), ci: args.ci, rollback: configuration.settings.get(['rollback']), hotswap: determineHotswapMode(args.hotswap, args.hotswapFallback), watch: args.watch, traceLogs: args.logs, concurrency: args.concurrency, assetParallelism: configuration.settings.get(['assetParallelism']), assetBuildTime: configuration.settings.get(['assetPrebuild']) ? cdk_toolkit_1.AssetBuildTime.ALL_BEFORE_DEPLOY : cdk_toolkit_1.AssetBuildTime.JUST_IN_TIME, }); case 'import': return cli.import({ selector, toolkitStackName, roleArn: args.roleArn, deploymentMethod: { method: 'change-set', execute: args.execute, changeSetName: args.changeSetName, }, progress: configuration.settings.get(['progress']), rollback: configuration.settings.get(['rollback']), recordResourceMapping: args['record-resource-mapping'], resourceMappingFile: args['resource-mapping'], force: args.force, }); case 'watch': return cli.watch({ selector, // parameters: parameterMap, // usePreviousParameters: args['previous-parameters'], // outputsFile: configuration.settings.get(['outputsFile']), // requireApproval: configuration.settings.get(['requireApproval']), // notificationArns: args.notificationArns, exclusively: args.exclusively, toolkitStackName, roleArn: args.roleArn, reuseAssets: args['build-exclude'], deploymentMethod: { method: 'change-set', changeSetName: args.changeSetName, }, force: args.force, progress: configuration.settings.get(['progress']), rollback: configuration.settings.get(['rollback']), hotswap: determineHotswapMode(args.hotswap, args.hotswapFallback, true), traceLogs: args.logs, concurrency: args.concurrency, }); case 'destroy': return cli.destroy({ selector, exclusively: args.exclusively, force: args.force, roleArn: args.roleArn, ci: args.ci, }); case 'synthesize': case 'synth': const quiet = configuration.settings.get(['quiet']) ?? args.quiet; if (args.exclusively) { return cli.synth(args.STACKS, args.exclusively, quiet, args.validation, argv.json); } else { return cli.synth(args.STACKS, true, quiet, args.validation, argv.json); } case 'notices': // This is a valid command, but we're postponing its execution return; case 'metadata': return cli.metadata(args.STACK, argv.json); case 'acknowledge': case 'ack': return cli.acknowledge(args.ID); case 'init': const language = configuration.settings.get(['language']); if (args.list) { return (0, init_1.printAvailableTemplates)(language); } else { return (0, init_1.cliInit)({ type: args.TEMPLATE, language, canUseNetwork: undefined, generateOnly: args.generateOnly, }); } case 'migrate': return cli.migrate({ stackName: args['stack-name'], fromPath: args['from-path'], fromStack: args['from-stack'], language: args.language, outputPath: args['output-path'], account: args.account, region: args.region, compress: args.compress, }); case 'version': return (0, logging_1.data)(version.DISPLAY_VERSION); default: throw new Error('Unknown command: ' + command); } } } exports.exec = exec; /** * Determine which version of bootstrapping * (legacy, or "new") should be used. */ function determineBootstrapVersion(args, configuration) { const isV1 = version.DISPLAY_VERSION.startsWith('1.'); return isV1 ? determineV1BootstrapSource(args, configuration) : determineV2BootstrapSource(args); } function determineV1BootstrapSource(args, configuration) { let source; if (args.template) { (0, logging_1.print)(`Using bootstrapping template from ${args.template}`); source = { source: 'custom', templateFile: args.template }; } else if (process.env.CDK_NEW_BOOTSTRAP) { (0, logging_1.print)('CDK_NEW_BOOTSTRAP set, using new-style bootstrapping'); source = { source: 'default' }; } else if (isFeatureEnabled(configuration, cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT)) { (0, logging_1.print)(`'${cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT}' context set, using new-style bootstrapping`); source = { source: 'default' }; } else { // in V1, the "legacy" bootstrapping is the default source = { source: 'legacy' }; } return source; } function determineV2BootstrapSource(args) { let source; if (args.template) { (0, logging_1.print)(`Using bootstrapping template from ${args.template}`); source = { source: 'custom', templateFile: args.template }; } else if (process.env.CDK_LEGACY_BOOTSTRAP) { (0, logging_1.print)('CDK_LEGACY_BOOTSTRAP set, using legacy-style bootstrapping'); source = { source: 'legacy' }; } else { // in V2, the "new" bootstrapping is the default source = { source: 'default' }; } return source; } function isFeatureEnabled(configuration, featureFlag) { return configuration.context.get(featureFlag) ?? cxapi.futureFlagDefault(featureFlag); } /** * Translate a Yargs input array to something that makes more sense in a programming language * model (telling the difference between absence and an empty array) * * - An empty array is the default case, meaning the user didn't pass any arguments. We return * undefined. * - If the user passed a single empty string, they did something like `--array=`, which we'll * take to mean they passed an empty array. */ function arrayFromYargs(xs) { if (xs.length === 0) { return undefined; } return xs.filter(x => x !== ''); } function yargsNegativeAlias(shortName, longName) { return (argv) => { if (shortName in argv && argv[shortName]) { argv[longName] = false; } return argv; }; } function determineHotswapMode(hotswap, hotswapFallback, watch) { if (hotswap && hotswapFallback) { throw new Error('Can not supply both --hotswap and --hotswap-fallback at the same time'); } else if (!hotswap && !hotswapFallback) { if (hotswap === undefined && hotswapFallback === undefined) { return watch ? common_1.HotswapMode.HOTSWAP_ONLY : common_1.HotswapMode.FULL_DEPLOYMENT; } else if (hotswap === false || hotswapFallback === false) { return common_1.HotswapMode.FULL_DEPLOYMENT; } } let hotswapMode; if (hotswap) { hotswapMode = common_1.HotswapMode.HOTSWAP_ONLY; } else /*if (hotswapFallback)*/ { hotswapMode = common_1.HotswapMode.FALL_BACK; } return hotswapMode; } function cli(args = process.argv.slice(2)) { exec(args) .then(async (value) => { if (typeof value === 'number') { process.exitCode = value; } }) .catch(err => { (0, logging_1.error)(err.message); if (err.stack) { (0, logging_1.debug)(err.stack); } process.exitCode = 1; }); } exports.cli = cli; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlDQUF5QztBQUN6QyxnQ0FBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDJEQUF1RTtBQUl2RSxpREFBbUQ7QUFFbkQsMkRBQStEO0FBQy9ELDRDQUErQztBQUMvQyxrREFBa0Q7QUFDbEQsb0RBQXFFO0FBRXJFLHdFQUFpRjtBQUNqRixnREFBb0Q7QUFDcEQsd0RBQXFEO0FBQ3JELDhDQUErQztBQUMvQywwREFBc0Q7QUFDdEQsa0dBQThGO0FBQzlGLG9EQUFnRTtBQUNoRSxxREFBaUU7QUFDakUsK0NBQTJEO0FBQzNELG1EQUErRDtBQUMvRCxxREFBc0U7QUFDdEUsc0NBQThDO0FBQzlDLHNDQUF1RjtBQUN2Riw0Q0FBK0U7QUFDL0UsNENBQWdFO0FBQ2hFLDhDQUFtRTtBQUNuRSwwQ0FBMEM7QUFFMUMsNkNBQTZDO0FBQzdDLCtDQUErQztBQUMvQyxpRUFBaUU7QUFDakUsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBRS9CLDRCQUE0QjtBQUM1QixpREFBaUQsQ0FBQyxRQUFRO0FBRTFELEtBQUssVUFBVSx5QkFBeUIsQ0FBQyxJQUFjO0lBQ3JELHVEQUF1RDtJQUN2RCxFQUFFO0lBQ0Ysa0VBQWtFO0lBQ2xFLEVBQUU7SUFDRixtRkFBbUY7SUFDbkYsRUFBRTtJQUNGLGdJQUFnSTtJQUNoSSw4SEFBOEg7SUFDOUgsRUFBRTtJQUNGLHNHQUFzRztJQUN0RyxFQUFFO0lBQ0Ysd0dBQXdHO0lBRXhHLE1BQU0scUJBQXFCLEdBQTBDO1FBQ25FLE1BQU0sRUFBRSxTQUFTO1FBQ2pCLEtBQUssRUFBRSxVQUFVO0tBQ2xCLENBQUM7SUFFRixNQUFNLHFCQUFxQixHQUFHLE1BQU0sSUFBQSw2QkFBc0IsR0FBRSxDQUFDO0lBQzdELE9BQU8sS0FBSztTQUNULEdBQUcsQ0FBQyxLQUFLLENBQUM7U0FDVixLQUFLLENBQUMsaUNBQWlDLENBQUM7U0FDeEMsTUFBTSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsNEtBQTRLLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxDQUFDO1NBQ3BQLE1BQU0sQ0FBQyxPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxvQ0FBb0MsRUFBRSxDQUFDO1NBQy9FLE1BQU0sQ0FBQyxTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLDZDQUE2QyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxDQUFDO1NBQ2xJLE1BQU0sQ0FBQyxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLDhGQUE4RixFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsQ0FBQztTQUMvSixNQUFNLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsZ0NBQWdDLEVBQUUsQ0FBQztTQUM1RSxNQUFNLENBQUMsUUFBUSxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsdUNBQXVDLEVBQUUsQ0FBQztTQUNwRixNQUFNLENBQUMsU0FBUyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsd0dBQXdHLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1NBQ3JLLE1BQU0sQ0FBQyxlQUFlLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLHVFQUF1RSxFQUFFLENBQUM7U0FDM0ksTUFBTSxDQUFDLE1BQU0sRUFBRSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsc0VBQXNFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxDQUFDO1NBQzdJLE1BQU0sQ0FBQyxTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLGdFQUFnRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQztTQUMxSSxLQUFLLENBQUMsU0FBUyxDQUFDO1NBQ2hCLE1BQU0sQ0FBQyxPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSw4RkFBOEYsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUM7U0FDMUosTUFBTSxDQUFDLFNBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLDBEQUEwRCxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQztTQUMxSCxNQUFNLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsMkZBQTJGLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxDQUFDO1NBQ3pKLE1BQU0sQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVB