diff --git a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/io-helper.ts b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/io-helper.ts index 81cad3616..54d67d608 100644 --- a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/io-helper.ts +++ b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/io-helper.ts @@ -13,7 +13,6 @@ export type ActionLessRequest = Omit, 'action'>; * Wraps a client provided IoHost and provides additional features & services to toolkit internal classes. */ export interface IoHelper extends IIoHost { - action: ToolkitAction; notify(msg: ActionLessMessage): Promise; requestResponse(msg: ActionLessRequest): Promise; } @@ -23,7 +22,6 @@ export interface IoHelper extends IIoHost { */ export function asIoHelper(ioHost: IIoHost, action: ToolkitAction): IoHelper { return { - action, notify: async (msg: Omit, 'action'>) => { await ioHost.notify({ ...msg, diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/context-aware-source.ts b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/context-aware-source.ts index f4d7ef377..ddc608203 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/context-aware-source.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/context-aware-source.ts @@ -43,13 +43,13 @@ export class ContextAwareCloudAssembly implements ICloudAssemblySource { private canLookup: boolean; private context: Context; private contextFile: string; - private ioHost: IoHelper; + private ioHelper: IoHelper; constructor(private readonly source: ICloudAssemblySource, private readonly props: ContextAwareCloudAssemblyProps) { this.canLookup = props.lookups ?? true; this.context = props.context; this.contextFile = props.contextFile ?? PROJECT_CONTEXT; // @todo new feature not needed right now - this.ioHost = props.services.ioHost; + this.ioHelper = props.services.ioHelper; } /** @@ -77,14 +77,14 @@ export class ContextAwareCloudAssembly implements ICloudAssemblySource { let tryLookup = true; if (previouslyMissingKeys && equalSets(missingKeysSet, previouslyMissingKeys)) { - await this.ioHost.notify(IO.CDK_ASSEMBLY_I0240.msg('Not making progress trying to resolve environmental context. Giving up.', { missingKeys })); + await this.ioHelper.notify(IO.CDK_ASSEMBLY_I0240.msg('Not making progress trying to resolve environmental context. Giving up.', { missingKeys })); tryLookup = false; } previouslyMissingKeys = missingKeysSet; if (tryLookup) { - await this.ioHost.notify(IO.CDK_ASSEMBLY_I0241.msg('Some context information is missing. Fetching...', { missingKeys })); + await this.ioHelper.notify(IO.CDK_ASSEMBLY_I0241.msg('Some context information is missing. Fetching...', { missingKeys })); await contextproviders.provideContextValues( assembly.manifest.missing, this.context, @@ -92,7 +92,7 @@ export class ContextAwareCloudAssembly implements ICloudAssemblySource { ); // Cache the new context to disk - await this.ioHost.notify(IO.CDK_ASSEMBLY_I0042.msg(`Writing updated context to ${this.contextFile}...`, { + await this.ioHelper.notify(IO.CDK_ASSEMBLY_I0042.msg(`Writing updated context to ${this.contextFile}...`, { contextFile: this.contextFile, context: this.context.all, })); diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/prepare-source.ts b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/prepare-source.ts index b0237c676..593dc5b46 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/prepare-source.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/prepare-source.ts @@ -38,7 +38,7 @@ export function determineOutputDirectory(outdir?: string) { * @param context The context key/value bash. */ export async function prepareDefaultEnvironment(services: ToolkitServices, props: { outdir?: string } = {}): Promise { - const logFn = (msg: string, ...args: any) => services.ioHost.notify(IO.CDK_ASSEMBLY_I0010.msg(format(msg, ...args))); + const logFn = (msg: string, ...args: any) => services.ioHelper.notify(IO.CDK_ASSEMBLY_I0010.msg(format(msg, ...args))); const env = await oldPrepare(services.sdkProvider, logFn); if (props.outdir) { diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/source-builder.ts b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/source-builder.ts index cd38fba1b..b36bbfeb6 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/source-builder.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/source-builder.ts @@ -65,7 +65,7 @@ export abstract class CloudAssemblySourceBuilder { return assembly; } - return assemblyFromDirectory(assembly.directory, services.ioHost, props.loadAssemblyOptions); + return assemblyFromDirectory(assembly.directory, services.ioHelper, props.loadAssemblyOptions); }, }, contextAssemblyProps, @@ -89,8 +89,8 @@ export abstract class CloudAssemblySourceBuilder { { produce: async () => { // @todo build - await services.ioHost.notify(IO.CDK_ASSEMBLY_I0150.msg('--app points to a cloud assembly, so we bypass synth')); - return assemblyFromDirectory(directory, services.ioHost, props.loadAssemblyOptions); + await services.ioHelper.notify(IO.CDK_ASSEMBLY_I0150.msg('--app points to a cloud assembly, so we bypass synth')); + return assemblyFromDirectory(directory, services.ioHelper, props.loadAssemblyOptions); }, }, contextAssemblyProps, @@ -139,17 +139,17 @@ export abstract class CloudAssemblySourceBuilder { eventPublisher: async (type, line) => { switch (type) { case 'data_stdout': - await services.ioHost.notify(IO.CDK_ASSEMBLY_I1001.msg(line)); + await services.ioHelper.notify(IO.CDK_ASSEMBLY_I1001.msg(line)); break; case 'data_stderr': - await services.ioHost.notify(IO.CDK_ASSEMBLY_E1002.msg(line)); + await services.ioHelper.notify(IO.CDK_ASSEMBLY_E1002.msg(line)); break; } }, extraEnv: envWithContext, cwd: props.workingDirectory, }); - return assemblyFromDirectory(outdir, services.ioHost, props.loadAssemblyOptions); + return assemblyFromDirectory(outdir, services.ioHelper, props.loadAssemblyOptions); }); } finally { await lock?.release(); diff --git a/packages/@aws-cdk/toolkit-lib/lib/toolkit/private/index.ts b/packages/@aws-cdk/toolkit-lib/lib/toolkit/private/index.ts index 0439ab2bc..ece7a961b 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/toolkit/private/index.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/toolkit/private/index.ts @@ -9,7 +9,7 @@ import type { IoHelper } from '../../api/shared-private'; */ export interface ToolkitServices { sdkProvider: SdkProvider; - ioHost: IoHelper; + ioHelper: IoHelper; } /** diff --git a/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts b/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts index 77fff94fb..5438922c9 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts @@ -142,7 +142,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab */ protected override async sourceBuilderServices(): Promise { return { - ioHost: asIoHelper(this.ioHost, 'assembly'), + ioHelper: asIoHelper(this.ioHost, 'assembly'), sdkProvider: await this.sdkProvider('assembly'), }; } @@ -151,17 +151,17 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab * Bootstrap Action */ public async bootstrap(environments: BootstrapEnvironments, options: BootstrapOptions): Promise { - const ioHost = asIoHelper(this.ioHost, 'bootstrap'); + const ioHelper = asIoHelper(this.ioHost, 'bootstrap'); const bootstrapEnvironments = await environments.getEnvironments(); const source = options.source ?? BootstrapSource.default(); const parameters = options.parameters; - const bootstrapper = new Bootstrapper(source, { ioHost, action: 'bootstrap' }); + const bootstrapper = new Bootstrapper(source, ioHelper); const sdkProvider = await this.sdkProvider('bootstrap'); const limit = pLimit(20); // eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism await Promise.all(bootstrapEnvironments.map((environment: cxapi.Environment, currentIdx) => limit(async () => { - await ioHost.notify(IO.CDK_TOOLKIT_I9100.msg(`${chalk.bold(environment.name)}: bootstrapping...`, { + await ioHelper.notify(IO.CDK_TOOLKIT_I9100.msg(`${chalk.bold(environment.name)}: bootstrapping...`, { total: bootstrapEnvironments.length, current: currentIdx+1, environment, @@ -184,10 +184,10 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab ? ` ✅ ${environment.name} (no changes)` : ` ✅ ${environment.name}`; - await ioHost.notify(IO.CDK_TOOLKIT_I9900.msg(chalk.green('\n' + message), { environment })); - await bootstrapTimer.endAs(ioHost, 'bootstrap'); + await ioHelper.notify(IO.CDK_TOOLKIT_I9900.msg(chalk.green('\n' + message), { environment })); + await bootstrapTimer.endAs(ioHelper, 'bootstrap'); } catch (e: any) { - await ioHost.notify(IO.CDK_TOOLKIT_E9900.msg(`\n ❌ ${chalk.bold(environment.name)} failed: ${formatErrorMessage(e)}`, { error: e })); + await ioHelper.notify(IO.CDK_TOOLKIT_E9900.msg(`\n ❌ ${chalk.bold(environment.name)} failed: ${formatErrorMessage(e)}`, { error: e })); throw e; } }))); @@ -197,13 +197,13 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab * Synth Action */ public async synth(cx: ICloudAssemblySource, options: SynthOptions = {}): Promise { - const ioHost = asIoHelper(this.ioHost, 'synth'); + const ioHelper = asIoHelper(this.ioHost, 'synth'); const synthTimer = Timer.start(); const assembly = await assemblyFromSource(cx); const stacks = assembly.selectStacksV2(options.stacks ?? ALL_STACKS); const autoValidateStacks = options.validateStacks ? [assembly.selectStacksForValidation()] : []; - await this.validateStacksMetadata(stacks.concat(...autoValidateStacks), ioHost); - await synthTimer.endAs(ioHost, 'synth'); + await this.validateStacksMetadata(stacks.concat(...autoValidateStacks), ioHelper); + await synthTimer.endAs(ioHelper, 'synth'); // if we have a single stack, print it to STDOUT const message = `Successfully synthesized to ${chalk.blue(path.resolve(stacks.assembly.directory))}`; @@ -217,7 +217,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab const firstStack = stacks.firstStack!; const template = firstStack.template; const obscuredTemplate = obscureTemplate(template); - await ioHost.notify(IO.CDK_TOOLKIT_I1901.msg(message, { + await ioHelper.notify(IO.CDK_TOOLKIT_I1901.msg(message, { ...assemblyData, stack: { stackName: firstStack.stackName, @@ -229,8 +229,8 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab })); } else { // not outputting template to stdout, let's explain things to the user a little bit... - await ioHost.notify(IO.CDK_TOOLKIT_I1902.msg(chalk.green(message), assemblyData)); - await ioHost.notify(IO.DEFAULT_TOOLKIT_INFO.msg(`Supply a stack id (${stacks.stackArtifacts.map((s) => chalk.green(s.hierarchicalId)).join(', ')}) to display its template.`)); + await ioHelper.notify(IO.CDK_TOOLKIT_I1902.msg(chalk.green(message), assemblyData)); + await ioHelper.notify(IO.DEFAULT_TOOLKIT_INFO.msg(`Supply a stack id (${stacks.stackArtifacts.map((s) => chalk.green(s.hierarchicalId)).join(', ')}) to display its template.`)); } return new IdentityCloudAssemblySource(assembly.assembly); @@ -242,16 +242,16 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab * List selected stacks and their dependencies */ public async list(cx: ICloudAssemblySource, options: ListOptions = {}): Promise { - const ioHost = asIoHelper(this.ioHost, 'list'); + const ioHelper = asIoHelper(this.ioHost, 'list'); const synthTimer = Timer.start(); const assembly = await assemblyFromSource(cx); const stackCollection = await assembly.selectStacksV2(options.stacks ?? ALL_STACKS); - await synthTimer.endAs(ioHost, 'synth'); + await synthTimer.endAs(ioHelper, 'synth'); const stacks = stackCollection.withDependencies(); const message = stacks.map(s => s.id).join('\n'); - await ioHost.notify(IO.CDK_TOOLKIT_I2901.msg(message, { stacks })); + await ioHelper.notify(IO.CDK_TOOLKIT_I2901.msg(message, { stacks })); return stacks; } @@ -269,19 +269,19 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab * Helper to allow deploy being called as part of the watch action. */ private async _deploy(assembly: StackAssembly, action: 'deploy' | 'watch', options: ExtendedDeployOptions = {}) { - const ioHost = asIoHelper(this.ioHost, action); + const ioHelper = asIoHelper(this.ioHost, action); const synthTimer = Timer.start(); const stackCollection = assembly.selectStacksV2(options.stacks ?? ALL_STACKS); - await this.validateStacksMetadata(stackCollection, ioHost); - const synthDuration = await synthTimer.endAs(ioHost, 'synth'); + await this.validateStacksMetadata(stackCollection, ioHelper); + const synthDuration = await synthTimer.endAs(ioHelper, 'synth'); if (stackCollection.stackCount === 0) { - await ioHost.notify(IO.CDK_TOOLKIT_E5001.msg('This app contains no stacks')); + await ioHelper.notify(IO.CDK_TOOLKIT_E5001.msg('This app contains no stacks')); return; } const deployments = await this.deploymentsForAction('deploy'); - const migrator = new ResourceMigrator({ deployments, ioHost, action }); + const migrator = new ResourceMigrator({ deployments, ioHelper }); await migrator.tryMigrateResources(stackCollection, options); @@ -289,7 +289,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab const hotswapMode = options.hotswap ?? HotswapMode.FULL_DEPLOYMENT; if (hotswapMode !== HotswapMode.FULL_DEPLOYMENT) { - await ioHost.notify(IO.CDK_TOOLKIT_W5400.msg([ + await ioHelper.notify(IO.CDK_TOOLKIT_W5400.msg([ '⚠️ The --hotswap and --hotswap-fallback flags deliberately introduce CloudFormation drift to speed up deployments', '⚠️ They should only be used for development - never use them for your production Stacks!', ].join('\n'))); @@ -324,7 +324,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab const deployStack = async (stackNode: StackNode) => { const stack = stackNode.stack; if (stackCollection.stackCount !== 1) { - await ioHost.notify(IO.DEFAULT_TOOLKIT_INFO.msg(chalk.bold(stack.displayName))); + await ioHelper.notify(IO.DEFAULT_TOOLKIT_INFO.msg(chalk.bold(stack.displayName))); } if (!stack.environment) { @@ -338,11 +338,11 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab // stack is empty and doesn't exist => do nothing const stackExists = await deployments.stackExists({ stack }); if (!stackExists) { - return ioHost.notify(IO.CDK_TOOLKIT_W5021.msg(`${chalk.bold(stack.displayName)}: stack has no resources, skipping deployment.`)); + return ioHelper.notify(IO.CDK_TOOLKIT_W5021.msg(`${chalk.bold(stack.displayName)}: stack has no resources, skipping deployment.`)); } // stack is empty, but exists => delete - await ioHost.notify(IO.CDK_TOOLKIT_W5022.msg(`${chalk.bold(stack.displayName)}: stack has no resources, deleting existing stack.`)); + await ioHelper.notify(IO.CDK_TOOLKIT_W5022.msg(`${chalk.bold(stack.displayName)}: stack has no resources, deleting existing stack.`)); await this._destroy(assembly, 'deploy', { stacks: { patterns: [stack.hierarchicalId], strategy: StackSelectionStrategy.PATTERN_MUST_MATCH_SINGLE }, roleArn: options.roleArn, @@ -356,7 +356,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab const permissionChangeType = determinePermissionType(currentTemplate, stack); const deployMotivation = '"--require-approval" is enabled and stack includes security-sensitive updates.'; const deployQuestion = `${deployMotivation}\nDo you wish to deploy these changes`; - const deployConfirmed = await ioHost.requestResponse(IO.CDK_TOOLKIT_I5060.req(deployQuestion, { + const deployConfirmed = await ioHelper.requestResponse(IO.CDK_TOOLKIT_I5060.req(deployQuestion, { motivation: deployMotivation, concurrency, permissionChangeType: permissionChangeType, @@ -381,7 +381,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab } const stackIndex = stacks.indexOf(stack) + 1; - await ioHost.notify(IO.CDK_TOOLKIT_I5100.msg(`${chalk.bold(stack.displayName)}: deploying... [${stackIndex}/${stackCollection.stackCount}]`, { + await ioHelper.notify(IO.CDK_TOOLKIT_I5100.msg(`${chalk.bold(stack.displayName)}: deploying... [${stackIndex}/${stackCollection.stackCount}]`, { total: stackCollection.stackCount, current: stackIndex, stack, @@ -435,9 +435,9 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab const question = `${motivation}. Perform a regular deployment`; if (options.force) { - await ioHost.notify(IO.DEFAULT_TOOLKIT_WARN.msg(`${motivation}. Rolling back first (--force).`)); + await ioHelper.notify(IO.DEFAULT_TOOLKIT_WARN.msg(`${motivation}. Rolling back first (--force).`)); } else { - const confirmed = await ioHost.requestResponse(IO.CDK_TOOLKIT_I5050.req(question, { + const confirmed = await ioHelper.requestResponse(IO.CDK_TOOLKIT_I5050.req(question, { motivation, concurrency, })); @@ -463,9 +463,9 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab // @todo no force here if (options.force) { - await ioHost.notify(IO.DEFAULT_TOOLKIT_WARN.msg(`${motivation}. Proceeding with regular deployment (--force).`)); + await ioHelper.notify(IO.DEFAULT_TOOLKIT_WARN.msg(`${motivation}. Proceeding with regular deployment (--force).`)); } else { - const confirmed = await ioHost.requestResponse(IO.CDK_TOOLKIT_I5050.req(question, { + const confirmed = await ioHelper.requestResponse(IO.CDK_TOOLKIT_I5050.req(question, { motivation, concurrency, })); @@ -488,8 +488,8 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab ? ` ✅ ${stack.displayName} (no changes)` : ` ✅ ${stack.displayName}`; - await ioHost.notify(IO.CDK_TOOLKIT_I5900.msg(chalk.green('\n' + message), deployResult)); - deployDuration = await deployTimer.endAs(ioHost, 'deploy'); + await ioHelper.notify(IO.CDK_TOOLKIT_I5900.msg(chalk.green('\n' + message), deployResult)); + deployDuration = await deployTimer.endAs(ioHelper, 'deploy'); if (Object.keys(deployResult.outputs).length > 0) { const buffer = ['Outputs:']; @@ -499,9 +499,9 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab const value = deployResult.outputs[name]; buffer.push(`${chalk.cyan(stack.id)}.${chalk.cyan(name)} = ${chalk.underline(chalk.cyan(value))}`); } - await ioHost.notify(IO.CDK_TOOLKIT_I5901.msg(buffer.join('\n'))); + await ioHelper.notify(IO.CDK_TOOLKIT_I5901.msg(buffer.join('\n'))); } - await ioHost.notify(IO.CDK_TOOLKIT_I5901.msg(`Stack ARN:\n${deployResult.stackArn}`)); + await ioHelper.notify(IO.CDK_TOOLKIT_I5901.msg(`Stack ARN:\n${deployResult.stackArn}`)); } catch (e: any) { // It has to be exactly this string because an integration test tests for // "bold(stackname) failed: ResourceNotReady: " @@ -512,13 +512,13 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab if (options.traceLogs) { // deploy calls that originate from watch will come with their own cloudWatchLogMonitor const cloudWatchLogMonitor = options.cloudWatchLogMonitor ?? new CloudWatchLogEventMonitor(); - const foundLogGroupsResult = await findCloudWatchLogGroups(await this.sdkProvider('deploy'), { ioHost, action }, stack); + const foundLogGroupsResult = await findCloudWatchLogGroups(await this.sdkProvider('deploy'), ioHelper, stack); cloudWatchLogMonitor.addLogGroups( foundLogGroupsResult.env, foundLogGroupsResult.sdk, foundLogGroupsResult.logGroupNames, ); - await ioHost.notify(IO.CDK_TOOLKIT_I5031.msg(`The following log groups are added: ${foundLogGroupsResult.logGroupNames}`)); + await ioHelper.notify(IO.CDK_TOOLKIT_I5031.msg(`The following log groups are added: ${foundLogGroupsResult.logGroupNames}`)); } // If an outputs file has been specified, create the file path and write stack outputs to it once. @@ -533,7 +533,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab } } const duration = synthDuration.asMs + (deployDuration?.asMs ?? 0); - await ioHost.notify(IO.CDK_TOOLKIT_I5001.msg(`\n✨ Total time: ${formatTime(duration)}s\n`, { duration })); + await ioHelper.notify(IO.CDK_TOOLKIT_I5001.msg(`\n✨ Total time: ${formatTime(duration)}s\n`, { duration })); }; const assetBuildTime = options.assetBuildTime ?? AssetBuildTime.ALL_BEFORE_DEPLOY; @@ -544,7 +544,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab stack, ...stack.dependencies.filter(x => cxapi.AssetManifestArtifact.isAssetManifestArtifact(x)), ]); - const workGraph = new WorkGraphBuilder({ ioHost, action }, prebuildAssets).build(stacksAndTheirAssetManifests); + const workGraph = new WorkGraphBuilder(ioHelper, prebuildAssets).build(stacksAndTheirAssetManifests); // Unless we are running with '--force', skip already published assets if (!options.force) { @@ -572,7 +572,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab */ public async watch(cx: ICloudAssemblySource, options: WatchOptions): Promise { const assembly = await assemblyFromSource(cx, false); - const ioHost = asIoHelper(this.ioHost, 'watch'); + const ioHelper = asIoHelper(this.ioHost, 'watch'); const rootDir = options.watchDir ?? process.cwd(); if (options.include === undefined && options.exclude === undefined) { @@ -605,7 +605,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab }).concat(`${outdir}/**`, '**/.*', '**/.*/**', '**/node_modules/**'); // Print some debug information on computed settings - await ioHost.notify(IO.CDK_TOOLKIT_I5310.msg([ + await ioHelper.notify(IO.CDK_TOOLKIT_I5310.msg([ `root directory used for 'watch' is: ${rootDir}`, `'include' patterns for 'watch': ${JSON.stringify(watchIncludes)}`, `'exclude' patterns for 'watch': ${JSON.stringify(watchExcludes)}`, @@ -642,7 +642,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab // TypeScript doesn't realize latch can change between 'awaits', // and thinks the above 'while' condition is always 'false' without the cast latch = 'deploying'; - await ioHost.notify(IO.CDK_TOOLKIT_I5315.msg("Detected file changes during deployment. Invoking 'cdk deploy' again")); + await ioHelper.notify(IO.CDK_TOOLKIT_I5315.msg("Detected file changes during deployment. Invoking 'cdk deploy' again")); await this.invokeDeployFromWatch(assembly, options, cloudWatchLogMonitor); } latch = 'open'; @@ -656,8 +656,8 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab }) .on('ready', async () => { latch = 'open'; - await ioHost.notify(IO.DEFAULT_TOOLKIT_DEBUG.msg("'watch' received the 'ready' event. From now on, all file changes will trigger a deployment")); - await ioHost.notify(IO.CDK_TOOLKIT_I5314.msg("Triggering initial 'cdk deploy'")); + await ioHelper.notify(IO.DEFAULT_TOOLKIT_DEBUG.msg("'watch' received the 'ready' event. From now on, all file changes will trigger a deployment")); + await ioHelper.notify(IO.CDK_TOOLKIT_I5314.msg("Triggering initial 'cdk deploy'")); await deployAndWatch(); }) .on('all', async (event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', filePath: string) => { @@ -666,14 +666,14 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab path: filePath, }; if (latch === 'pre-ready') { - await ioHost.notify(IO.CDK_TOOLKIT_I5311.msg(`'watch' is observing ${event === 'addDir' ? 'directory' : 'the file'} '${filePath}' for changes`, watchEvent)); + await ioHelper.notify(IO.CDK_TOOLKIT_I5311.msg(`'watch' is observing ${event === 'addDir' ? 'directory' : 'the file'} '${filePath}' for changes`, watchEvent)); } else if (latch === 'open') { - await ioHost.notify(IO.CDK_TOOLKIT_I5312.msg(`Detected change to '${filePath}' (type: ${event}). Triggering 'cdk deploy'`, watchEvent)); + await ioHelper.notify(IO.CDK_TOOLKIT_I5312.msg(`Detected change to '${filePath}' (type: ${event}). Triggering 'cdk deploy'`, watchEvent)); await deployAndWatch(); } else { // this means latch is either 'deploying' or 'queued' latch = 'queued'; - await ioHost.notify(IO.CDK_TOOLKIT_I5313.msg( + await ioHelper.notify(IO.CDK_TOOLKIT_I5313.msg( `Detected change to '${filePath}' (type: ${event}) while 'cdk deploy' is still running. Will queue for another deployment after this one finishes'`, watchEvent, )); @@ -695,21 +695,21 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab * Helper to allow rollback being called as part of the deploy or watch action. */ private async _rollback(assembly: StackAssembly, action: 'rollback' | 'deploy' | 'watch', options: RollbackOptions): Promise { - const ioHost = asIoHelper(this.ioHost, action); + const ioHelper = asIoHelper(this.ioHost, action); const synthTimer = Timer.start(); const stacks = assembly.selectStacksV2(options.stacks); - await this.validateStacksMetadata(stacks, ioHost); - await synthTimer.endAs(ioHost, 'synth'); + await this.validateStacksMetadata(stacks, ioHelper); + await synthTimer.endAs(ioHelper, 'synth'); if (stacks.stackCount === 0) { - await ioHost.notify(IO.CDK_TOOLKIT_E6001.msg('No stacks selected')); + await ioHelper.notify(IO.CDK_TOOLKIT_E6001.msg('No stacks selected')); return; } let anyRollbackable = false; for (const [index, stack] of stacks.stackArtifacts.entries()) { - await ioHost.notify(IO.CDK_TOOLKIT_I6100.msg(`Rolling back ${chalk.bold(stack.displayName)}`, { + await ioHelper.notify(IO.CDK_TOOLKIT_I6100.msg(`Rolling back ${chalk.bold(stack.displayName)}`, { total: stacks.stackCount, current: index + 1, stack, @@ -728,9 +728,9 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab if (!stackResult.notInRollbackableState) { anyRollbackable = true; } - await rollbackTimer.endAs(ioHost, 'rollback'); + await rollbackTimer.endAs(ioHelper, 'rollback'); } catch (e: any) { - await ioHost.notify(IO.CDK_TOOLKIT_E6900.msg(`\n ❌ ${chalk.bold(stack.displayName)} failed: ${formatErrorMessage(e)}`, { error: e })); + await ioHelper.notify(IO.CDK_TOOLKIT_E6900.msg(`\n ❌ ${chalk.bold(stack.displayName)} failed: ${formatErrorMessage(e)}`, { error: e })); throw new ToolkitError('Rollback failed (use --force to orphan failing resources)'); } } @@ -753,23 +753,23 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab * Helper to allow destroy being called as part of the deploy action. */ private async _destroy(assembly: StackAssembly, action: 'deploy' | 'destroy', options: DestroyOptions): Promise { - const ioHost = asIoHelper(this.ioHost, action); + const ioHelper = asIoHelper(this.ioHost, action); const synthTimer = Timer.start(); // The stacks will have been ordered for deployment, so reverse them for deletion. const stacks = await assembly.selectStacksV2(options.stacks).reversed(); - await synthTimer.endAs(ioHost, 'synth'); + await synthTimer.endAs(ioHelper, 'synth'); const motivation = 'Destroying stacks is an irreversible action'; const question = `Are you sure you want to delete: ${chalk.red(stacks.hierarchicalIds.join(', '))}`; - const confirmed = await ioHost.requestResponse(IO.CDK_TOOLKIT_I7010.req(question, { motivation })); + const confirmed = await ioHelper.requestResponse(IO.CDK_TOOLKIT_I7010.req(question, { motivation })); if (!confirmed) { - return ioHost.notify(IO.CDK_TOOLKIT_E7010.msg('Aborted by user')); + return ioHelper.notify(IO.CDK_TOOLKIT_E7010.msg('Aborted by user')); } const destroyTimer = Timer.start(); try { for (const [index, stack] of stacks.stackArtifacts.entries()) { - await ioHost.notify(IO.CDK_TOOLKIT_I7100.msg(chalk.green(`${chalk.blue(stack.displayName)}: destroying... [${index + 1}/${stacks.stackCount}]`), { + await ioHelper.notify(IO.CDK_TOOLKIT_I7100.msg(chalk.green(`${chalk.blue(stack.displayName)}: destroying... [${index + 1}/${stacks.stackCount}]`), { total: stacks.stackCount, current: index + 1, stack, @@ -781,14 +781,14 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab deployName: stack.stackName, roleArn: options.roleArn, }); - await ioHost.notify(IO.CDK_TOOLKIT_I7900.msg(chalk.green(`\n ✅ ${chalk.blue(stack.displayName)}: ${action}ed`), stack)); + await ioHelper.notify(IO.CDK_TOOLKIT_I7900.msg(chalk.green(`\n ✅ ${chalk.blue(stack.displayName)}: ${action}ed`), stack)); } catch (e: any) { - await ioHost.notify(IO.CDK_TOOLKIT_E7900.msg(`\n ❌ ${chalk.blue(stack.displayName)}: ${action} failed ${e}`, { error: e })); + await ioHelper.notify(IO.CDK_TOOLKIT_E7900.msg(`\n ❌ ${chalk.blue(stack.displayName)}: ${action} failed ${e}`, { error: e })); throw e; } } } finally { - await destroyTimer.endAs(ioHost, 'destroy'); + await destroyTimer.endAs(ioHelper, 'destroy'); } } @@ -816,8 +816,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab return new Deployments({ sdkProvider: await this.sdkProvider(action), toolkitStackName: this.toolkitStackName, - ioHost: this.ioHost as any, // @todo temporary while we have to separate IIoHost interfaces - action, + ioHelper: asIoHelper(this.ioHost, action), }); } diff --git a/packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts b/packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts index 8582c9618..5de89fa85 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts @@ -1,20 +1,19 @@ import { inspect, format } from 'util'; import { Logger } from '@smithy/types'; -import type { IIoHost } from '../../toolkit/cli-io-host'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { replacerBufferWithInfo } from '../../util'; export class SdkToCliLogger implements Logger { - private readonly ioHost: IIoHost; + private readonly ioHelper: IoHelper; - public constructor(ioHost: IIoHost) { - this.ioHost = ioHost; + public constructor(ioHelper: IoHelper) { + this.ioHelper = ioHelper; } private notify(level: 'debug' | 'info' | 'warn' | 'error', ...content: any[]) { - void this.ioHost.notify({ + void this.ioHelper.notify({ time: new Date(), level: 'trace', // always log all SDK logs at trace level, no matter what level they are coming from the SDK - action: 'none' as any, code: 'CDK_SDK_I0000', message: format('[SDK %s] %s', level, formatSdkLoggerContent(content)), }); diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts b/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts index d62071184..e913e48e5 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts @@ -4,8 +4,8 @@ import * as cxapi from '@aws-cdk/cx-api'; import type { BootstrapEnvironmentOptions, BootstrappingParameters } from './bootstrap-props'; import { BootstrapStack, bootstrapVersionFromTemplate } from './deploy-bootstrap'; import { legacyBootstrapTemplate } from './legacy-template'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { warn } from '../../cli/messages'; -import { IoMessaging } from '../../toolkit/cli-io-host'; import { ToolkitError } from '../../toolkit/error'; import { bundledPackageRootDir, loadStructuredFile, serializeStructure } from '../../util'; import type { SDK, SdkProvider } from '../aws-auth'; @@ -16,10 +16,13 @@ import { DEFAULT_TOOLKIT_STACK_NAME } from '../toolkit-info'; export type BootstrapSource = { source: 'legacy' } | { source: 'default' } | { source: 'custom'; templateFile: string }; export class Bootstrapper { + private readonly ioHelper: IoHelper; + constructor( private readonly source: BootstrapSource = { source: 'default' }, - private readonly msg: IoMessaging, + ioHelper: IoHelper, ) { + this.ioHelper = ioHelper; } public bootstrapEnvironment( @@ -67,7 +70,7 @@ export class Bootstrapper { } const toolkitStackName = options.toolkitStackName ?? DEFAULT_TOOLKIT_STACK_NAME; - const current = await BootstrapStack.lookup(sdkProvider, environment, toolkitStackName, this.msg); + const current = await BootstrapStack.lookup(sdkProvider, environment, toolkitStackName, this.ioHelper); return current.update( await this.loadTemplate(params), {}, @@ -92,7 +95,7 @@ export class Bootstrapper { const bootstrapTemplate = await this.loadTemplate(); const toolkitStackName = options.toolkitStackName ?? DEFAULT_TOOLKIT_STACK_NAME; - const current = await BootstrapStack.lookup(sdkProvider, environment, toolkitStackName, this.msg); + const current = await BootstrapStack.lookup(sdkProvider, environment, toolkitStackName, this.ioHelper); const partition = await current.partition(); if (params.createCustomerMasterKey !== undefined && params.kmsKeyId) { @@ -148,8 +151,7 @@ export class Bootstrapper { // Would leave AdministratorAccess policies with a trust relationship, without the user explicitly // approving the trust policy. const implicitPolicy = `arn:${partition}:iam::aws:policy/AdministratorAccess`; - await this.msg.ioHost.notify(warn( - this.msg.action, + await this.ioHelper.notify(warn( `Using default execution policy of '${implicitPolicy}'. Pass '--cloudformation-execution-policies' to customize.`, )); } else if (cloudFormationExecutionPolicies.length === 0) { @@ -197,18 +199,15 @@ export class Bootstrapper { } if (currentPermissionsBoundary !== policyName) { if (!currentPermissionsBoundary) { - await this.msg.ioHost.notify(warn( - this.msg.action, + await this.ioHelper.notify(warn( `Adding new permissions boundary ${policyName}`, )); } else if (!policyName) { - await this.msg.ioHost.notify(warn( - this.msg.action, + await this.ioHelper.notify(warn( `Removing existing permissions boundary ${currentPermissionsBoundary}`, )); } else { - await this.msg.ioHost.notify(warn( - this.msg.action, + await this.ioHelper.notify(warn( `Changing permissions boundary from ${currentPermissionsBoundary} to ${policyName}`, )); } diff --git a/packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts b/packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts index b7044e0b0..0b54689fe 100644 --- a/packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts +++ b/packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts @@ -10,8 +10,8 @@ import { BootstrapEnvironmentOptions, DEFAULT_BOOTSTRAP_VARIANT, } from './bootstrap-props'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { warn } from '../../cli/messages'; -import { IoMessaging } from '../../toolkit/cli-io-host'; import type { SDK, SdkProvider } from '../aws-auth'; import { assertIsSuccessfulDeployStackResult, SuccessfulDeployStackResult } from '../deployments'; import { deployStack } from '../deployments/deploy-stack'; @@ -34,15 +34,15 @@ import { DEFAULT_TOOLKIT_STACK_NAME, ToolkitInfo } from '../toolkit-info'; * current bootstrap stack and doing something intelligent). */ export class BootstrapStack { - public static async lookup(sdkProvider: SdkProvider, environment: Environment, toolkitStackName: string, msg: IoMessaging) { + public static async lookup(sdkProvider: SdkProvider, environment: Environment, toolkitStackName: string, ioHelper: IoHelper) { toolkitStackName = toolkitStackName ?? DEFAULT_TOOLKIT_STACK_NAME; const resolvedEnvironment = await sdkProvider.resolveEnvironment(environment); const sdk = (await sdkProvider.forEnvironment(resolvedEnvironment, Mode.ForWriting)).sdk; - const currentToolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, sdk, msg, toolkitStackName); + const currentToolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, sdk, ioHelper, toolkitStackName); - return new BootstrapStack(sdkProvider, sdk, resolvedEnvironment, toolkitStackName, currentToolkitInfo, msg); + return new BootstrapStack(sdkProvider, sdk, resolvedEnvironment, toolkitStackName, currentToolkitInfo, ioHelper); } protected constructor( @@ -51,7 +51,7 @@ export class BootstrapStack { private readonly resolvedEnvironment: Environment, private readonly toolkitStackName: string, private readonly currentToolkitInfo: ToolkitInfo, - private readonly msg: IoMessaging, + private readonly ioHelper: IoHelper, ) { } @@ -88,8 +88,7 @@ export class BootstrapStack { const currentVariant = this.currentToolkitInfo.variant; const newVariant = bootstrapVariantFromTemplate(template); if (currentVariant !== newVariant) { - await this.msg.ioHost.notify(warn( - this.msg.action, + await this.ioHelper.notify(warn( `Bootstrap stack already exists, containing '${currentVariant}'. Not overwriting it with a template containing '${newVariant}' (use --force if you intend to overwrite)`, )); return abortResponse; @@ -99,15 +98,13 @@ export class BootstrapStack { const newVersion = bootstrapVersionFromTemplate(template); const currentVersion = this.currentToolkitInfo.version; if (newVersion < currentVersion) { - await this.msg.ioHost.notify(warn( - this.msg.action, + await this.ioHelper.notify(warn( `Bootstrap stack already at version ${currentVersion}. Not downgrading it to version ${newVersion} (use --force if you intend to downgrade)`, )); if (newVersion === 0) { // A downgrade with 0 as target version means we probably have a new-style bootstrap in the account, // and an old-style bootstrap as current target, which means the user probably forgot to put this flag in. - await this.msg.ioHost.notify(warn( - this.msg.action, + await this.ioHelper.notify(warn( "(Did you set the '@aws-cdk/core:newStyleStackSynthesis' feature flag in cdk.json?)", )); } @@ -145,8 +142,8 @@ export class BootstrapStack { parameters, usePreviousParameters: options.usePreviousParameters ?? true, // Obviously we can't need a bootstrap stack to deploy a bootstrap stack - envResources: new NoBootstrapStackEnvironmentResources(this.resolvedEnvironment, this.sdk, this.msg), - }, this.msg); + envResources: new NoBootstrapStackEnvironmentResources(this.resolvedEnvironment, this.sdk, this.ioHelper), + }, this.ioHelper); assertIsSuccessfulDeployStackResult(ret); diff --git a/packages/aws-cdk/lib/api/deployments/asset-publishing.ts b/packages/aws-cdk/lib/api/deployments/asset-publishing.ts index 7417459d8..5579d4e14 100644 --- a/packages/aws-cdk/lib/api/deployments/asset-publishing.ts +++ b/packages/aws-cdk/lib/api/deployments/asset-publishing.ts @@ -13,8 +13,9 @@ import { type ISecretsManagerClient, } from 'cdk-assets'; import type { SDK } from '..'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { formatMessage } from '../../cli/messages'; -import { IIoHost, IoMessageLevel, IoMessaging } from '../../toolkit/cli-io-host'; +import type { IoMessageLevel } from '../../toolkit/cli-io-host'; import { ToolkitError } from '../../toolkit/error'; import type { SdkProvider } from '../aws-auth'; import { Mode } from '../plugin'; @@ -43,7 +44,7 @@ export async function publishAssets( sdk: SdkProvider, targetEnv: Environment, options: PublishAssetsOptions, - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, ) { // This shouldn't really happen (it's a programming error), but we don't have // the types here to guide us. Do an runtime validation to be super super sure. @@ -58,7 +59,7 @@ export async function publishAssets( const publisher = new AssetPublishing(manifest, { aws: new PublishingAws(sdk, targetEnv), - progressListener: new PublishingProgressListener({ ioHost, action }), + progressListener: new PublishingProgressListener(ioHelper), throwOnError: false, publishInParallel: options.parallel ?? true, buildAssets: true, @@ -183,12 +184,10 @@ export const EVENT_TO_LEVEL: Record = { }; export abstract class BasePublishProgressListener implements IPublishProgressListener { - protected readonly ioHost: IIoHost; - protected readonly action: IoMessaging['action']; + protected readonly ioHelper: IoHelper; - constructor({ ioHost, action }: IoMessaging) { - this.ioHost = ioHost; - this.action = action; + constructor(ioHelper: IoHelper) { + this.ioHelper = ioHelper; } protected abstract getMessage(type: EventType, event: IPublishProgress): string; @@ -196,10 +195,9 @@ export abstract class BasePublishProgressListener implements IPublishProgressLis public onPublishEvent(type: EventType, event: IPublishProgress): void { const level = EVENT_TO_LEVEL[type]; if (level) { - void this.ioHost.notify( + void this.ioHelper.notify( formatMessage({ level, - action: this.action, message: this.getMessage(type, event), }), ); diff --git a/packages/aws-cdk/lib/api/deployments/assets.ts b/packages/aws-cdk/lib/api/deployments/assets.ts index 866354367..d600e7a47 100644 --- a/packages/aws-cdk/lib/api/deployments/assets.ts +++ b/packages/aws-cdk/lib/api/deployments/assets.ts @@ -4,8 +4,8 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as chalk from 'chalk'; import { AssetManifestBuilder } from './asset-manifest-builder'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { debug } from '../../cli/messages'; -import { IoMessaging } from '../../toolkit/cli-io-host'; import { ToolkitError } from '../../toolkit/error'; import { EnvironmentResources } from '../environment'; import { ToolkitInfo } from '../toolkit-info'; @@ -17,7 +17,7 @@ import { ToolkitInfo } from '../toolkit-info'; * pass Asset coordinates. */ export async function addMetadataAssetsToManifest( - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, stack: cxapi.CloudFormationStackArtifact, assetManifest: AssetManifestBuilder, envResources: EnvironmentResources, @@ -44,16 +44,16 @@ export async function addMetadataAssetsToManifest( const reuseAsset = reuse.indexOf(asset.id) > -1; if (reuseAsset) { - await ioHost.notify(debug(action, `Reusing asset ${asset.id}: ${JSON.stringify(asset)}`)); + await ioHelper.notify(debug(`Reusing asset ${asset.id}: ${JSON.stringify(asset)}`)); continue; } - await ioHost.notify(debug(action, `Preparing asset ${asset.id}: ${JSON.stringify(asset)}`)); + await ioHelper.notify(debug(`Preparing asset ${asset.id}: ${JSON.stringify(asset)}`)); if (!stack.assembly) { throw new ToolkitError('Unexpected: stack assembly is required in order to find assets in assembly directory'); } - Object.assign(params, await prepareAsset({ ioHost, action }, asset, assetManifest, envResources, toolkitInfo)); + Object.assign(params, await prepareAsset(ioHelper, asset, assetManifest, envResources, toolkitInfo)); } return params; @@ -61,7 +61,7 @@ export async function addMetadataAssetsToManifest( // eslint-disable-next-line max-len async function prepareAsset( - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, asset: cxschema.AssetMetadataEntry, assetManifest: AssetManifestBuilder, envResources: EnvironmentResources, @@ -71,7 +71,7 @@ async function prepareAsset( case 'zip': case 'file': return prepareFileAsset( - { ioHost, action }, + ioHelper, asset, assetManifest, toolkitInfo, @@ -85,7 +85,7 @@ async function prepareAsset( } async function prepareFileAsset( - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, asset: cxschema.FileAssetMetadataEntry, assetManifest: AssetManifestBuilder, toolkitInfo: ToolkitInfo, @@ -98,7 +98,7 @@ async function prepareFileAsset( const key = `${s3Prefix}${baseName}`; const s3url = `s3://${toolkitInfo.bucketName}/${key}`; - await ioHost.notify(debug(action, `Storing asset ${asset.path} at ${s3url}`)); + await ioHelper.notify(debug(`Storing asset ${asset.path} at ${s3url}`)); assetManifest.addFileAsset(asset.sourceHash, { path: asset.path, diff --git a/packages/aws-cdk/lib/api/deployments/checks.ts b/packages/aws-cdk/lib/api/deployments/checks.ts index 35a04e267..92c6f1e41 100644 --- a/packages/aws-cdk/lib/api/deployments/checks.ts +++ b/packages/aws-cdk/lib/api/deployments/checks.ts @@ -1,11 +1,11 @@ +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { debug } from '../../cli/messages'; -import { IoMessaging } from '../../toolkit/cli-io-host'; import { ToolkitError } from '../../toolkit/error'; import { SDK } from '../aws-auth'; export async function determineAllowCrossAccountAssetPublishing( sdk: SDK, - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, customStackName?: string, ): Promise { try { @@ -34,8 +34,8 @@ export async function determineAllowCrossAccountAssetPublishing( // of creating bootstrap resources. If they do, there's nothing for us to validate, // but we can't use that as a reason to disallow cross-account publishing. We'll just // have to trust they did their due diligence. So we fail open. - await ioHost.notify(debug(action, `Error determining cross account asset publishing: ${e}`)); - await ioHost.notify(debug(action, 'Defaulting to allowing cross account asset publishing')); + await ioHelper.notify(debug(`Error determining cross account asset publishing: ${e}`)); + await ioHelper.notify(debug('Defaulting to allowing cross account asset publishing')); return true; } } diff --git a/packages/aws-cdk/lib/api/deployments/cloudformation.ts b/packages/aws-cdk/lib/api/deployments/cloudformation.ts index e2a2bf030..da4ec7af2 100644 --- a/packages/aws-cdk/lib/api/deployments/cloudformation.ts +++ b/packages/aws-cdk/lib/api/deployments/cloudformation.ts @@ -13,8 +13,8 @@ import { import { AssetManifest, FileManifestEntry } from 'cdk-assets'; import { AssetManifestBuilder } from './asset-manifest-builder'; import type { Deployments } from './deployments'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { debug } from '../../cli/messages'; -import { IoMessaging } from '../../toolkit/cli-io-host'; import { ToolkitError } from '../../toolkit/error'; import { formatErrorMessage, deserializeStructure } from '../../util'; import type { ICloudFormationClient, SdkProvider } from '../aws-auth'; @@ -288,12 +288,12 @@ async function waitFor( */ export async function waitForChangeSet( cfn: ICloudFormationClient, - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, stackName: string, changeSetName: string, { fetchAll }: { fetchAll: boolean }, ): Promise { - await ioHost.notify(debug(action, format('Waiting for changeset %s on stack %s to finish creating...', changeSetName, stackName))); + await ioHelper.notify(debug(format('Waiting for changeset %s on stack %s to finish creating...', changeSetName, stackName))); const ret = await waitFor(async () => { const description = await describeChangeSet(cfn, stackName, changeSetName, { fetchAll, @@ -301,7 +301,7 @@ export async function waitForChangeSet( // The following doesn't use a switch because tsc will not allow fall-through, UNLESS it is allows // EVERYWHERE that uses this library directly or indirectly, which is undesirable. if (description.Status === 'CREATE_PENDING' || description.Status === 'CREATE_IN_PROGRESS') { - await ioHost.notify(debug(action, format('Changeset %s on stack %s is still creating', changeSetName, stackName))); + await ioHelper.notify(debug(format('Changeset %s on stack %s is still creating', changeSetName, stackName))); return undefined; } @@ -350,7 +350,7 @@ export type CreateChangeSetOptions = { * Create a changeset for a diff operation */ export async function createDiffChangeSet( - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, options: PrepareChangeSetOptions, ): Promise { // `options.stack` has been modified to include any nested stack templates directly inline with its own template, under a special `NestedTemplate` property. @@ -358,13 +358,13 @@ export async function createDiffChangeSet( // This causes CreateChangeSet to fail with `Template Error: Fn::Equals cannot be partially collapsed`. for (const resource of Object.values(options.stack.template.Resources ?? {})) { if ((resource as any).Type === 'AWS::CloudFormation::Stack') { - await ioHost.notify(debug(action, 'This stack contains one or more nested stacks, falling back to template-only diff...')); + await ioHelper.notify(debug('This stack contains one or more nested stacks, falling back to template-only diff...')); return undefined; } } - return uploadBodyParameterAndCreateChangeSet({ ioHost, action }, options); + return uploadBodyParameterAndCreateChangeSet(ioHelper, options); } /** @@ -394,7 +394,7 @@ function templatesFromAssetManifestArtifact( } async function uploadBodyParameterAndCreateChangeSet( - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, options: PrepareChangeSetOptions, ): Promise { try { @@ -415,7 +415,7 @@ async function uploadBodyParameterAndCreateChangeSet( 'Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)\n', ); - return await createChangeSet({ ioHost, action }, { + return await createChangeSet(ioHelper, { cfn, changeSetName: 'cdk-diff-change-set', stack: options.stack, @@ -428,7 +428,7 @@ async function uploadBodyParameterAndCreateChangeSet( role: executionRoleArn, }); } catch (e: any) { - await ioHost.notify(debug(action, e)); + await ioHelper.notify(debug(e)); options.stream.write( 'Could not create a change set, will base the diff on template differences (run again with -v to see the reason)\n', ); @@ -465,12 +465,12 @@ export async function uploadStackTemplateAssets(stack: cxapi.CloudFormationStack } export async function createChangeSet( - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, options: CreateChangeSetOptions, ): Promise { - await cleanupOldChangeset(options.cfn, { ioHost, action }, options.changeSetName, options.stack.stackName); + await cleanupOldChangeset(options.cfn, ioHelper, options.changeSetName, options.stack.stackName); - await ioHost.notify(debug(action, `Attempting to create ChangeSet with name ${options.changeSetName} for stack ${options.stack.stackName}`)); + await ioHelper.notify(debug(`Attempting to create ChangeSet with name ${options.changeSetName} for stack ${options.stack.stackName}`)); const templateParams = TemplateParameters.fromTemplate(options.stack.template); const stackParams = templateParams.supplyAll(options.parameters); @@ -490,12 +490,12 @@ export async function createChangeSet( Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'], }); - await ioHost.notify(debug(action, format('Initiated creation of changeset: %s; waiting for it to finish creating...', changeSet.Id))); + await ioHelper.notify(debug(format('Initiated creation of changeset: %s; waiting for it to finish creating...', changeSet.Id))); // Fetching all pages if we'll execute, so we can have the correct change count when monitoring. - const createdChangeSet = await waitForChangeSet(options.cfn, { ioHost, action }, options.stack.stackName, options.changeSetName, { + const createdChangeSet = await waitForChangeSet(options.cfn, ioHelper, options.stack.stackName, options.changeSetName, { fetchAll: options.willExecute, }); - await cleanupOldChangeset(options.cfn, { ioHost, action }, options.changeSetName, options.stack.stackName); + await cleanupOldChangeset(options.cfn, ioHelper, options.changeSetName, options.stack.stackName); return createdChangeSet; } @@ -509,13 +509,13 @@ function toCfnTags(tags: { [id: string]: string }): Tag[] { async function cleanupOldChangeset( cfn: ICloudFormationClient, - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, changeSetName: string, stackName: string, ) { // Delete any existing change sets generated by CDK since change set names must be unique. // The delete request is successful as long as the stack exists (even if the change set does not exist). - await ioHost.notify(debug(action, `Removing existing change set with name ${changeSetName} if it exists`)); + await ioHelper.notify(debug(`Removing existing change set with name ${changeSetName} if it exists`)); await cfn.deleteChangeSet({ StackName: stackName, ChangeSetName: changeSetName, @@ -556,10 +556,10 @@ export function changeSetHasNoChanges(description: DescribeChangeSetCommandOutpu */ export async function waitForStackDelete( cfn: ICloudFormationClient, - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, stackName: string, ): Promise { - const stack = await stabilizeStack(cfn, { ioHost, action }, stackName); + const stack = await stabilizeStack(cfn, ioHelper, stackName); if (!stack) { return undefined; } @@ -588,10 +588,10 @@ export async function waitForStackDelete( */ export async function waitForStackDeploy( cfn: ICloudFormationClient, - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, stackName: string, ): Promise { - const stack = await stabilizeStack(cfn, { ioHost, action }, stackName); + const stack = await stabilizeStack(cfn, ioHelper, stackName); if (!stack) { return undefined; } @@ -614,18 +614,19 @@ export async function waitForStackDeploy( */ export async function stabilizeStack( cfn: ICloudFormationClient, - { ioHost, action }: IoMessaging, stackName: string, + ioHelper: IoHelper, + stackName: string, ) { - await ioHost.notify(debug(action, format('Waiting for stack %s to finish creating or updating...', stackName))); + await ioHelper.notify(debug(format('Waiting for stack %s to finish creating or updating...', stackName))); return waitFor(async () => { const stack = await CloudFormationStack.lookup(cfn, stackName); if (!stack.exists) { - await ioHost.notify(debug(action, format('Stack %s does not exist', stackName))); + await ioHelper.notify(debug(format('Stack %s does not exist', stackName))); return null; } const status = stack.stackStatus; if (status.isInProgress) { - await ioHost.notify(debug(action, format('Stack %s has an ongoing operation in progress and is not stable (%s)', stackName, status))); + await ioHelper.notify(debug(format('Stack %s has an ongoing operation in progress and is not stable (%s)', stackName, status))); return undefined; } else if (status.isReviewInProgress) { // This may happen if a stack creation operation is interrupted before the ChangeSet execution starts. Recovering @@ -634,7 +635,7 @@ export async function stabilizeStack( // "forever" we proceed as if the stack was existing and stable. If there is a concurrent operation that just // hasn't finished proceeding just yet, either this operation or the concurrent one may fail due to the other one // having made progress. Which is fine. I guess. - await ioHost.notify(debug(action, format('Stack %s is in REVIEW_IN_PROGRESS state. Considering this is a stable status (%s)', stackName, status))); + await ioHelper.notify(debug(format('Stack %s is in REVIEW_IN_PROGRESS state. Considering this is a stable status (%s)', stackName, status))); } return stack; diff --git a/packages/aws-cdk/lib/api/deployments/deploy-stack.ts b/packages/aws-cdk/lib/api/deployments/deploy-stack.ts index acef95344..82380c131 100644 --- a/packages/aws-cdk/lib/api/deployments/deploy-stack.ts +++ b/packages/aws-cdk/lib/api/deployments/deploy-stack.ts @@ -28,8 +28,8 @@ import { import { ChangeSetDeploymentMethod, DeploymentMethod } from './deployment-method'; import { DeployStackResult, SuccessfulDeployStackResult } from './deployment-result'; import { tryHotswapDeployment } from './hotswap-deployments'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { debug, info, warn } from '../../cli/messages'; -import { IIoHost, IoMessaging } from '../../toolkit/cli-io-host'; import { ToolkitError } from '../../toolkit/error'; import { formatErrorMessage } from '../../util'; import type { SDK, SdkProvider, ICloudFormationClient } from '../aws-auth'; @@ -201,7 +201,7 @@ export interface DeployStackOptions { readonly assetParallelism?: boolean; } -export async function deployStack(options: DeployStackOptions, { ioHost, action }: IoMessaging): Promise { +export async function deployStack(options: DeployStackOptions, ioHelper: IoHelper): Promise { const stackArtifact = options.stack; const stackEnv = options.resolvedEnvironment; @@ -212,11 +212,11 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action let cloudFormationStack = await CloudFormationStack.lookup(cfn, deployName); if (cloudFormationStack.stackStatus.isCreationFailure) { - await ioHost.notify(debug(action, + await ioHelper.notify(debug( `Found existing stack ${deployName} that had previously failed creation. Deleting it before attempting to re-create it.`, )); await cfn.deleteStack({ StackName: deployName }); - const deletedStack = await waitForStackDelete(cfn, { ioHost, action }, deployName); + const deletedStack = await waitForStackDelete(cfn, ioHelper, deployName); if (deletedStack && deletedStack.stackStatus.name !== 'DELETE_COMPLETE') { throw new ToolkitError( `Failed deleting stack ${deployName} that had previously failed creation (current state: ${deletedStack.stackStatus})`, @@ -233,7 +233,7 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action // parameters. const legacyAssets = new AssetManifestBuilder(); const assetParams = await addMetadataAssetsToManifest( - { ioHost, action }, + ioHelper, stackArtifact, legacyAssets, options.envResources, @@ -250,12 +250,12 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action const hotswapMode = options.hotswap ?? HotswapMode.FULL_DEPLOYMENT; const hotswapPropertyOverrides = options.hotswapPropertyOverrides ?? new HotswapPropertyOverrides(); - if (await canSkipDeploy(options, cloudFormationStack, stackParams.hasChanges(cloudFormationStack.parameters), { ioHost, action })) { - await ioHost.notify(debug(action, `${deployName}: skipping deployment (use --force to override)`)); + if (await canSkipDeploy(options, cloudFormationStack, stackParams.hasChanges(cloudFormationStack.parameters), ioHelper)) { + await ioHelper.notify(debug(`${deployName}: skipping deployment (use --force to override)`)); // if we can skip deployment and we are performing a hotswap, let the user know // that no hotswap deployment happened if (hotswapMode !== HotswapMode.FULL_DEPLOYMENT) { - await ioHost.notify(info(action, + await ioHelper.notify(info( format( `\n ${ICON} %s\n`, chalk.bold('hotswap deployment skipped - no changes were detected (use --force to override)'), @@ -269,7 +269,7 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action stackArn: cloudFormationStack.stackId, }; } else { - await ioHost.notify(debug(action, `${deployName}: deploying...`)); + await ioHelper.notify(debug(`${deployName}: deploying...`)); } const bodyParameter = await makeBodyParameter( @@ -283,19 +283,19 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action try { bootstrapStackName = (await options.envResources.lookupToolkit()).stackName; } catch (e) { - await ioHost.notify(debug(action, `Could not determine the bootstrap stack name: ${e}`)); + await ioHelper.notify(debug(`Could not determine the bootstrap stack name: ${e}`)); } await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, { parallel: options.assetParallelism, - allowCrossAccount: await determineAllowCrossAccountAssetPublishing(options.sdk, { ioHost, action }, bootstrapStackName), - }, { ioHost, action }); + allowCrossAccount: await determineAllowCrossAccountAssetPublishing(options.sdk, ioHelper, bootstrapStackName), + }, ioHelper); if (hotswapMode !== HotswapMode.FULL_DEPLOYMENT) { // attempt to short-circuit the deployment if possible try { const hotswapDeploymentResult = await tryHotswapDeployment( options.sdkProvider, - { ioHost, action }, + ioHelper, stackParams.values, cloudFormationStack, stackArtifact, @@ -304,7 +304,7 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action if (hotswapDeploymentResult) { return hotswapDeploymentResult; } - await ioHost.notify(info(action, format( + await ioHelper.notify(info(format( 'Could not perform a hotswap deployment, as the stack %s contains non-Asset changes', stackArtifact.displayName, ))); @@ -312,14 +312,14 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action if (!(e instanceof CfnEvaluationException)) { throw e; } - await ioHost.notify(info(action, format( + await ioHelper.notify(info(format( 'Could not perform a hotswap deployment, because the CloudFormation template could not be resolved: %s', formatErrorMessage(e), ))); } if (hotswapMode === HotswapMode.FALL_BACK) { - await ioHost.notify(info(action, 'Falling back to doing a full deployment')); + await ioHelper.notify(info('Falling back to doing a full deployment')); options.sdk.appendCustomUserAgent('cdk-hotswap/fallback'); } else { return { @@ -338,8 +338,7 @@ export async function deployStack(options: DeployStackOptions, { ioHost, action stackArtifact, stackParams, bodyParameter, - ioHost, - action, + ioHelper, ); return fullDeployment.performDeployment(); } @@ -367,8 +366,7 @@ class FullCloudFormationDeployment { private readonly stackArtifact: cxapi.CloudFormationStackArtifact, private readonly stackParams: ParameterValues, private readonly bodyParameter: TemplateBodyParameter, - private readonly ioHost: IIoHost, - private readonly action: IoMessaging['action'], + private readonly ioHelper: IoHelper, ) { this.cfn = options.sdk.cloudFormation(); this.stackName = options.deployName ?? stackArtifact.stackName; @@ -404,9 +402,9 @@ class FullCloudFormationDeployment { await this.updateTerminationProtection(); if (changeSetHasNoChanges(changeSetDescription)) { - await this.ioHost.notify(debug(this.action, format('No changes are to be performed on %s.', this.stackName))); + await this.ioHelper.notify(debug(format('No changes are to be performed on %s.', this.stackName))); if (execute) { - await this.ioHost.notify(debug(this.action, format('Deleting empty change set %s', changeSetDescription.ChangeSetId))); + await this.ioHelper.notify(debug(format('Deleting empty change set %s', changeSetDescription.ChangeSetId))); await this.cfn.deleteChangeSet({ StackName: this.stackName, ChangeSetName: changeSetName, @@ -414,8 +412,7 @@ class FullCloudFormationDeployment { } if (this.options.force) { - await this.ioHost.notify(warn( - this.action, + await this.ioHelper.notify(warn( [ 'You used the --force flag, but CloudFormation reported that the deployment would not make any changes.', 'According to CloudFormation, all resources are already up-to-date with the state in your CDK app.', @@ -435,7 +432,7 @@ class FullCloudFormationDeployment { } if (!execute) { - await this.ioHost.notify(info(this.action, format( + await this.ioHelper.notify(info(format( 'Changeset %s created and waiting in review for manual execution (--no-execute)', changeSetDescription.ChangeSetId, ))); @@ -467,8 +464,8 @@ class FullCloudFormationDeployment { private async createChangeSet(changeSetName: string, willExecute: boolean, importExistingResources: boolean) { await this.cleanupOldChangeset(changeSetName); - await this.ioHost.notify(debug(this.action, `Attempting to create ChangeSet with name ${changeSetName} to ${this.verb} stack ${this.stackName}`)); - await this.ioHost.notify(info(this.action, format('%s: creating CloudFormation changeset...', chalk.bold(this.stackName)))); + await this.ioHelper.notify(debug(`Attempting to create ChangeSet with name ${changeSetName} to ${this.verb} stack ${this.stackName}`)); + await this.ioHelper.notify(info(format('%s: creating CloudFormation changeset...', chalk.bold(this.stackName)))); const changeSet = await this.cfn.createChangeSet({ StackName: this.stackName, ChangeSetName: changeSetName, @@ -480,15 +477,15 @@ class FullCloudFormationDeployment { ...this.commonPrepareOptions(), }); - await this.ioHost.notify(debug(this.action, format('Initiated creation of changeset: %s; waiting for it to finish creating...', changeSet.Id))); + await this.ioHelper.notify(debug(format('Initiated creation of changeset: %s; waiting for it to finish creating...', changeSet.Id))); // Fetching all pages if we'll execute, so we can have the correct change count when monitoring. - return waitForChangeSet(this.cfn, { ioHost: this.ioHost, action: this.action }, this.stackName, changeSetName, { + return waitForChangeSet(this.cfn, this.ioHelper, this.stackName, changeSetName, { fetchAll: willExecute, }); } private async executeChangeSet(changeSet: DescribeChangeSetCommandOutput): Promise { - await this.ioHost.notify(debug(this.action, format('Initiating execution of changeset %s on stack %s', changeSet.ChangeSetId, this.stackName))); + await this.ioHelper.notify(debug(format('Initiating execution of changeset %s on stack %s', changeSet.ChangeSetId, this.stackName))); await this.cfn.executeChangeSet({ StackName: this.stackName, @@ -497,8 +494,7 @@ class FullCloudFormationDeployment { ...this.commonExecuteOptions(), }); - await this.ioHost.notify(debug( - this.action, + await this.ioHelper.notify(debug( format( 'Execution of changeset %s on stack %s has started; waiting for the update to complete...', changeSet.ChangeSetId, @@ -515,7 +511,7 @@ class FullCloudFormationDeployment { if (this.cloudFormationStack.exists) { // Delete any existing change sets generated by CDK since change set names must be unique. // The delete request is successful as long as the stack exists (even if the change set does not exist). - await this.ioHost.notify(debug(this.action, `Removing existing change set with name ${changeSetName} if it exists`)); + await this.ioHelper.notify(debug(`Removing existing change set with name ${changeSetName} if it exists`)); await this.cfn.deleteChangeSet({ StackName: this.stackName, ChangeSetName: changeSetName, @@ -527,8 +523,7 @@ class FullCloudFormationDeployment { // Update termination protection only if it has changed. const terminationProtection = this.stackArtifact.terminationProtection ?? false; if (!!this.cloudFormationStack.terminationProtection !== terminationProtection) { - await this.ioHost.notify(debug( - this.action, + await this.ioHelper.notify(debug( format ( 'Updating termination protection from %s to %s for stack %s', this.cloudFormationStack.terminationProtection, @@ -540,12 +535,12 @@ class FullCloudFormationDeployment { StackName: this.stackName, EnableTerminationProtection: terminationProtection, }); - await this.ioHost.notify(debug(this.action, format('Termination protection updated to %s for stack %s', terminationProtection, this.stackName))); + await this.ioHelper.notify(debug(format('Termination protection updated to %s for stack %s', terminationProtection, this.stackName))); } } private async directDeployment(): Promise { - await this.ioHost.notify(info(this.action, format('%s: %s stack...', chalk.bold(this.stackName), this.update ? 'updating' : 'creating'))); + await this.ioHelper.notify(info(format('%s: %s stack...', chalk.bold(this.stackName), this.update ? 'updating' : 'creating'))); const startTime = new Date(); @@ -561,7 +556,7 @@ class FullCloudFormationDeployment { }); } catch (err: any) { if (err.message === 'No updates are to be performed.') { - await this.ioHost.notify(debug(this.action, format('No updates are to be performed for stack %s', this.stackName))); + await this.ioHelper.notify(debug(format('No updates are to be performed for stack %s', this.stackName))); return { type: 'did-deploy-stack', noOp: true, @@ -595,15 +590,14 @@ class FullCloudFormationDeployment { stack: this.stackArtifact, stackName: this.stackName, resourcesTotal: expectedChanges, - ioHost: this.ioHost, - action: this.action, + ioHelper: this.ioHelper, changeSetCreationTime: startTime, }); await monitor.start(); let finalState = this.cloudFormationStack; try { - const successStack = await waitForStackDeploy(this.cfn, { ioHost: this.ioHost, action: this.action }, this.stackName); + const successStack = await waitForStackDeploy(this.cfn, this.ioHelper, this.stackName); // This shouldn't really happen, but catch it anyway. You never know. if (!successStack) { @@ -615,7 +609,7 @@ class FullCloudFormationDeployment { } finally { await monitor.stop(); } - debug(this.action, format('Stack %s has completed updating', this.stackName)); + debug(format('Stack %s has completed updating', this.stackName)); return { type: 'did-deploy-stack', noOp: false, @@ -666,7 +660,7 @@ export interface DestroyStackOptions { deployName?: string; } -export async function destroyStack(options: DestroyStackOptions, { ioHost, action }: IoMessaging) { +export async function destroyStack(options: DestroyStackOptions, ioHelper: IoHelper) { const deployName = options.deployName || options.stack.stackName; const cfn = options.sdk.cloudFormation(); @@ -678,14 +672,13 @@ export async function destroyStack(options: DestroyStackOptions, { ioHost, actio cfn, stack: options.stack, stackName: deployName, - ioHost, - action, + ioHelper: ioHelper, }); await monitor.start(); try { await cfn.deleteStack({ StackName: deployName, RoleARN: options.roleArn }); - const destroyedStack = await waitForStackDelete(cfn, { ioHost, action }, deployName); + const destroyedStack = await waitForStackDelete(cfn, ioHelper, deployName); if (destroyedStack && destroyedStack.stackStatus.name !== 'DELETE_COMPLETE') { throw new ToolkitError(`Failed to destroy ${deployName}: ${destroyedStack.stackStatus}`); } @@ -711,14 +704,14 @@ async function canSkipDeploy( deployStackOptions: DeployStackOptions, cloudFormationStack: CloudFormationStack, parameterChanges: ParameterChanges, - { ioHost, action }: IoMessaging, + ioHelper: IoHelper, ): Promise { const deployName = deployStackOptions.deployName || deployStackOptions.stack.stackName; - await ioHost.notify(debug(action, `${deployName}: checking if we can skip deploy`)); + await ioHelper.notify(debug(`${deployName}: checking if we can skip deploy`)); // Forced deploy if (deployStackOptions.force) { - await ioHost.notify(debug(action, `${deployName}: forced deployment`)); + await ioHelper.notify(debug(`${deployName}: forced deployment`)); return false; } @@ -727,53 +720,53 @@ async function canSkipDeploy( deployStackOptions.deploymentMethod?.method === 'change-set' && deployStackOptions.deploymentMethod.execute === false ) { - await ioHost.notify(debug(action, `${deployName}: --no-execute, always creating change set`)); + await ioHelper.notify(debug(`${deployName}: --no-execute, always creating change set`)); return false; } // No existing stack if (!cloudFormationStack.exists) { - await ioHost.notify(debug(action, `${deployName}: no existing stack`)); + await ioHelper.notify(debug(`${deployName}: no existing stack`)); return false; } // Template has changed (assets taken into account here) if (JSON.stringify(deployStackOptions.stack.template) !== JSON.stringify(await cloudFormationStack.template())) { - await ioHost.notify(debug(action, `${deployName}: template has changed`)); + await ioHelper.notify(debug(`${deployName}: template has changed`)); return false; } // Tags have changed if (!compareTags(cloudFormationStack.tags, deployStackOptions.tags ?? [])) { - await ioHost.notify(debug(action, `${deployName}: tags have changed`)); + await ioHelper.notify(debug(`${deployName}: tags have changed`)); return false; } // Notification arns have changed if (!arrayEquals(cloudFormationStack.notificationArns, deployStackOptions.notificationArns ?? [])) { - await ioHost.notify(debug(action, `${deployName}: notification arns have changed`)); + await ioHelper.notify(debug(`${deployName}: notification arns have changed`)); return false; } // Termination protection has been updated if (!!deployStackOptions.stack.terminationProtection !== !!cloudFormationStack.terminationProtection) { - await ioHost.notify(debug(action, `${deployName}: termination protection has been updated`)); + await ioHelper.notify(debug(`${deployName}: termination protection has been updated`)); return false; } // Parameters have changed if (parameterChanges) { if (parameterChanges === 'ssm') { - await ioHost.notify(debug(action, `${deployName}: some parameters come from SSM so we have to assume they may have changed`)); + await ioHelper.notify(debug(`${deployName}: some parameters come from SSM so we have to assume they may have changed`)); } else { - await ioHost.notify(debug(action, `${deployName}: parameters have changed`)); + await ioHelper.notify(debug(`${deployName}: parameters have changed`)); } return false; } // Existing stack is in a failed state if (cloudFormationStack.stackStatus.isFailure) { - await ioHost.notify(debug(action, `${deployName}: stack is in a failure state`)); + await ioHelper.notify(debug(`${deployName}: stack is in a failure state`)); return false; } diff --git a/packages/aws-cdk/lib/api/deployments/deployments.ts b/packages/aws-cdk/lib/api/deployments/deployments.ts index bfb54c847..828596958 100644 --- a/packages/aws-cdk/lib/api/deployments/deployments.ts +++ b/packages/aws-cdk/lib/api/deployments/deployments.ts @@ -24,8 +24,8 @@ import { loadCurrentTemplateWithNestedStacks, type RootTemplateWithNestedStacks, } from './nested-stack-helpers'; +import { IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private'; import { debug, warn } from '../../cli/messages'; -import { IIoHost, IoMessaging } from '../../toolkit/cli-io-host'; import { ToolkitError } from '../../toolkit/error'; import { formatErrorMessage } from '../../util'; import type { SdkProvider } from '../aws-auth/sdk-provider'; @@ -296,8 +296,7 @@ export interface StackExistsOptions { export interface DeploymentsProps { readonly sdkProvider: SdkProvider; readonly toolkitStackName?: string; - readonly ioHost: IIoHost; - readonly action: IoMessaging['action']; + readonly ioHelper: IoHelper; } /** @@ -332,18 +331,16 @@ export class Deployments { private _allowCrossAccountAssetPublishing: boolean | undefined; - private readonly ioHost: IIoHost; - private readonly action: IoMessaging['action']; + private readonly ioHelper: IoHelper; constructor(private readonly props: DeploymentsProps) { this.assetSdkProvider = props.sdkProvider; this.deployStackSdkProvider = props.sdkProvider; - this.ioHost = props.ioHost; - this.action = props.action; + this.ioHelper = props.ioHelper; this.envs = new EnvironmentAccess( props.sdkProvider, props.toolkitStackName ?? DEFAULT_TOOLKIT_STACK_NAME, - { ioHost: this.ioHost, action: this.action }, + this.ioHelper, ); } @@ -363,7 +360,7 @@ export class Deployments { } public async readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact): Promise