Skip to content

Commit dd641b0

Browse files
feat(cli): --yes option to accept all questions (#826)
Adds a new `--yes` cli option so that when provided, commands can continue without confirmation. Any confirmation prompts will be accepted. Other user input will use a default value if available. The CLI might still prompt for input in situations where there is no default answer. Fixes #732 --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license --------- Co-authored-by: Momo Kornher <kornherm@amazon.co.uk>
1 parent 742ccfc commit dd641b0

File tree

12 files changed

+153
-8
lines changed

12 files changed

+153
-8
lines changed

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/synth/cdk-synth-telemetry-with-errors.integtest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ integTest(
3636
ci: expect.anything(), // changes based on where this is called
3737
validation: true,
3838
quiet: false,
39+
yes: false,
3940
},
4041
config: {
4142
context: {},
@@ -84,6 +85,7 @@ integTest(
8485
ci: expect.anything(), // changes based on where this is called
8586
validation: true,
8687
quiet: false,
88+
yes: false,
8789
},
8890
config: {
8991
context: {},

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/synth/cdk-synth-telemetry.integtest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ integTest(
2828
ci: expect.anything(), // changes based on where this is called
2929
validation: true,
3030
quiet: false,
31+
yes: false,
3132
},
3233
config: {
3334
context: {},
@@ -77,6 +78,7 @@ integTest(
7778
ci: expect.anything(), // changes based on where this is called
7879
validation: true,
7980
quiet: false,
81+
yes: false,
8082
},
8183
config: {
8284
context: {},

packages/aws-cdk/lib/cli/cli-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export async function makeConfig(): Promise<CliConfig> {
4444
'ci': { type: 'boolean', desc: 'Force CI detection. If CI=true then logs will be sent to stdout instead of stderr', default: YARGS_HELPERS.isCI() },
4545
'unstable': { type: 'array', desc: 'Opt in to unstable features. The flag indicates that the scope and API of a feature might still change. Otherwise the feature is generally production ready and fully supported. Can be specified multiple times.', default: [] },
4646
'telemetry-file': { type: 'string', desc: 'Send telemetry data to a local file.', default: undefined },
47+
'yes': { type: 'boolean', alias: 'y', desc: 'Automatically answer interactive prompts with the recommended response. This includes confirming actions.', default: false },
4748
},
4849
commands: {
4950
'list': {

packages/aws-cdk/lib/cli/cli-type-registry.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@
127127
"telemetry-file": {
128128
"type": "string",
129129
"desc": "Send telemetry data to a local file."
130+
},
131+
"yes": {
132+
"type": "boolean",
133+
"alias": "y",
134+
"desc": "Automatically answer interactive prompts with the recommended response. This includes confirming actions.",
135+
"default": false
130136
}
131137
},
132138
"commands": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
7070
isCI: Boolean(argv.ci),
7171
currentAction: cmd,
7272
stackProgress: argv.progress,
73+
autoRespond: argv.yes,
7374
}, true);
7475
const ioHelper = asIoHelper(ioHost, ioHost.currentAction as any);
7576

packages/aws-cdk/lib/cli/convert-to-user-input.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function convertYargsToUserInput(args: any): UserInput {
3535
ci: args.ci,
3636
unstable: args.unstable,
3737
telemetryFile: args.telemetryFile,
38+
yes: args.yes,
3839
};
3940
let commandOptions;
4041
switch (args._[0] as Command) {
@@ -344,6 +345,7 @@ export function convertConfigToUserInput(config: any): UserInput {
344345
ci: config.ci,
345346
unstable: config.unstable,
346347
telemetryFile: config.telemetryFile,
348+
yes: config.yes,
347349
};
348350
const listOptions = {
349351
long: config.list?.long,

packages/aws-cdk/lib/cli/io-host/cli-io-host.ts

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ export interface CliIoHostProps {
8181
* @default StackActivityProgress.BAR
8282
*/
8383
readonly stackProgress?: StackActivityProgress;
84+
85+
/**
86+
* Whether the CLI should attempt to automatically respond to prompts.
87+
*
88+
* When true, operation will usually proceed without interactive confirmation.
89+
* Confirmations are responded to with yes. Other prompts will respond with the default value.
90+
*
91+
* @default false
92+
*/
93+
readonly autoRespond?: boolean;
8494
}
8595

8696
/**
@@ -160,6 +170,8 @@ export class CliIoHost implements IIoHost {
160170
private corkedCounter = 0;
161171
private readonly corkedLoggingBuffer: IoMessage<unknown>[] = [];
162172

173+
private readonly autoRespond: boolean;
174+
163175
public telemetry?: TelemetrySession;
164176

165177
private constructor(props: CliIoHostProps = {}) {
@@ -170,6 +182,7 @@ export class CliIoHost implements IIoHost {
170182
this.requireDeployApproval = props.requireDeployApproval ?? RequireApproval.BROADENING;
171183

172184
this.stackProgress = props.stackProgress ?? StackActivityProgress.BAR;
185+
this.autoRespond = props.autoRespond ?? false;
173186
}
174187

175188
public async startTelemetry(args: any, context: Context, _proxyAgent?: Agent) {
@@ -413,6 +426,35 @@ export class CliIoHost implements IIoHost {
413426
const concurrency = data.concurrency ?? 0;
414427
const responseDescription = data.responseDescription;
415428

429+
// Special approval prompt
430+
// Determine if the message needs approval. If it does, continue (it is a basic confirmation prompt)
431+
// If it does not, return success (true). We only check messages with codes that we are aware
432+
// are requires approval codes.
433+
if (this.skipApprovalStep(msg)) {
434+
return true;
435+
}
436+
437+
// In --yes mode, respond for the user if we can
438+
if (this.autoRespond) {
439+
// respond with yes to all confirmations
440+
if (isConfirmationPrompt(msg)) {
441+
await this.notify({
442+
...msg,
443+
message: `${chalk.cyan(msg.message)} (auto-confirmed)`,
444+
});
445+
return true;
446+
}
447+
448+
// respond with the default for all other messages
449+
if (msg.defaultResponse) {
450+
await this.notify({
451+
...msg,
452+
message: `${chalk.cyan(msg.message)} (auto-responded with default: ${util.format(msg.defaultResponse)})`,
453+
});
454+
return msg.defaultResponse;
455+
}
456+
}
457+
416458
// only talk to user if STDIN is a terminal (otherwise, fail)
417459
if (!this.isTTY) {
418460
throw new ToolkitError(`${motivation}, but terminal (TTY) is not attached so we are unable to get a confirmation from the user`);
@@ -423,14 +465,6 @@ export class CliIoHost implements IIoHost {
423465
throw new ToolkitError(`${motivation}, but concurrency is greater than 1 so we are unable to get a confirmation from the user`);
424466
}
425467

426-
// Special approval prompt
427-
// Determine if the message needs approval. If it does, continue (it is a basic confirmation prompt)
428-
// If it does not, return success (true). We only check messages with codes that we are aware
429-
// are requires approval codes.
430-
if (this.skipApprovalStep(msg)) {
431-
return true;
432-
}
433-
434468
// Basic confirmation prompt
435469
// We treat all requests with a boolean response as confirmation prompts
436470
if (isConfirmationPrompt(msg)) {

packages/aws-cdk/lib/cli/parse-command-line-arguments.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ export function parseCommandLineArguments(args: Array<string>): any {
161161
type: 'string',
162162
desc: 'Send telemetry data to a local file.',
163163
})
164+
.option('yes', {
165+
default: false,
166+
type: 'boolean',
167+
alias: 'y',
168+
desc: 'Automatically answer interactive prompts with the recommended response. This includes confirming actions.',
169+
})
164170
.command(['list [STACKS..]', 'ls [STACKS..]'], 'Lists all stacks in the app', (yargs: Argv) =>
165171
yargs
166172
.option('long', {

packages/aws-cdk/lib/cli/user-input.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ export interface GlobalOptions {
327327
* @default - undefined
328328
*/
329329
readonly telemetryFile?: string;
330+
331+
/**
332+
* Automatically answer interactive prompts with the recommended response. This includes confirming actions.
333+
*
334+
* @default - false
335+
*/
336+
readonly yes?: boolean;
330337
}
331338

332339
/**

packages/aws-cdk/test/cli/cli-arguments.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ describe('yargs', () => {
3535
unstable: [],
3636
notices: undefined,
3737
output: undefined,
38+
yes: false,
3839
},
3940
deploy: {
4041
STACKS: undefined,

0 commit comments

Comments
 (0)