From ef26d33058a7ca6614ea01dc7f244f9545e4da5a Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Wed, 10 Jun 2026 17:30:09 +0100 Subject: [PATCH] feat(cli): add --debug-app and --debug-cli flags Split the `--debug` flag into `--debug-app` (sets CDK_DEBUG, enables app-level tracing) and `--debug-cli` (enables AWS SDK tracing). The existing `--debug` flag now implies both via a new `yargsImplies` middleware. Config file backwards compatibility is preserved: `debug: true` in cdk.json implies both targets. Also adds configuration reporting to `cdk doctor` and extracts `shouldDisplayVersionMessage()` for cleaner version display logic. --- .projenrc.ts | 2 +- aws-cdk-cli.code-workspace | 2 +- .../doctor/cdk-doctor.integtest.ts | 2 +- .../lib/api/cloud-assembly/environment.ts | 2 +- .../api/cloud-assembly/environment.test.ts | 25 ++++++++++- .../@aws-cdk/user-input-gen/lib/yargs-gen.ts | 14 ++++++- .../user-input-gen/lib/yargs-types.ts | 9 ++++ packages/aws-cdk/lib/cli/cli-config.ts | 4 +- .../aws-cdk/lib/cli/cli-type-registry.json | 18 +++++++- packages/aws-cdk/lib/cli/cli.ts | 15 ++++--- .../aws-cdk/lib/cli/convert-to-user-input.ts | 4 ++ packages/aws-cdk/lib/cli/display-version.ts | 8 ++-- .../lib/cli/parse-command-line-arguments.ts | 13 +++++- .../aws-cdk/lib/cli/user-configuration.ts | 20 +++++++-- packages/aws-cdk/lib/cli/user-input.ts | 16 +++++++- .../aws-cdk/lib/cli/util/yargs-helpers.ts | 19 +++++++++ packages/aws-cdk/lib/commands/context.ts | 2 - packages/aws-cdk/lib/commands/doctor.ts | 41 +++++++++++++++++-- .../aws-cdk/test/cli/cli-arguments.test.ts | 2 + .../aws-cdk/test/cli/cli-commands.test.ts | 2 +- .../aws-cdk/test/cli/configuration.test.ts | 34 +++++++++++++++ .../cli/parse-command-line-arguments.test.ts | 34 +++++++++++++++ packages/aws-cdk/test/cli/user-config.test.ts | 23 +++++++++++ packages/aws-cdk/test/cli/version.test.ts | 9 +--- .../aws-cdk/test/commands/cdk-doctor.test.ts | 34 +++++++++++++++ 25 files changed, 312 insertions(+), 42 deletions(-) diff --git a/.projenrc.ts b/.projenrc.ts index 139c0cbe0..c9815291d 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -345,7 +345,7 @@ const repoProject = new yarn.Monorepo({ }); repoProject.tryFindObjectFile(`${repoProject.name}.code-workspace`)?.patch( - pj.JsonPatch.add('/settings/jest.jestCommandLine', 'npx jest'), + pj.JsonPatch.add('/settings/jest.jestCommandLine', 'yarn jest'), pj.JsonPatch.add('/settings/js~1ts.tsdk.path', '/node_modules/typescript/lib'), ); diff --git a/aws-cdk-cli.code-workspace b/aws-cdk-cli.code-workspace index bc246cdc2..c18d4e7ce 100644 --- a/aws-cdk-cli.code-workspace +++ b/aws-cdk-cli.code-workspace @@ -49,7 +49,7 @@ "files.exclude": { "packages": true }, - "jest.jestCommandLine": "npx jest", + "jest.jestCommandLine": "yarn jest", "js/ts.tsdk.path": "/node_modules/typescript/lib" } } diff --git a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/doctor/cdk-doctor.integtest.ts b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/doctor/cdk-doctor.integtest.ts index 8bf123d92..2e616a728 100644 --- a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/doctor/cdk-doctor.integtest.ts +++ b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/doctor/cdk-doctor.integtest.ts @@ -4,7 +4,7 @@ integTest( 'cdk doctor displays diagnostic information', withDefaultFixture(async (fixture) => { const output = await fixture.cdk(['doctor']); - expect(output).toContain('CDK Version'); + expect(output).toContain('CDK CLI Version'); expect(output).toContain('AWS environment variables'); expect(output).toContain('CDK environment variables'); }), diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/environment.ts b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/environment.ts index 4ec5ff561..7f8129090 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/environment.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/environment.ts @@ -89,7 +89,7 @@ export function synthParametersFromSettings(settings: Settings): { env: { // An environment variable instead of a context variable, so it can also // be accessed in framework code where we don't have access to a construct tree. - ...settings.get(['debug']) ? { CDK_DEBUG: 'true' } : {}, + ...settings.get(['debugApp']) ? { CDK_DEBUG: 'true' } : {}, }, }; } diff --git a/packages/@aws-cdk/toolkit-lib/test/api/cloud-assembly/environment.test.ts b/packages/@aws-cdk/toolkit-lib/test/api/cloud-assembly/environment.test.ts index 431e5b093..1a921231d 100644 --- a/packages/@aws-cdk/toolkit-lib/test/api/cloud-assembly/environment.test.ts +++ b/packages/@aws-cdk/toolkit-lib/test/api/cloud-assembly/environment.test.ts @@ -1,5 +1,6 @@ import * as fs from 'fs-extra'; -import { guessExecutable } from '../../../lib/api/cloud-assembly/environment'; +import { guessExecutable, synthParametersFromSettings } from '../../../lib/api/cloud-assembly/environment'; +import { Settings } from '../../../lib/api/settings'; const BOTH = 'both' as const; const DONTCARE = 'DONT-CARE'; @@ -76,3 +77,25 @@ function explodeBoth(input: [F, ...R]): type NotBoth = Exclude; type NotBothA = { [I in keyof A]: NotBoth }; + +describe('synthParametersFromSettings sets CDK_DEBUG from debugApp', () => { + test('debugApp: true sets CDK_DEBUG', () => { + const { env } = synthParametersFromSettings(new Settings({ debugApp: true })); + expect(env.CDK_DEBUG).toBe('true'); + }); + + test('debugApp: false does not set CDK_DEBUG', () => { + const { env } = synthParametersFromSettings(new Settings({ debugApp: false })); + expect(env.CDK_DEBUG).toBeUndefined(); + }); + + test('debugCli alone does not set CDK_DEBUG', () => { + const { env } = synthParametersFromSettings(new Settings({ debugCli: true })); + expect(env.CDK_DEBUG).toBeUndefined(); + }); + + test('no debug settings does not set CDK_DEBUG', () => { + const { env } = synthParametersFromSettings(new Settings({})); + expect(env.CDK_DEBUG).toBeUndefined(); + }); +}); diff --git a/packages/@aws-cdk/user-input-gen/lib/yargs-gen.ts b/packages/@aws-cdk/user-input-gen/lib/yargs-gen.ts index a40f0c789..7d7bff894 100644 --- a/packages/@aws-cdk/user-input-gen/lib/yargs-gen.ts +++ b/packages/@aws-cdk/user-input-gen/lib/yargs-gen.ts @@ -2,7 +2,7 @@ import type { IScope, Statement } from '@cdklabs/typewriter'; import { $E, Expression, ExternalModule, FreeFunction, Module, SelectiveModuleImport, ThingSymbol, Type, TypeScriptRenderer, code, expr } from '@cdklabs/typewriter'; import { EsLintRules } from '@cdklabs/typewriter/lib/eslint-rules'; import * as prettier from 'prettier'; -import { lit, SOURCE_OF_TRUTH } from './util'; +import { lit, SOURCE_OF_TRUTH, kebabToCamelCase } from './util'; import type { CliConfig, CliOption, YargsOption } from './yargs-types'; // to import lodash.clonedeep properly, we would need to set esModuleInterop: true @@ -16,6 +16,7 @@ export class CliHelpers extends ExternalModule { public readonly isCI = makeCallableExpr(this, 'isCI'); public readonly shouldDisplayNotices = makeCallableExpr(this, 'shouldDisplayNotices'); public readonly yargsNegativeAlias = makeCallableExpr(this, 'yargsNegativeAlias'); + public readonly yargsImplies = makeCallableExpr(this, 'yargsImplies'); } function makeCallableExpr(scope: IScope, name: string) { @@ -141,7 +142,7 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: CliOpt optionProps.requiresArg = true; } - for (const optionProp of Object.keys(optionProps).filter(opt => !['negativeAlias'].includes(opt))) { + for (const optionProp of Object.keys(optionProps).filter(opt => !['negativeAlias', 'implies'].includes(opt))) { const optionValue = (optionProps as any)[optionProp]; if (optionValue instanceof Expression) { optionArgs[optionProp] = optionValue; @@ -164,6 +165,15 @@ function makeOptions(prefix: Expression, options: { [optionName: string]: CliOpt })); optionsExpr = optionsExpr.callMethod('middleware', middleware, lit(true)); } + + // Special case for `implies`: a flag that switches on other boolean options. + // We add a middleware that sets the implied options to `true` when the flag is set: + // .middleware(yargsImplies('debug', ['debugApp', 'debugCli']), true) + if (theOption.implies && theOption.implies.length > 0) { + const impliedOptions = theOption.implies.map((implied) => kebabToCamelCase(implied)); + const middleware = helpers.yargsImplies.call(lit(kebabToCamelCase(option)), lit(impliedOptions)); + optionsExpr = optionsExpr.callMethod('middleware', middleware, lit(true)); + } } return optionsExpr; diff --git a/packages/@aws-cdk/user-input-gen/lib/yargs-types.ts b/packages/@aws-cdk/user-input-gen/lib/yargs-types.ts index a19fff94e..f9d171530 100644 --- a/packages/@aws-cdk/user-input-gen/lib/yargs-types.ts +++ b/packages/@aws-cdk/user-input-gen/lib/yargs-types.ts @@ -31,6 +31,15 @@ export interface YargsOption { export interface CliOption extends Omit { negativeAlias?: string; + + /** + * Other (boolean) options that are switched on to `true` when this option is set. + * + * Implemented as a yargs middleware that runs before validation. Use kebab-case + * option names (e.g. `['debug-app', 'debug-cli']`); they are converted to the + * camelCase argv keys for you. + */ + implies?: string[]; } export interface Middleware { diff --git a/packages/aws-cdk/lib/cli/cli-config.ts b/packages/aws-cdk/lib/cli/cli-config.ts index 5d9ee3e8a..581778a4b 100644 --- a/packages/aws-cdk/lib/cli/cli-config.ts +++ b/packages/aws-cdk/lib/cli/cli-config.ts @@ -29,7 +29,9 @@ export async function makeConfig(): Promise { 'ignore-errors': { type: 'boolean', default: false, desc: 'Ignores synthesis errors, which will likely produce an invalid output' }, 'json': { type: 'boolean', alias: 'j', desc: 'Use JSON output instead of YAML when templates are printed to STDOUT', default: false }, 'verbose': { type: 'boolean', alias: 'v', desc: 'Show debug logs (specify multiple times to increase verbosity)', default: false, count: true }, - 'debug': { type: 'boolean', desc: 'Debug the CDK app. Log additional information during synthesis, such as creation stack traces of tokens (sets CDK_DEBUG, will slow down synthesis)', default: false }, + 'debug': { type: 'boolean', default: false, implies: ['debug-app', 'debug-cli'], desc: 'Produce more detailed output to help diagnose unexpected behavior for the CDK app and CDK CLI. Note that this will significantly slow down synthesis time.' }, + 'debug-app': { type: 'boolean', default: false, desc: 'Debug the CDK app. Logs additional information during synthesis, such as creation stack traces and sets the CDK_DEBUG environment variable. Will slow down synthesis.' }, + 'debug-cli': { type: 'boolean', default: false, desc: 'Debug the CDK CLI itself.' }, 'profile': { type: 'string', desc: 'Use the indicated AWS profile as the default environment', requiresArg: true }, 'region': { type: 'string', desc: 'Use the indicated AWS region as the default region', requiresArg: true }, 'proxy': { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified', requiresArg: true }, diff --git a/packages/aws-cdk/lib/cli/cli-type-registry.json b/packages/aws-cdk/lib/cli/cli-type-registry.json index 86219b947..a348d1b88 100644 --- a/packages/aws-cdk/lib/cli/cli-type-registry.json +++ b/packages/aws-cdk/lib/cli/cli-type-registry.json @@ -53,8 +53,22 @@ }, "debug": { "type": "boolean", - "desc": "Debug the CDK app. Log additional information during synthesis, such as creation stack traces of tokens (sets CDK_DEBUG, will slow down synthesis)", - "default": false + "default": false, + "implies": [ + "debug-app", + "debug-cli" + ], + "desc": "Produce more detailed output to help diagnose unexpected behavior for the CDK app and CDK CLI. Note that this will significantly slow down synthesis time." + }, + "debug-app": { + "type": "boolean", + "default": false, + "desc": "Debug the CDK app. Logs additional information during synthesis, such as creation stack traces and sets the CDK_DEBUG environment variable. Will slow down synthesis." + }, + "debug-cli": { + "type": "boolean", + "default": false, + "desc": "Debug the CDK CLI itself." }, "profile": { "type": "string", diff --git a/packages/aws-cdk/lib/cli/cli.ts b/packages/aws-cdk/lib/cli/cli.ts index e776f570c..4db7aa019 100644 --- a/packages/aws-cdk/lib/cli/cli.ts +++ b/packages/aws-cdk/lib/cli/cli.ts @@ -6,7 +6,7 @@ import * as chalk from 'chalk'; import { guessLanguage } from '../util'; import { CdkToolkit, AssetBuildTime } from './cdk-toolkit'; import { ciSystemIsStdErrSafe } from './ci-systems'; -import { displayVersionMessage } from './display-version'; +import { displayVersionMessage, shouldDisplayVersionMessage } from './display-version'; import type { IoMessageLevel } from './io-host'; import { CliIoHost } from './io-host'; import { parseCommandLineArguments } from './parse-command-line-arguments'; @@ -88,12 +88,8 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise 2); + // CLI debugging (and the highest verbosity, `-vvv`) turns on verbose AWS SDK tracing. + setSdkTracing(Boolean(argv.debugCli) || argv.verbose > 2); try { await checkForPlatformWarnings(ioHelper); @@ -246,7 +242,9 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise { - if (!process.stdout.isTTY || process.env.CDK_DISABLE_VERSION_CHECK) { - return; - } - try { const versionMessages = await getVersionMessages(currentVersion, versionCheckCache ?? new VersionCheckTTL()); for (const e of formatAsBanner(versionMessages)) { diff --git a/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts b/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts index 4ea2086ad..74c1642e8 100644 --- a/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts @@ -73,7 +73,18 @@ export function parseCommandLineArguments(args: Array): any { .option('debug', { default: false, type: 'boolean', - desc: 'Debug the CDK app. Log additional information during synthesis, such as creation stack traces of tokens (sets CDK_DEBUG, will slow down synthesis)', + desc: 'Produce more detailed output to help diagnose unexpected behavior for the CDK app and CDK CLI. Note that this will significantly slow down synthesis time.', + }) + .middleware(helpers.yargsImplies('debug', ['debugApp', 'debugCli']), true) + .option('debug-app', { + default: false, + type: 'boolean', + desc: 'Debug the CDK app. Logs additional information during synthesis, such as creation stack traces and sets the CDK_DEBUG environment variable. Will slow down synthesis.', + }) + .option('debug-cli', { + default: false, + type: 'boolean', + desc: 'Debug the CDK CLI itself.', }) .option('profile', { default: undefined, diff --git a/packages/aws-cdk/lib/cli/user-configuration.ts b/packages/aws-cdk/lib/cli/user-configuration.ts index 61438a348..6092535b3 100644 --- a/packages/aws-cdk/lib/cli/user-configuration.ts +++ b/packages/aws-cdk/lib/cli/user-configuration.ts @@ -176,11 +176,19 @@ export class Configuration { this.context = new Context(...contextSources); // Build settings from what's left - this.settings = this.defaultConfig + const mergedSettings = this.defaultConfig .merge(userConfig) .merge(this.projectConfig) - .merge(this.commandLineArguments) - .makeReadOnly(); + .merge(this.commandLineArguments); + + // Backwards compatibility: a `debug: true` in a config file (cdk.json / ~/.cdk.json) + // implies both debug targets, mirroring the `--debug` CLI flag. + if (mergedSettings.get(['debug'])) { + mergedSettings.set(['debugApp'], true); + mergedSettings.set(['debugCli'], true); + } + + this.settings = mergedSettings.makeReadOnly(); await this.ioHelper.defaults.debug('merged settings:', this.settings.all); @@ -304,7 +312,11 @@ export async function commandLineArgumentsToSettings(ioHelper: IoHelper, argv: A build: argv.build, caBundlePath: argv.caBundlePath, context, - debug: argv.debug, + // The `--debug` implication (both debug targets) is resolved during argument + // parsing, so here we only read the resolved per-target flags. + debugApp: argv.debugApp, + debugCli: argv.debugCli, + verbose: argv.verbose, tags, language: argv.language, pathMetadata: argv.pathMetadata, diff --git a/packages/aws-cdk/lib/cli/user-input.ts b/packages/aws-cdk/lib/cli/user-input.ts index 68d850821..6afd6ce4f 100644 --- a/packages/aws-cdk/lib/cli/user-input.ts +++ b/packages/aws-cdk/lib/cli/user-input.ts @@ -237,12 +237,26 @@ export interface GlobalOptions { readonly verbose?: number; /** - * Debug the CDK app. Log additional information during synthesis, such as creation stack traces of tokens (sets CDK_DEBUG, will slow down synthesis) + * Produce more detailed output to help diagnose unexpected behavior for the CDK app and CDK CLI. Note that this will significantly slow down synthesis time. * * @default - false */ readonly debug?: boolean; + /** + * Debug the CDK app. Logs additional information during synthesis, such as creation stack traces and sets the CDK_DEBUG environment variable. Will slow down synthesis. + * + * @default - false + */ + readonly debugApp?: boolean; + + /** + * Debug the CDK CLI itself. + * + * @default - false + */ + readonly debugCli?: boolean; + /** * Use the indicated AWS profile as the default environment * diff --git a/packages/aws-cdk/lib/cli/util/yargs-helpers.ts b/packages/aws-cdk/lib/cli/util/yargs-helpers.ts index 7cfb548a4..f7bae7408 100644 --- a/packages/aws-cdk/lib/cli/util/yargs-helpers.ts +++ b/packages/aws-cdk/lib/cli/util/yargs-helpers.ts @@ -24,6 +24,25 @@ export function yargsNegativeAlias) => Record { + return (argv: Record) => { + if (argv[flag]) { + for (const implied of impliedOptions) { + argv[implied] = true; + } + } + return argv; + }; +} + /** * Returns the current version of the CLI * @returns the current version of the CLI diff --git a/packages/aws-cdk/lib/commands/context.ts b/packages/aws-cdk/lib/commands/context.ts index fef328a8d..734b2f5f8 100644 --- a/packages/aws-cdk/lib/commands/context.ts +++ b/packages/aws-cdk/lib/commands/context.ts @@ -4,7 +4,6 @@ import * as chalk from 'chalk'; import picomatch = require('picomatch'); import type { Context } from '../api/context'; import type { IoHelper } from '../api-private'; -import { displayVersionMessage } from '../cli/display-version'; import { renderTable } from '../cli/tables'; import { PROJECT_CONFIG, PROJECT_CONTEXT, USER_DEFAULTS } from '../cli/user-configuration'; @@ -72,7 +71,6 @@ export async function contextHandler(options: ContextOptions): Promise { await listContext(ioHelper, options.context); } } - await displayVersionMessage(ioHelper); return 0; } diff --git a/packages/aws-cdk/lib/commands/doctor.ts b/packages/aws-cdk/lib/commands/doctor.ts index 34939e8fe..c9215acee 100644 --- a/packages/aws-cdk/lib/commands/doctor.ts +++ b/packages/aws-cdk/lib/commands/doctor.ts @@ -1,14 +1,27 @@ import * as process from 'process'; import * as cxapi from '@aws-cdk/cx-api'; import * as chalk from 'chalk'; +import type { Settings } from '../api/settings'; import type { IoHelper } from '../api-private'; import { displayVersionMessage } from '../cli/display-version'; import { versionWithBuild } from '../cli/version'; -export async function doctor({ ioHelper }: { ioHelper: IoHelper }): Promise { +export interface DoctorOptions { + readonly ioHelper: IoHelper; + + /** + * The resolved CLI settings, used to report the active CLI configuration + * (debug targets, verbosity, ...). + * + * @default - configuration is not reported + */ + readonly settings?: Settings; +} + +export async function doctor({ ioHelper, settings }: DoctorOptions): Promise { let exitStatus: number = 0; for (const verification of verifications) { - if (!await verification(ioHelper)) { + if (!await verification(ioHelper, settings)) { exitStatus = -1; } } @@ -16,16 +29,36 @@ export async function doctor({ ioHelper }: { ioHelper: IoHelper }): Promise boolean | Promise> = [ +const verifications: Array<(ioHelper: IoHelper, settings?: Settings) => boolean | Promise> = [ displayVersionInformation, + displayCliConfiguration, displayAwsEnvironmentVariables, displayCdkEnvironmentVariables, ]; // ### Verifications ### +async function displayCliConfiguration(ioHelper: IoHelper, settings?: Settings) { + const verbosity = Number(settings?.get(['verbose']) ?? 0); + const verbose = verbosity ? `extra verbosity (-${'v'.repeat(verbosity)})` : 'normal verbosity'; + + const debug = []; + if (Boolean(settings?.get(['debugApp']))) { + debug.push('CDK app'); + } + if (Boolean(settings?.get(['debugCli']))) { + debug.push('CLI'); + } + const debugging = debug.length ? `debugging ${debug.join(' & ')}` : 'no debugging'; + + await ioHelper.defaults.info(chalk.gray.italic( + `${verbose}, ${debugging}`, + )); + return true; +} + async function displayVersionInformation(ioHelper: IoHelper) { - await ioHelper.defaults.info(`ℹ️ CDK Version: ${chalk.green(versionWithBuild())}`); + await ioHelper.defaults.info(`ℹ️ CDK CLI Version: ${chalk.green(versionWithBuild())}`); return true; } diff --git a/packages/aws-cdk/test/cli/cli-arguments.test.ts b/packages/aws-cdk/test/cli/cli-arguments.test.ts index 18a56ba1b..a126a83ec 100644 --- a/packages/aws-cdk/test/cli/cli-arguments.test.ts +++ b/packages/aws-cdk/test/cli/cli-arguments.test.ts @@ -28,6 +28,8 @@ describe('yargs', () => { versionReporting: undefined, ci: true, debug: false, + debugApp: false, + debugCli: false, ec2creds: undefined, json: false, lookups: true, diff --git a/packages/aws-cdk/test/cli/cli-commands.test.ts b/packages/aws-cdk/test/cli/cli-commands.test.ts index b6ebbb3a7..1eb47ca0b 100644 --- a/packages/aws-cdk/test/cli/cli-commands.test.ts +++ b/packages/aws-cdk/test/cli/cli-commands.test.ts @@ -20,7 +20,7 @@ jest.mock('../../lib/api/notices', () => ({ describe('doctor', () => { test('prints CDK version', async () => { await exec(['doctor']); - expect(notifySpy).toHaveBeenCalledWith(expect.objectContaining({ level: 'info', message: expect.stringContaining('CDK Version:') })); + expect(notifySpy).toHaveBeenCalledWith(expect.objectContaining({ level: 'info', message: expect.stringContaining('CDK CLI Version:') })); }); }); diff --git a/packages/aws-cdk/test/cli/configuration.test.ts b/packages/aws-cdk/test/cli/configuration.test.ts index 7ccb0db31..7f65c9eff 100644 --- a/packages/aws-cdk/test/cli/configuration.test.ts +++ b/packages/aws-cdk/test/cli/configuration.test.ts @@ -1,4 +1,5 @@ import type { Tag } from '../../lib/api/tags'; +import { parseCommandLineArguments } from '../../lib/cli/parse-command-line-arguments'; import { Command, commandLineArgumentsToSettings } from '../../lib/cli/user-configuration'; import { TestIoHost } from '../_helpers/io-host'; @@ -24,6 +25,39 @@ test('can parse string context from command line arguments with equals sign in v expect(settings2.get(['context']).foo).toEqual( 'bar='); }); +describe('the debug flags map to separate debugApp/debugCli settings', () => { + // The `debugApp` setting gates the CDK_DEBUG environment variable. + // `--debug` implies both targets (resolved during argument parsing); + // `--debug-app` / `--debug-cli` are independent. + test('--debug enables both debugApp and debugCli', async () => { + const argv = await parseCommandLineArguments(['deploy', '--debug']); + const settings = await commandLineArgumentsToSettings(ioHelper, argv); + expect(settings.get(['debugApp'])).toBe(true); + expect(settings.get(['debugCli'])).toBe(true); + }); + + test('--debug-app enables only debugApp', async () => { + const argv = await parseCommandLineArguments(['deploy', '--debug-app']); + const settings = await commandLineArgumentsToSettings(ioHelper, argv); + expect(settings.get(['debugApp'])).toBe(true); + expect(settings.get(['debugCli'])).toBe(false); + }); + + test('--debug-cli enables only debugCli', async () => { + const argv = await parseCommandLineArguments(['deploy', '--debug-cli']); + const settings = await commandLineArgumentsToSettings(ioHelper, argv); + expect(settings.get(['debugCli'])).toBe(true); + expect(settings.get(['debugApp'])).toBe(false); + }); + + test('no debug flags leave both settings false', async () => { + const argv = await parseCommandLineArguments(['deploy']); + const settings = await commandLineArgumentsToSettings(ioHelper, argv); + expect(settings.get(['debugApp'])).toBe(false); + expect(settings.get(['debugCli'])).toBe(false); + }); +}); + test('can parse tag values from command line arguments', async () => { // GIVEN const settings1 = await commandLineArgumentsToSettings(ioHelper, { tags: ['foo=bar'], _: [Command.DEPLOY] }); diff --git a/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts b/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts index 467584405..5181d7a4d 100644 --- a/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts +++ b/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts @@ -5,6 +5,40 @@ test('cdk deploy -R sets rollback to false', async () => { expect(argv.rollback).toBe(false); }); +test('cdk deploy --debug implies both --debug-app and --debug-cli', async () => { + const argv = await parseCommandLineArguments(['deploy', '--debug']); + expect(argv.debug).toBe(true); + expect(argv.debugApp).toBe(true); + expect(argv.debugCli).toBe(true); +}); + +test('cdk deploy --debug-app sets only the app debug flag', async () => { + const argv = await parseCommandLineArguments(['deploy', '--debug-app']); + expect(argv.debugApp).toBe(true); + expect(argv.debug).toBe(false); + expect(argv.debugCli).toBe(false); +}); + +test('cdk deploy --debug-cli sets only the cli debug flag', async () => { + const argv = await parseCommandLineArguments(['deploy', '--debug-cli']); + expect(argv.debugCli).toBe(true); + expect(argv.debug).toBe(false); + expect(argv.debugApp).toBe(false); +}); + +test('cdk deploy --debug does not consume the following stack argument', async () => { + const argv = await parseCommandLineArguments(['deploy', '--debug', 'MyStack']); + expect(argv.debug).toBe(true); + expect(argv.STACKS).toEqual(['MyStack']); +}); + +test('cdk deploy without debug flags leaves them false', async () => { + const argv = await parseCommandLineArguments(['deploy']); + expect(argv.debug).toBe(false); + expect(argv.debugApp).toBe(false); + expect(argv.debugCli).toBe(false); +}); + describe('cdk docs', () => { const originalPlatform = process.platform; // Helper to mock process.platform diff --git a/packages/aws-cdk/test/cli/user-config.test.ts b/packages/aws-cdk/test/cli/user-config.test.ts index f9af665a4..9f134054d 100644 --- a/packages/aws-cdk/test/cli/user-config.test.ts +++ b/packages/aws-cdk/test/cli/user-config.test.ts @@ -176,3 +176,26 @@ test('array settings are not overridden by yarg defaults', async () => { expect(configWithPlugin.settings.get(['plugin'])).toEqual(['[]']); expect(configWithoutPlugin.settings.get(['plugin'])).toEqual(['dummy']); }); + +test('a `debug: true` config file setting implies both debug targets', async () => { + // GIVEN + const GIVEN_CONFIG: Map = new Map([ + [PROJECT_CONFIG, { + debug: true, + }], + ]); + + // WHEN + mockedFs.pathExists.mockImplementation(path => { + return GIVEN_CONFIG.has(path); + }); + mockedFs.readJSON.mockImplementation(((path: string) => { + return GIVEN_CONFIG.get(path); + }) as any); + + const config = await Configuration.fromArgsAndFiles(ioHelper); + + // THEN + expect(config.settings.get(['debugApp'])).toBe(true); + expect(config.settings.get(['debugCli'])).toBe(true); +}); diff --git a/packages/aws-cdk/test/cli/version.test.ts b/packages/aws-cdk/test/cli/version.test.ts index 2057de1d8..c7ae1f6d5 100644 --- a/packages/aws-cdk/test/cli/version.test.ts +++ b/packages/aws-cdk/test/cli/version.test.ts @@ -7,8 +7,7 @@ import { setTimeout as _setTimeout } from 'timers'; import { promisify } from 'util'; import * as npm from '../../lib/cli/util/npm'; import { isDeveloperBuildVersion } from '../../lib/cli/version'; -import { TestIoHost } from '../_helpers/io-host'; -import { displayVersionMessage, getVersionMessages, VersionCheckTTL } from '../../lib/cli/display-version'; +import { getVersionMessages, shouldDisplayVersionMessage, VersionCheckTTL } from '../../lib/cli/display-version'; jest.setTimeout(10_000); @@ -18,9 +17,6 @@ function tmpfile(): string { return `/tmp/version-${Math.floor(Math.random() * 10000)}`; } -const ioHost = new TestIoHost(); -const ioHelper = ioHost.asHelper(); - beforeEach(() => { process.chdir(os.tmpdir()); // Need a chdir because in the workspace 'npm view' will take a long time }); @@ -74,8 +70,7 @@ test('No Version specified for storage in the TTL file', async () => { test('Skip version check if environment variable is set', async () => { sinon.stub(process, 'stdout').value({ ...process.stdout, isTTY: true }); sinon.stub(process, 'env').value({ ...process.env, CDK_DISABLE_VERSION_CHECK: '1' }); - await displayVersionMessage(ioHelper); - expect(ioHost.notifySpy).not.toHaveBeenCalled(); + expect(shouldDisplayVersionMessage()).toBe(false); }); describe('version message', () => { diff --git a/packages/aws-cdk/test/commands/cdk-doctor.test.ts b/packages/aws-cdk/test/commands/cdk-doctor.test.ts index d7b505e4f..d5f3c3d03 100644 --- a/packages/aws-cdk/test/commands/cdk-doctor.test.ts +++ b/packages/aws-cdk/test/commands/cdk-doctor.test.ts @@ -1,3 +1,4 @@ +import { Settings } from '../../lib/api/settings'; import { doctor } from '../../lib/commands/doctor'; import { TestIoHost } from '../_helpers/io-host'; @@ -5,8 +6,41 @@ const ioHost = new TestIoHost(); const ioHelper = ioHost.asHelper('doctor'); describe('`cdk doctor`', () => { + beforeEach(() => { + ioHost.notifySpy.mockClear(); + }); + test('exits with 0 when everything is OK', async () => { const result = await doctor({ ioHelper }); expect(result).toBe(0); }); + + test('prints the CLI configuration as a single line from settings', async () => { + const settings = new Settings({ debugApp: true, debugCli: false, verbose: 2 }); + await doctor({ ioHelper, settings }); + + const messages = ioHost.notifySpy.mock.calls.map((c) => c[0].message); + const configLineIndex = messages.findIndex((m) => m.includes('CDK CLI Version')) + 1; + const configLine = messages[configLineIndex]; + + expect(configLine).toBeDefined(); + expect(configLine).toContain('extra verbosity'); + expect(configLine).toContain('-vv'); + expect(configLine).toContain('debugging CDK app'); + expect(configLine).not.toContain('CLI'); + }); + + test('reports everything disabled when no settings are provided', async () => { + await doctor({ ioHelper }); + + const messages = ioHost.notifySpy.mock.calls.map((c) => c[0].message); + const configLineIndex = messages.findIndex((m) => m.includes('CDK CLI Version')) + 1; + const configLine = messages[configLineIndex]; + + expect(configLine).toBeDefined(); + expect(configLine).toContain('normal verbosity'); + expect(configLine).toContain('no debugging'); + expect(configLine).not.toContain('CLI'); + expect(configLine).not.toContain('CDK apps'); + }); });