Skip to content
23 changes: 12 additions & 11 deletions packages/aws-cdk/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
import { PluginHost } from '../lib/api/plugin';
import { ToolkitInfo } from '../lib/api/toolkit-info';
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 { context } from '../lib/commands/context';
import { docs } from '../lib/commands/docs';
import { doctor } from '../lib/commands/doctor';
import { getMigrateScanType } from '../lib/commands/migrate';
import { cliInit, printAvailableTemplates } from '../lib/init';
import { data, debug, error, print, setCI, setLogLevel, LogLevel } from '../lib/logging';
Expand All @@ -37,7 +37,6 @@
}

export async function exec(args: string[], synthesizer?: Synthesizer): Promise<number | void> {

const argv = await parseCommandLineArguments(args);

// if one -v, log at a DEBUG level
Expand Down Expand Up @@ -150,9 +149,6 @@
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 {
Expand All @@ -170,7 +166,6 @@
await notices.refresh();
notices.display();
}

}

async function main(command: string, args: any): Promise<number | void> {
Expand Down Expand Up @@ -203,13 +198,19 @@

switch (command) {
case 'context':
return context(commandOptions);
return context({

Check warning on line 201 in packages/aws-cdk/lib/cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/aws-cdk/lib/cli.ts#L201

Added line #L201 was not covered by tests
context: configuration.context,
clear: argv.clear,
json: argv.json,
force: argv.force,
reset: argv.reset,
});

case 'docs':
return docs(commandOptions);
return docs({ browser: configuration.settings.get(['browser']) });

Check warning on line 210 in packages/aws-cdk/lib/cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/aws-cdk/lib/cli.ts#L210

Added line #L210 was not covered by tests

case 'doctor':
return doctor(commandOptions);
return doctor();

Check warning on line 213 in packages/aws-cdk/lib/cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/aws-cdk/lib/cli.ts#L213

Added line #L213 was not covered by tests

case 'ls':
case 'list':
Expand Down
26 changes: 0 additions & 26 deletions packages/aws-cdk/lib/command-api.ts

This file was deleted.

101 changes: 70 additions & 31 deletions packages/aws-cdk/lib/commands/context.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,72 @@
import * as chalk from 'chalk';
import { minimatch } from 'minimatch';
import * as version from '../../lib/version';
import { CommandOptions } from '../command-api';
import { print, error, warning } from '../logging';
import { Context, PROJECT_CONFIG, PROJECT_CONTEXT, USER_DEFAULTS } from '../settings';
import { renderTable } from '../util';

export async function realHandler(options: CommandOptions): Promise<number> {
const { configuration, args } = options;
if (args.clear) {
configuration.context.clear();
await configuration.saveContext();
/**
* Options for the context command
*/
export interface ContextOptions {
/**
* The context object sourced from all context locations
*/
context: Context;

/**
* The context key (or its index) to reset
*
* @default undefined
*/
reset?: string;

/**
* Ignore missing key error
*
* @default false
*/
force?: boolean;

/**
* Clear all context
*
* @default false
*/
clear?: boolean;

/**
* Use JSON output instead of YAML when templates are printed to STDOUT
*
* @default false
*/
json?: boolean;
}

export async function context(options: ContextOptions): Promise<number> {
if (options.clear) {
options.context.clear();
await options.context.save(PROJECT_CONTEXT);

Check warning on line 49 in packages/aws-cdk/lib/commands/context.ts

View check run for this annotation

Codecov / codecov/patch

packages/aws-cdk/lib/commands/context.ts#L48-L49

Added lines #L48 - L49 were not covered by tests
print('All context values cleared.');
} else if (args.reset) {
invalidateContext(configuration.context, args.reset as string, args.force as boolean);
await configuration.saveContext();
} else if (options.reset) {
invalidateContext(options.context, options.reset, options.force ?? false);
await options.context.save(PROJECT_CONTEXT);
} else {
// List -- support '--json' flag
if (args.json) {
const contextValues = configuration.context.all;
if (options.json) {
const contextValues = options.context.all;

Check warning on line 57 in packages/aws-cdk/lib/commands/context.ts

View check run for this annotation

Codecov / codecov/patch

packages/aws-cdk/lib/commands/context.ts#L57

Added line #L57 was not covered by tests
process.stdout.write(JSON.stringify(contextValues, undefined, 2));
} else {
listContext(configuration.context);
listContext(options.context);
}
}
await version.displayVersionMessage();

return 0;
}

function listContext(context: Context) {
const keys = contextKeys(context);
function listContext(contextObj: Context) {
const keys = contextKeys(contextObj);

if (keys.length === 0) {
print('This CDK application does not have any saved context values yet.');
Expand All @@ -45,7 +81,7 @@
// Print config by default
const data: any[] = [[chalk.green('#'), chalk.green('Key'), chalk.green('Value')]];
for (const [i, key] of keys) {
const jsonWithoutNewlines = JSON.stringify(context.all[key], undefined, 2).replace(/\s+/g, ' ');
const jsonWithoutNewlines = JSON.stringify(contextObj.all[key], undefined, 2).replace(/\s+/g, ' ');
data.push([i, key, jsonWithoutNewlines]);
}
print('Context found in %s:', chalk.blue(PROJECT_CONFIG));
Expand All @@ -56,17 +92,17 @@
print(`Run ${chalk.blue('cdk context --reset KEY_OR_NUMBER')} to remove a context key. It will be refreshed on the next CDK synthesis run.`);
}

function invalidateContext(context: Context, key: string, force: boolean) {
function invalidateContext(contextObj: Context, key: string, force: boolean) {
const i = parseInt(key, 10);
if (`${i}` === key) {
// was a number and we fully parsed it.
key = keyByNumber(context, i);
key = keyByNumber(contextObj, i);
}
// Unset!
if (context.has(key)) {
context.unset(key);
if (contextObj.has(key)) {
contextObj.unset(key);
// check if the value was actually unset.
if (!context.has(key)) {
if (!contextObj.has(key)) {
print('Context value %s reset. It will be refreshed on next synthesis', chalk.blue(key));
return;
}
Expand All @@ -79,15 +115,15 @@
}

// check if value is expression matching keys
const matches = keysByExpression(context, key);
const matches = keysByExpression(contextObj, key);

if (matches.length > 0) {

matches.forEach((match) => {
context.unset(match);
contextObj.unset(match);
});

const { unset, readonly } = getUnsetAndReadonly(context, matches);
const { unset, readonly } = getUnsetAndReadonly(contextObj, matches);

// output the reset values
printUnset(unset);
Expand All @@ -105,13 +141,15 @@
throw new Error(`No context value matching key: ${key}`);
}
}

function printUnset(unset: string[]) {
if (unset.length === 0) return;
print('The following matched context values reset. They will be refreshed on next synthesis');
unset.forEach((match) => {
print(' %s', match);
});
}

function printReadonly(readonly: string[]) {
if (readonly.length === 0) return;
warning('The following matched context values could not be reset through the CLI');
Expand All @@ -121,13 +159,14 @@
print('');
print('This usually means they are configured in %s or %s', chalk.blue(PROJECT_CONFIG), chalk.blue(USER_DEFAULTS));
}
function keysByExpression(context: Context, expression: string) {
return context.keys.filter(minimatch.filter(expression));

function keysByExpression(contextObj: Context, expression: string) {
return contextObj.keys.filter(minimatch.filter(expression));
}

function getUnsetAndReadonly(context: Context, matches: string[]) {
function getUnsetAndReadonly(contextObj: Context, matches: string[]) {
return matches.reduce<{ unset: string[]; readonly: string[] }>((acc, match) => {
if (context.has(match)) {
if (contextObj.has(match)) {
acc.readonly.push(match);
} else {
acc.unset.push(match);
Expand All @@ -136,8 +175,8 @@
}, { unset: [], readonly: [] });
}

function keyByNumber(context: Context, n: number) {
for (const [i, key] of contextKeys(context)) {
function keyByNumber(contextObj: Context, n: number) {
for (const [i, key] of contextKeys(contextObj)) {
if (n === i) {
return key;
}
Expand All @@ -148,8 +187,8 @@
/**
* Return enumerated keys in a definitive order
*/
function contextKeys(context: Context): [number, string][] {
const keys = context.keys;
function contextKeys(contextObj: Context): [number, string][] {
const keys = contextObj.keys;
keys.sort();
return enumerate1(keys);
}
Expand Down
15 changes: 12 additions & 3 deletions packages/aws-cdk/lib/commands/docs.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import * as childProcess from 'child_process';
import * as chalk from 'chalk';
import { debug, print, warning } from '../../lib/logging';
import { CommandOptions } from '../command-api';

export const command = 'docs';
export const describe = 'Opens the reference documentation in a browser';
export const aliases = ['doc'];

export async function realHandler(options: CommandOptions): Promise<number> {
/**
* Options for the docs command
*/
export interface DocsOptions {
/**
* The command to use to open the browser
*/
browser: string;
}

export async function docs(options: DocsOptions): Promise<number> {
const url = 'https://docs.aws.amazon.com/cdk/api/v2/';
print(chalk.green(url));
const browserCommand = (options.args.browser as string).replace(/%u/g, url);
const browserCommand = (options.browser).replace(/%u/g, url);
debug(`Opening documentation ${chalk.green(browserCommand)}`);
return new Promise<number>((resolve, _reject) => {
childProcess.exec(browserCommand, (err, stdout, stderr) => {
Expand Down
3 changes: 1 addition & 2 deletions packages/aws-cdk/lib/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import * as cxapi from '@aws-cdk/cx-api';
import * as chalk from 'chalk';
import { print } from '../../lib/logging';
import * as version from '../../lib/version';
import { CommandOptions } from '../command-api';

export async function realHandler(_options: CommandOptions): Promise<number> {
export async function doctor(): Promise<number> {
let exitStatus: number = 0;
for (const verification of verifications) {
if (!await verification()) {
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-cdk/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ export async function makeConfig(): Promise<CliConfig> {
context: {
description: 'Manage cached context values',
options: {
reset: { alias: 'e', desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true },
reset: { alias: 'e', desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true, default: undefined },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: we should probably change the codegen to always add an explicit default if none is provied.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

force: { alias: 'f', desc: 'Ignore missing key error', type: 'boolean', default: false },
clear: { desc: 'Clear all context', type: 'boolean' },
clear: { desc: 'Clear all context', type: 'boolean', default: false },
},
},
docs: {
Expand Down
2 changes: 2 additions & 0 deletions packages/aws-cdk/lib/parse-command-line-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ export function parseCommandLineArguments(args: Array<string>): any {
desc: 'The context key (or its index) to reset',
type: 'string',
requiresArg: true,
default: undefined,
})
.option('force', {
alias: 'f',
Expand All @@ -752,6 +753,7 @@ export function parseCommandLineArguments(args: Array<string>): any {
.option('clear', {
desc: 'Clear all context',
type: 'boolean',
default: false,
})
)
.command(['docs', 'doc'], 'Opens the reference documentation in a browser', (yargs: Argv) =>
Expand Down
Loading
Loading