-
Notifications
You must be signed in to change notification settings - Fork 108
feat(toolkit-lib): simplify deploy approval message and surface auto-confirm #1585
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
base: main
Are you sure you want to change the base?
Changes from 8 commits
90ff4a1
41c4504
5e9031f
a1f1697
0eea727
67cd2b0
176ee61
01637af
a98a20c
5839bb4
6ac4963
e2f9454
8f46813
052988a
a9204b2
a41d958
cad6741
6d8e25e
3ceeb11
c34ca43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -398,6 +398,44 @@ export class CliIoHost implements IIoHost { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Augment toolkit-lib's flag-free I5060 motivation with the | ||
| * `--require-approval` framing so CLI users see flag context. | ||
| */ | ||
| private augmentDeployApprovalMessage<DataType, ResponseType>(msg: IoRequest<DataType, ResponseType>): IoRequest<DataType, ResponseType> { | ||
| const approvalToolkitCodes = ['CDK_TOOLKIT_I5060']; | ||
| if (!(msg.code && approvalToolkitCodes.includes(msg.code))) { | ||
| return msg; | ||
| } | ||
|
|
||
| const data = (msg.data ?? {}) as { motivation?: string; permissionChangeType?: string }; | ||
| const baseMotivation = data.motivation; | ||
| if (!baseMotivation) { | ||
| return msg; | ||
| } | ||
|
|
||
| let prefix: string; | ||
| switch (this.requireDeployApproval) { | ||
| case RequireApproval.ANYCHANGE: | ||
| prefix = `"--require-approval" is set to '${RequireApproval.ANYCHANGE}'. `; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because you want to reuse the original message, and both need to make sense in their own context, the final sentence here will be very staccato: Instead, you can include You might have noticed already, but I care quite a lot how CDK "feels". Not just: what the CLI says is technically accurate, but also that it speaks in a normal voice to a normal user. No formal IBM-speak, no vague Microsoft-speak.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Added
On the voice, this was what I thought as being both natural and not too informal. Any thoughts/ suggestions? |
||
| break; | ||
| case RequireApproval.BROADENING: | ||
| if (data.permissionChangeType !== 'broadening') { | ||
| return msg; | ||
| } | ||
| prefix = '"--require-approval" is enabled. '; | ||
| break; | ||
| default: | ||
| return msg; | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. I feel this much harder to read than the old system. I had to read three times to see the differences here. Also this if is going to be annoying if we are adding more states. I suggest you keep this as a templating string: const updateTypeText = hasSecurityChanges ? "security-sensitive updates" : "updates";
const requireApproval = this.requireDeployApproval; // maybe? not sure.
return `Stack includes ${updateTypeText} and "--require-approval" is set to '${requireApproval}'.\nDo you wish to deploy these changes?`;
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. switched to the suggested template string form. it's now in the rewrite listener in |
||
|
|
||
| const augmentedMotivation = prefix + baseMotivation; | ||
| return { | ||
| ...msg, | ||
| message: msg.message.replace(baseMotivation, augmentedMotivation), | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Determines the output stream, based on message and configuration. | ||
| */ | ||
|
|
@@ -464,13 +502,18 @@ export class CliIoHost implements IIoHost { | |
| return true; | ||
| } | ||
|
|
||
| // The library emits I5060 with a flag-free motivation. The CLI is the | ||
| // layer that knows about `--require-approval` and adds it to the | ||
| // user-facing message before any downstream rendering. | ||
| const promptMessage = this.augmentDeployApprovalMessage(msg).message; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for this function to return a full |
||
|
|
||
| // In --yes mode, respond for the user if we can | ||
| if (this.autoRespond) { | ||
| // respond with yes to all confirmations | ||
| if (isConfirmationPrompt(msg)) { | ||
| await this.notify({ | ||
| ...msg, | ||
| message: `${chalk.cyan(msg.message)} (auto-confirmed)`, | ||
| message: `${chalk.cyan(promptMessage)} (auto-confirmed)`, | ||
| }); | ||
| return true; | ||
| } | ||
|
|
@@ -479,7 +522,7 @@ export class CliIoHost implements IIoHost { | |
| if (msg.defaultResponse) { | ||
| await this.notify({ | ||
| ...msg, | ||
| message: `${chalk.cyan(msg.message)} (auto-responded with default: ${util.format(msg.defaultResponse)})`, | ||
| message: `${chalk.cyan(promptMessage)} (auto-responded with default: ${util.format(msg.defaultResponse)})`, | ||
| }); | ||
| return msg.defaultResponse; | ||
| } | ||
|
|
@@ -498,7 +541,7 @@ export class CliIoHost implements IIoHost { | |
| // Basic confirmation prompt | ||
| // We treat all requests with a boolean response as confirmation prompts | ||
| if (isConfirmationPrompt(msg)) { | ||
| const confirmed = await promptly.confirm(`${chalk.cyan(msg.message)} (y/n)`); | ||
| const confirmed = await promptly.confirm(`${chalk.cyan(promptMessage)} (y/n)`); | ||
| if (!confirmed) { | ||
| throw new ToolkitError('AbortedByUser', 'Aborted by user'); | ||
| } | ||
|
|
@@ -508,7 +551,7 @@ export class CliIoHost implements IIoHost { | |
| // Asking for a specific value | ||
| const prompt = extractPromptInfo(msg); | ||
| const desc = responseDescription ?? prompt.default; | ||
| const answer = await promptly.prompt(`${chalk.cyan(msg.message)}${desc ? ` (${desc})` : ''}`, { | ||
| const answer = await promptly.prompt(`${chalk.cyan(promptMessage)}${desc ? ` (${desc})` : ''}`, { | ||
| default: prompt.default, | ||
| trim: true, | ||
| }); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there not an existing type definition that models the
datafield here? I feel like we require this.In fact, there should be a
CDK_TOOLKIT_I5060.is(msg)that will enforce a type guard that will automatically makemsg.datahave the right type.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the type already exists
DeployConfirmationRequestinpayloads/deploy.ts. It's been wired to I5060 viamake.confirm<DeployConfirmationRequest>(...)inmessages.tssince before this PR. The untyped cast you commented on is gone and is replaced by the suggested type guard.Added
is()onIoRequestMakerinmessage-maker.ts(it was missing, onlyIoMessageMakerhad it) andhasSecurityChanges: booleanonDeployConfirmationRequest. The augmenter now usesIO.CDK_TOOLKIT_I5060.is(msg)to narrowmsg.dataand readshasSecurityChangesto pick the right phrasing. So the prompt branch is driven by a typed payload field instead of re-deriving frompermissionChangeType.