Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(cli): docs, doctor, context commands do not use configuration directly anymore #32578

Merged
merged 10 commits into from
Dec 20, 2024
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: configuration.settings.get(['clear']),
json: configuration.settings.get(['json']),
force: configuration.settings.get(['force']),
reset: configuration.settings.get(['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) {
kaizencc marked this conversation as resolved.
Show resolved Hide resolved
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