Skip to content

feat(toolkit-lib): emit marker messages during actions #219

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 11, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .projenrc.ts
Original file line number Diff line number Diff line change
@@ -705,6 +705,7 @@ const tmpToolkitHelpers = configureProject(
'archiver',
'glob',
'semver',
'uuid',
'yaml@^1',
],
tsconfig: {
4 changes: 4 additions & 0 deletions packages/@aws-cdk/tmp-toolkit-helpers/.projen/deps.json

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

2 changes: 1 addition & 1 deletion packages/@aws-cdk/tmp-toolkit-helpers/.projen/tasks.json

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

1 change: 1 addition & 0 deletions packages/@aws-cdk/tmp-toolkit-helpers/package.json

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
@@ -0,0 +1 @@
export * from './stack-selector';
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Which stacks should be selected from a cloud assembly
*/
export enum StackSelectionStrategy {
/**
* Returns all stacks in the app regardless of patterns,
* including stacks inside nested assemblies.
*/
ALL_STACKS = 'all-stacks',

/**
* Returns all stacks in the main (top level) assembly only.
*/
MAIN_ASSEMBLY = 'main-assembly',

/**
* If the assembly includes a single stack, returns it.
* Otherwise throws an exception.
*/
ONLY_SINGLE = 'only-single',

/**
* Return stacks matched by patterns.
* If no stacks are found, execution is halted successfully.
* Most likely you don't want to use this but `StackSelectionStrategy.MUST_MATCH_PATTERN`
*/
PATTERN_MATCH = 'pattern-match',

/**
* Return stacks matched by patterns.
* Throws an exception if the patterns don't match at least one stack in the assembly.
*/
PATTERN_MUST_MATCH = 'pattern-must-match',

/**
* Returns if exactly one stack is matched by the pattern(s).
* Throws an exception if no stack, or more than exactly one stack are matched.
*/
PATTERN_MUST_MATCH_SINGLE = 'pattern-must-match-single',
}

/**
* When selecting stacks, what other stacks to include because of dependencies
*/
export enum ExpandStackSelection {
/**
* Don't select any extra stacks
*/
NONE = 'none',

/**
* Include stacks that this stack depends on
*/
UPSTREAM = 'upstream',

/**
* Include stacks that depend on this stack
*/
DOWNSTREAM = 'downstream',

/**
* @TODO
* Include both directions.
* I.e. stacks that this stack depends on, and stacks that depend on this stack.
*/
// FULL = 'full',
}

/**
* A specification of which stacks should be selected
*/
export interface StackSelector {
/**
* The behavior if if no selectors are provided.
*/
strategy: StackSelectionStrategy;

/**
* A list of patterns to match the stack hierarchical ids
* Only used with `PATTERN_*` selection strategies.
*/
patterns?: string[];

/**
* Expand the selection to upstream/downstream stacks.
* @default ExpandStackSelection.None only select the specified/matched stacks
*/
expand?: ExpandStackSelection;

/**
* By default, we throw an exception if the assembly contains no stacks.
* Set to `false`, to halt execution for empty assemblies without error.
*
* Note that actions can still throw if a stack selection result is empty,
* but the assembly contains stacks in principle.
*
* @default true
*/
failOnEmpty?: boolean;
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/tmp-toolkit-helpers/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './cloud-assembly';
export * from './io';
export * from './toolkit-error';
1 change: 0 additions & 1 deletion packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/index.ts
Original file line number Diff line number Diff line change
@@ -2,4 +2,3 @@ export * from './io-host';
export * from './io-message';
export * from './toolkit-action';
export * from './payloads';
export * from './messages';
10 changes: 10 additions & 0 deletions packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/io-message.ts
Original file line number Diff line number Diff line change
@@ -59,6 +59,16 @@ export interface IoMessage<T> {
*/
readonly message: string;

/**
* Identifies the message span, this message belongs to.
*
* A message span, groups multiple messages together that semantically related to the same operation.
* This is an otherwise meaningless identifier.
*
* A message without a `spanId`, does not belong to a span.
*/
readonly span?: string;

/**
* The data attached to the message.
*/
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { CloudFormationStackArtifact } from '@aws-cdk/cx-api';
import type { IManifestEntry } from 'cdk-assets';
import type { PermissionChangeType } from './diff';
import type { ConfirmationRequest } from './types';

@@ -55,3 +56,46 @@ export interface NeedRollbackFirstDeployStackResult {
export interface ReplacementRequiresRollbackStackResult {
readonly type: 'replacement-requires-rollback';
}

export interface StackDeployProgress {
/**
* The total number of stacks being deployed
*/
readonly total: number;
/**
* The count of the stack currently attempted to be deployed
*
* This is counting value, not an identifier.
*/
readonly current: number;
/**
* The stack that's currently being deployed
*/
readonly stack: CloudFormationStackArtifact;
}

/**
* Payload for a yes/no confirmation in deploy. Includes information on
* what kind of change is being made.
*/
export interface DeployConfirmationRequest extends ConfirmationRequest {
/**
* The type of change being made to the IAM permissions.
*/
readonly permissionChangeType: PermissionChangeType;
}

export interface BuildAsset {
/**
* The asset that is build
*/
readonly asset: IManifestEntry;
}

export interface PublishAsset {

/**
* The asset that is published
*/
readonly asset: IManifestEntry;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import type { CloudFormationStackArtifact } from '@aws-cdk/cx-api';

export interface StackDestroy {
/**
* The stacks that will be destroyed
*/
readonly stacks: CloudFormationStackArtifact[];
}

export interface StackDestroyProgress {
/**
* The total number of stacks being destroyed
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ export * from './sdk-trace';
export * from './context';
export * from './rollback';
export * from './stack-activity';
export * from './synth';
export * from './types';
export * from './progress';
export * from './watch';
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type MetadataEntry } from '@aws-cdk/cloud-assembly-schema';
import { type CloudFormationStackArtifact } from '@aws-cdk/cx-api';
import type { MetadataEntry } from '@aws-cdk/cloud-assembly-schema';
import type { CloudFormationStackArtifact } from '@aws-cdk/cx-api';
import type { StackEvent } from '@aws-sdk/client-cloudformation';
import type { StackProgress } from './progress';

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { StackSelector } from '../../cloud-assembly/stack-selector';

export interface StackSelectionDetails {
/**
* The selected stacks, if any
*/
readonly stacks: StackSelector;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/**
* The computed file watch settings
*/
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from './io-helper';
export * from './level-priority';
export * from './span';
export * from './message-maker';
export * from './messages';
export * from './types';
Original file line number Diff line number Diff line change
@@ -1,39 +1,59 @@
import type { IIoHost } from '../io-host';
import type { IoMessage, IoRequest } from '../io-message';
import type { ToolkitAction } from '../toolkit-action';
import type { SpanEnd, SpanDefinition } from './span';
import { SpanMaker } from './span';

export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
export type SimplifiedMessage<T> = Pick<IoMessage<T>, 'level' | 'code' | 'message' | 'data'>;
export type ActionLessMessage<T> = Omit<IoMessage<T>, 'action'>;
export type ActionLessRequest<T, U> = Omit<IoRequest<T, U>, 'action'>;

/**
* Helper for IO messaging.
*
* Wraps a client provided IoHost and provides additional features & services to toolkit internal classes.
* A class containing helper tools to interact with IoHost
*/
export interface IoHelper extends IIoHost {
notify(msg: ActionLessMessage<unknown>): Promise<void>;
notify<T>(msg: ActionLessMessage<T>): Promise<void>;
requestResponse<T, U>(msg: ActionLessRequest<T, U>): Promise<U>;
export class IoHelper implements IIoHost {
public static fromIoHost(ioHost: IIoHost, action: ToolkitAction) {
return new IoHelper(ioHost, action);
}

private readonly ioHost: IIoHost;
private readonly action: ToolkitAction;

private constructor(ioHost: IIoHost, action: ToolkitAction) {
this.ioHost = ioHost;
this.action = action;
}

/**
* Forward a message to the IoHost, while injection the current action
*/
public notify(msg: ActionLessMessage<unknown>): Promise<void> {
return this.ioHost.notify({
...msg,
action: this.action,
});
}

/**
* Forward a request to the IoHost, while injection the current action
*/
public requestResponse<T, U>(msg: ActionLessRequest<T, U>): Promise<U> {
return this.ioHost.requestResponse({
...msg,
action: this.action,
});
}

/**
* Create a new marker from a given registry entry
*/
public span<S extends object, E extends SpanEnd>(definition: SpanDefinition<S, E>) {
return new SpanMaker(this, definition);
}
}

/**
* Wraps an IoHost and creates an IoHelper from it
*/
export function asIoHelper(ioHost: IIoHost, action: ToolkitAction): IoHelper {
return {
notify: async <T>(msg: Omit<IoMessage<T>, 'action'>) => {
await ioHost.notify({
...msg,
action,
});
},
requestResponse: async <T, U>(msg: Omit<IoRequest<T, U>, 'action'>) => {
return ioHost.requestResponse({
...msg,
action,
});
},
};
return IoHelper.fromIoHost(ioHost, action);
}
Loading