Skip to content

Commit

Permalink
refactor(cli): make the cli use more shared code (#207)
Browse files Browse the repository at this point in the history
Removes duplicated interfaces.

I know this is messy. Please bear with me 🙈 

---
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache-2.0 license
  • Loading branch information
mrgrain authored Mar 7, 2025
1 parent 2dca898 commit db6aa96
Show file tree
Hide file tree
Showing 24 changed files with 101 additions and 190 deletions.
2 changes: 2 additions & 0 deletions .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,8 @@ tmpToolkitHelpers.eslint?.addRules({
'@typescript-eslint/consistent-type-imports': 'error',
});

tmpToolkitHelpers.gitignore.addPatterns('test/**/*.map');

//////////////////////////////////////////////////////////////////////

let CLI_SDK_VERSION: '2' | '3' = ('3' as any);
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/tmp-toolkit-helpers/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './action-aware';
export * from './io-helper';
export * from './level-priority';
export * from './message-maker';
export * from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ export type ActionLessMessage<T> = Omit<IoMessage<T>, 'action'>;
export type ActionLessRequest<T, U> = Omit<IoRequest<T, U>, 'action'>;

/**
* Helper type for IoHosts that are action aware
* Helper for IO messaging.
*
* Wraps a client provided IoHost and provides additional features & services to toolkit internal classes.
*/
export interface ActionAwareIoHost extends IIoHost {
export interface IoHelper extends IIoHost {
action: ToolkitAction;
notify<T>(msg: ActionLessMessage<T>): Promise<void>;
requestResponse<T, U>(msg: ActionLessRequest<T, U>): Promise<U>;
}

/**
* An IoHost wrapper that adds the given action to an actionless message before
* sending the message to the given IoHost
* Wraps an IoHost and creates an IoHelper from it
*/
export function withAction(ioHost: IIoHost, action: ToolkitAction): ActionAwareIoHost {
export function asIoHelper(ioHost: IIoHost, action: ToolkitAction): IoHelper {
return {
action,
notify: async <T>(msg: Omit<IoMessage<T>, 'action'>) => {
await ioHost.notify({
...msg,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IoMessageCode, IoMessageLevel } from '../io-message';
import type { ActionLessMessage, ActionLessRequest } from './action-aware';
import type { ActionLessMessage, ActionLessRequest } from './io-helper';

/**
* Information for each IO Message Code.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isMessageRelevantForLevel } from '../../../lib/api/io/private/level-priority';
import { isMessageRelevantForLevel } from '../../../src/api/io/private/level-priority';

describe('IoMessageLevel', () => {
test.each`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type * as cxapi from '@aws-cdk/cx-api';
import type { ToolkitServices } from '../../../toolkit/private';
import { type Context, contextproviders, PROJECT_CONTEXT } from '../../aws-cdk';
import { IO } from '../../io/private';
import type { ActionAwareIoHost } from '../../shared-private';
import type { IoHelper } from '../../shared-private';
import { ToolkitError } from '../../shared-public';
import type { ICloudAssemblySource } from '../types';

Expand Down Expand Up @@ -43,7 +43,7 @@ export class ContextAwareCloudAssembly implements ICloudAssemblySource {
private canLookup: boolean;
private context: Context;
private contextFile: string;
private ioHost: ActionAwareIoHost;
private ioHost: IoHelper;

constructor(private readonly source: ICloudAssemblySource, private readonly props: ContextAwareCloudAssemblyProps) {
this.canLookup = props.lookups ?? true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { prepareDefaultEnvironment as oldPrepare, prepareContext, spaceAvailable
import { splitBySize } from '../../../private/util';
import type { ToolkitServices } from '../../../toolkit/private';
import { IO } from '../../io/private';
import type { ActionAwareIoHost } from '../../shared-private';
import type { IoHelper } from '../../shared-private';
import { ToolkitError } from '../../shared-public';
import type { AppSynthOptions, LoadAssemblyOptions } from '../source-builder';

Expand Down Expand Up @@ -130,7 +130,7 @@ export async function withContext<T>(
*
* @param assembly the assembly to check
*/
export async function checkContextOverflowSupport(assembly: cxapi.CloudAssembly, ioHost: ActionAwareIoHost): Promise<void> {
export async function checkContextOverflowSupport(assembly: cxapi.CloudAssembly, ioHost: IoHelper): Promise<void> {
const tree = loadTree(assembly);
const frameworkDoesNotSupportContextOverflow = some(tree, node => {
const fqn = node.constructInfo?.fqn;
Expand All @@ -149,7 +149,7 @@ export async function checkContextOverflowSupport(assembly: cxapi.CloudAssembly,
/**
* Safely create an assembly from a cloud assembly directory
*/
export async function assemblyFromDirectory(assemblyDir: string, ioHost: ActionAwareIoHost, loadOptions: LoadAssemblyOptions = {}) {
export async function assemblyFromDirectory(assemblyDir: string, ioHost: IoHelper, loadOptions: LoadAssemblyOptions = {}) {
try {
const assembly = new cxapi.CloudAssembly(assemblyDir, {
skipVersionCheck: !(loadOptions.checkVersion ?? true),
Expand Down
1 change: 0 additions & 1 deletion packages/@aws-cdk/toolkit-lib/lib/api/io/private/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './messages';
export * from './io-host-wrappers';
export * from './level-priority';
export * from './timer';
export * from './sdk-logger';
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { inspect } from 'util';
import type { Logger } from '@smithy/types';
import { IO } from './messages';
import { replacerBufferWithInfo } from '../../../private/util';
import type { ActionAwareIoHost } from '../../shared-private';
import type { IoHelper } from '../../shared-private';

/**
* An SDK logging trace.
Expand All @@ -27,7 +27,7 @@ export interface SdkTrace {
readonly content: any;
}

export function asSdkLogger(ioHost: ActionAwareIoHost): Logger {
export function asSdkLogger(ioHost: IoHelper): Logger {
return new class implements Logger {
// This is too much detail for our logs
public trace(..._content: any[]) {
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/toolkit-lib/lib/api/io/private/timer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { format } from 'util';
import { IO } from './messages';
import { formatTime } from '../../../private/util';
import type { ActionAwareIoHost } from '../../shared-private';
import type { IoHelper } from '../../shared-private';

/**
* Helper class to measure the time of code.
Expand Down Expand Up @@ -37,7 +37,7 @@ export class Timer {
* Ends the current timer as a specified timing and notifies the IoHost.
* @returns the elapsed time
*/
public async endAs(ioHost: ActionAwareIoHost, type: 'synth' | 'deploy' | 'rollback' | 'destroy' | 'bootstrap') {
public async endAs(ioHost: IoHelper, type: 'synth' | 'deploy' | 'rollback' | 'destroy' | 'bootstrap') {
const duration = this.end();
await ioHost.notify(timerMessage(type, duration));
return duration;
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/toolkit-lib/lib/toolkit/private/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import type { SdkProvider } from '../../api/aws-cdk';
import type { ICloudAssemblySource } from '../../api/cloud-assembly';
import { CachedCloudAssemblySource, StackAssembly } from '../../api/cloud-assembly/private';
import type { ActionAwareIoHost } from '../../api/shared-private';
import type { IoHelper } from '../../api/shared-private';

/**
* Helper struct to pass internal services around.
*/
export interface ToolkitServices {
sdkProvider: SdkProvider;
ioHost: ActionAwareIoHost;
ioHost: IoHelper;
}

/**
Expand Down
24 changes: 12 additions & 12 deletions packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import type { StackAssembly } from '../api/cloud-assembly/private';
import { ALL_STACKS, CloudAssemblySourceBuilder, IdentityCloudAssemblySource } from '../api/cloud-assembly/private';
import type { IIoHost, IoMessageLevel } from '../api/io';
import { Timer, IO, asSdkLogger, withoutColor, withoutEmojis, withTrimmedWhitespace } from '../api/io/private';
import type { ActionAwareIoHost } from '../api/shared-private';
import { withAction } from '../api/shared-private';
import type { IoHelper } from '../api/shared-private';
import { asIoHelper } from '../api/shared-private';
import type { ToolkitAction } from '../api/shared-public';
import { ToolkitError } from '../api/shared-public';
import { obscureTemplate, serializeStructure, validateSnsTopicArn, formatTime, formatErrorMessage } from '../private/util';
Expand Down Expand Up @@ -130,7 +130,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
if (!this._sdkProvider) {
this._sdkProvider = await SdkProvider.withAwsCliCompatibleDefaults({
...this.props.sdkConfig,
logger: asSdkLogger(withAction(this.ioHost, action)),
logger: asSdkLogger(asIoHelper(this.ioHost, action)),
});
}

Expand All @@ -142,7 +142,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
*/
protected override async sourceBuilderServices(): Promise<ToolkitServices> {
return {
ioHost: withAction(this.ioHost, 'assembly'),
ioHost: asIoHelper(this.ioHost, 'assembly'),
sdkProvider: await this.sdkProvider('assembly'),
};
}
Expand All @@ -151,7 +151,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
* Bootstrap Action
*/
public async bootstrap(environments: BootstrapEnvironments, options: BootstrapOptions): Promise<void> {
const ioHost = withAction(this.ioHost, 'bootstrap');
const ioHost = asIoHelper(this.ioHost, 'bootstrap');
const bootstrapEnvironments = await environments.getEnvironments();
const source = options.source ?? BootstrapSource.default();
const parameters = options.parameters;
Expand Down Expand Up @@ -197,7 +197,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
* Synth Action
*/
public async synth(cx: ICloudAssemblySource, options: SynthOptions = {}): Promise<ICloudAssemblySource> {
const ioHost = withAction(this.ioHost, 'synth');
const ioHost = asIoHelper(this.ioHost, 'synth');
const synthTimer = Timer.start();
const assembly = await assemblyFromSource(cx);
const stacks = assembly.selectStacksV2(options.stacks ?? ALL_STACKS);
Expand Down Expand Up @@ -242,7 +242,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
* List selected stacks and their dependencies
*/
public async list(cx: ICloudAssemblySource, options: ListOptions = {}): Promise<StackDetails[]> {
const ioHost = withAction(this.ioHost, 'list');
const ioHost = asIoHelper(this.ioHost, 'list');
const synthTimer = Timer.start();
const assembly = await assemblyFromSource(cx);
const stackCollection = await assembly.selectStacksV2(options.stacks ?? ALL_STACKS);
Expand All @@ -269,7 +269,7 @@ 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 = withAction(this.ioHost, action);
const ioHost = asIoHelper(this.ioHost, action);
const synthTimer = Timer.start();
const stackCollection = assembly.selectStacksV2(options.stacks ?? ALL_STACKS);
await this.validateStacksMetadata(stackCollection, ioHost);
Expand Down Expand Up @@ -571,7 +571,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
*/
public async watch(cx: ICloudAssemblySource, options: WatchOptions): Promise<void> {
const assembly = await assemblyFromSource(cx, false);
const ioHost = withAction(this.ioHost, 'watch');
const ioHost = asIoHelper(this.ioHost, 'watch');
const rootDir = options.watchDir ?? process.cwd();

if (options.include === undefined && options.exclude === undefined) {
Expand Down Expand Up @@ -694,7 +694,7 @@ 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<void> {
const ioHost = withAction(this.ioHost, action);
const ioHost = asIoHelper(this.ioHost, action);
const synthTimer = Timer.start();
const stacks = assembly.selectStacksV2(options.stacks);
await this.validateStacksMetadata(stacks, ioHost);
Expand Down Expand Up @@ -752,7 +752,7 @@ 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<void> {
const ioHost = withAction(this.ioHost, action);
const ioHost = 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();
Expand Down Expand Up @@ -794,7 +794,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
/**
* Validate the stacks for errors and warnings according to the CLI's current settings
*/
private async validateStacksMetadata(stacks: StackCollection, ioHost: ActionAwareIoHost) {
private async validateStacksMetadata(stacks: StackCollection, ioHost: IoHelper) {
const builder = (level: IoMessageLevel) => {
switch (level) {
case 'error': return IO.CDK_ASSEMBLY_E9999;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { IIoHost, IoMessage, IoMessageLevel, IoRequest } from '../../lib';
import { RequireApproval } from '../../lib';
import { isMessageRelevantForLevel } from '../../lib/api/io/private/level-priority';
import { isMessageRelevantForLevel } from '../../lib/api/shared-private';

/**
* A test implementation of IIoHost that does nothing but can by spied on.
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-cdk/lib/api/deployments/asset-publishing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from 'cdk-assets';
import type { SDK } from '..';
import { formatMessage } from '../../cli/messages';
import { IIoHost, IoMessageLevel, IoMessaging, ToolkitAction } from '../../toolkit/cli-io-host';
import { IIoHost, IoMessageLevel, IoMessaging } from '../../toolkit/cli-io-host';
import { ToolkitError } from '../../toolkit/error';
import type { SdkProvider } from '../aws-auth';
import { Mode } from '../plugin';
Expand Down Expand Up @@ -184,7 +184,7 @@ export const EVENT_TO_LEVEL: Record<EventType, IoMessageLevel | false> = {

export abstract class BasePublishProgressListener implements IPublishProgressListener {
protected readonly ioHost: IIoHost;
protected readonly action: ToolkitAction;
protected readonly action: IoMessaging['action'];

constructor({ ioHost, action }: IoMessaging) {
this.ioHost = ioHost;
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-cdk/lib/api/deployments/deploy-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { ChangeSetDeploymentMethod, DeploymentMethod } from './deployment-method
import { DeployStackResult, SuccessfulDeployStackResult } from './deployment-result';
import { tryHotswapDeployment } from './hotswap-deployments';
import { debug, info, warn } from '../../cli/messages';
import { IIoHost, IoMessaging, ToolkitAction } from '../../toolkit/cli-io-host';
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';
Expand Down Expand Up @@ -368,7 +368,7 @@ class FullCloudFormationDeployment {
private readonly stackParams: ParameterValues,
private readonly bodyParameter: TemplateBodyParameter,
private readonly ioHost: IIoHost,
private readonly action: ToolkitAction,
private readonly action: IoMessaging['action'],
) {
this.cfn = options.sdk.cloudFormation();
this.stackName = options.deployName ?? stackArtifact.stackName;
Expand Down
6 changes: 3 additions & 3 deletions packages/aws-cdk/lib/api/deployments/deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
type RootTemplateWithNestedStacks,
} from './nested-stack-helpers';
import { debug, warn } from '../../cli/messages';
import { IIoHost, IoMessaging, ToolkitAction } from '../../toolkit/cli-io-host';
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';
Expand Down Expand Up @@ -290,7 +290,7 @@ export interface DeploymentsProps {
readonly sdkProvider: SdkProvider;
readonly toolkitStackName?: string;
readonly ioHost: IIoHost;
readonly action: ToolkitAction;
readonly action: IoMessaging['action'];
}

/**
Expand Down Expand Up @@ -326,7 +326,7 @@ export class Deployments {
private _allowCrossAccountAssetPublishing: boolean | undefined;

private readonly ioHost: IIoHost;
private readonly action: ToolkitAction;
private readonly action: IoMessaging['action'];

constructor(private readonly props: DeploymentsProps) {
this.assetSdkProvider = props.sdkProvider;
Expand Down
Loading

0 comments on commit db6aa96

Please sign in to comment.