Skip to content

Commit 25db152

Browse files
authored
feat(toolkit-lib): add a return type for toolkit.diff() (#368)
`toolkit.diff()` now returns a `TemplateDiff` for each stack it diffs. Closes aws/aws-cdk#33182 --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license
1 parent 4bd6149 commit 25db152

File tree

6 files changed

+208
-127
lines changed

6 files changed

+208
-127
lines changed

Diff for: packages/@aws-cdk/tmp-toolkit-helpers/src/api/diff/diff-formatter.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,6 @@ export interface TemplateInfo {
120120
*/
121121
readonly changeSet?: any;
122122

123-
/**
124-
* The name of the stack
125-
*
126-
* @default undefined
127-
*/
128-
readonly stackName?: string;
129-
130123
/**
131124
* Whether or not there are any imported resources
132125
*
@@ -151,21 +144,31 @@ export class DiffFormatter {
151144
private readonly ioHelper: IoHelper;
152145
private readonly oldTemplate: any;
153146
private readonly newTemplate: cxapi.CloudFormationStackArtifact;
154-
private readonly stackName?: string;
147+
private readonly stackName: string;
155148
private readonly changeSet?: any;
156149
private readonly nestedStacks: { [nestedStackLogicalId: string]: NestedStackTemplates } | undefined;
157150
private readonly isImport: boolean;
158151

152+
/**
153+
* Stores the TemplateDiffs that get calculated in this DiffFormatter,
154+
* indexed by the stack name.
155+
*/
156+
private _diffs: { [name: string]: TemplateDiff } = {};
157+
159158
constructor(props: DiffFormatterProps) {
160159
this.ioHelper = props.ioHelper;
161160
this.oldTemplate = props.templateInfo.oldTemplate;
162161
this.newTemplate = props.templateInfo.newTemplate;
163-
this.stackName = props.templateInfo.stackName;
162+
this.stackName = props.templateInfo.newTemplate.stackName;
164163
this.changeSet = props.templateInfo.changeSet;
165164
this.nestedStacks = props.templateInfo.nestedStacks;
166165
this.isImport = props.templateInfo.isImport ?? false;
167166
}
168167

168+
public get diffs() {
169+
return this._diffs;
170+
}
171+
169172
/**
170173
* Format the stack diff
171174
*/
@@ -184,11 +187,12 @@ export class DiffFormatter {
184187

185188
private formatStackDiffHelper(
186189
oldTemplate: any,
187-
stackName: string | undefined,
190+
stackName: string,
188191
nestedStackTemplates: { [nestedStackLogicalId: string]: NestedStackTemplates } | undefined,
189192
options: ReusableStackDiffOptions,
190193
) {
191194
let diff = fullDiff(oldTemplate, this.newTemplate.template, this.changeSet, this.isImport);
195+
this._diffs[stackName] = diff;
192196

193197
// The stack diff is formatted via `Formatter`, which takes in a stream
194198
// and sends its output directly to that stream. To faciliate use of the
@@ -277,6 +281,7 @@ export class DiffFormatter {
277281
const ioDefaultHelper = new IoDefaultMessages(this.ioHelper);
278282

279283
const diff = fullDiff(this.oldTemplate, this.newTemplate.template, this.changeSet);
284+
this._diffs[this.stackName] = diff;
280285

281286
if (diffRequiresApproval(diff, options.requireApproval)) {
282287
// The security diff is formatted via `Formatter`, which takes in a stream

Diff for: packages/@aws-cdk/toolkit-lib/lib/actions/diff/private/helpers.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ async function cfnDiff(
9191
templateInfos.push({
9292
oldTemplate: currentTemplate,
9393
newTemplate: stack,
94-
stackName: stack.stackName,
9594
isImport: !!resourcesToImport,
9695
nestedStacks,
9796
changeSet,
@@ -167,3 +166,25 @@ export function determinePermissionType(
167166
return PermissionChangeType.NONE;
168167
}
169168
}
169+
170+
/**
171+
* Appends all properties from obj2 to obj1.
172+
* obj2 values take priority in the case of collisions.
173+
*
174+
* @param obj1 The object to modify
175+
* @param obj2 The object to consume
176+
*
177+
* @returns obj1 with all properties from obj2
178+
*/
179+
export function appendObject<T>(
180+
obj1: { [name: string]: T },
181+
obj2: { [name: string]: T },
182+
): { [name: string]: T } {
183+
// Directly modify obj1 by adding all properties from obj2
184+
for (const key in obj2) {
185+
obj1[key] = obj2[key];
186+
}
187+
188+
// Return the modified obj1
189+
return obj1;
190+
}

Diff for: packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as path from 'node:path';
2+
import type { TemplateDiff } from '@aws-cdk/cloudformation-diff';
23
import * as cxapi from '@aws-cdk/cx-api';
34
import * as chalk from 'chalk';
45
import * as chokidar from 'chokidar';
@@ -23,7 +24,7 @@ import {
2324
} from '../actions/deploy/private';
2425
import { type DestroyOptions } from '../actions/destroy';
2526
import type { DiffOptions } from '../actions/diff';
26-
import { determinePermissionType, makeTemplateInfos as prepareDiff } from '../actions/diff/private';
27+
import { appendObject, determinePermissionType, makeTemplateInfos as prepareDiff } from '../actions/diff/private';
2728
import { type ListOptions } from '../actions/list';
2829
import type { RefactorOptions } from '../actions/refactor';
2930
import { type RollbackOptions } from '../actions/rollback';
@@ -300,7 +301,7 @@ export class Toolkit extends CloudAssemblySourceBuilder {
300301
/**
301302
* Diff Action
302303
*/
303-
public async diff(cx: ICloudAssemblySource, options: DiffOptions): Promise<void> {
304+
public async diff(cx: ICloudAssemblySource, options: DiffOptions): Promise<{ [name: string]: TemplateDiff}> {
304305
const ioHelper = asIoHelper(this.ioHost, 'diff');
305306
const selectStacks = options.stacks ?? ALL_STACKS;
306307
const synthSpan = await ioHelper.span(SPAN.SYNTH_ASSEMBLY).begin({ stacks: selectStacks });
@@ -319,7 +320,7 @@ export class Toolkit extends CloudAssemblySourceBuilder {
319320
let formattedStackDiff = '';
320321

321322
const templateInfos = await prepareDiff(ioHelper, stacks, deployments, await this.sdkProvider('diff'), options);
322-
323+
const templateDiffs: { [name: string]: TemplateDiff } = {};
323324
for (const templateInfo of templateInfos) {
324325
const formatter = new DiffFormatter({
325326
ioHelper,
@@ -340,14 +341,15 @@ export class Toolkit extends CloudAssemblySourceBuilder {
340341
formattedStackDiff = diff.formattedDiff;
341342
diffs = diff.numStacksWithChanges;
342343
}
344+
appendObject(templateDiffs, formatter.diffs);
343345
}
344346

345347
await diffSpan.end(`✨ Number of stacks with differences: ${diffs}`, {
346348
formattedSecurityDiff,
347349
formattedStackDiff,
348350
});
349351

350-
return;
352+
return templateDiffs;
351353
}
352354

353355
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as s3 from 'aws-cdk-lib/aws-s3';
2+
import * as sqs from 'aws-cdk-lib/aws-sqs';
3+
import * as core from 'aws-cdk-lib/core';
4+
5+
export default async () => {
6+
const app = new core.App({ autoSynth: false });
7+
const stack1 = new core.Stack(app, 'Stack1');
8+
new s3.Bucket(stack1, 'MyBucket');
9+
const stack2 = new core.Stack(app, 'Stack2');
10+
new sqs.Queue(stack2, 'MyQueue');
11+
12+
return app.synth();
13+
};

0 commit comments

Comments
 (0)