diff --git a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/diff/diff-formatter.ts b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/diff/diff-formatter.ts index 9f5b50aac..b05b99427 100644 --- a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/diff/diff-formatter.ts +++ b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/diff/diff-formatter.ts @@ -208,7 +208,7 @@ export class DiffFormatter { try { // must output the stack name if there are differences, even if quiet if (stackName && (!options.quiet || !diff.isEmpty)) { - stream.write(format('Stack %s\n', chalk.bold(stackName))); + stream.write(format(`Stack ${chalk.bold(stackName)}\n`)); } if (!options.quiet && options.isImport) { @@ -239,20 +239,19 @@ export class DiffFormatter { ...logicalIdMapFromTemplate(this.oldTemplate), ...buildLogicalToPathMap(this.newTemplate), }, options.context); - - // store the stream containing a formatted stack diff - formattedDiff = stream.toString(); } else if (!options.quiet) { - options.ioDefaultHelper.info(chalk.green('There were no differences')); + stream.write(chalk.green('There were no differences\n')); + } + + if (filteredChangesCount > 0) { + stream.write(chalk.yellow(`Omitted ${filteredChangesCount} changes because they are likely mangled non-ASCII characters. Use --strict to print them.\n`)); } } finally { + // store the stream containing a formatted stack diff + formattedDiff = stream.toString(); stream.end(); } - if (filteredChangesCount > 0) { - options.ioDefaultHelper.info(chalk.yellow(`Omitted ${filteredChangesCount} changes because they are likely mangled non-ASCII characters. Use --strict to print them.`)); - } - for (const nestedStackLogicalId of Object.keys(nestedStackTemplates ?? {})) { if (!nestedStackTemplates) { break; @@ -285,18 +284,18 @@ export class DiffFormatter { const diff = fullDiff(this.oldTemplate, this.newTemplate.template, options.changeSet); if (diffRequiresApproval(diff, options.requireApproval)) { - ioDefaultHelper.info(format('Stack %s\n', chalk.bold(options.stackName))); - - // eslint-disable-next-line max-len - ioDefaultHelper.warning(`This deployment will make potentially sensitive changes according to your current security approval level (--require-approval ${options.requireApproval}).`); - ioDefaultHelper.warning('Please confirm you intend to make the following modifications:\n'); - // The security diff is formatted via `Formatter`, which takes in a stream // and sends its output directly to that stream. To faciliate use of the // global CliIoHost, we create our own stream to capture the output of // `Formatter` and return the output as a string for the consumer of // `formatSecurityDiff` to decide what to do with it. const stream = new StringWriteStream(); + + stream.write(format(`Stack ${chalk.bold(options.stackName)}\n`)); + + // eslint-disable-next-line max-len + ioDefaultHelper.warning(`This deployment will make potentially sensitive changes according to your current security approval level (--require-approval ${options.requireApproval}).`); + ioDefaultHelper.warning('Please confirm you intend to make the following modifications:\n'); try { // formatSecurityChanges updates the stream with the formatted security diff formatSecurityChanges(stream, diff, buildLogicalToPathMap(this.newTemplate)); diff --git a/packages/@aws-cdk/tmp-toolkit-helpers/test/api/diff/diff.test.ts b/packages/@aws-cdk/tmp-toolkit-helpers/test/api/diff/diff.test.ts index e57827dee..945703004 100644 --- a/packages/@aws-cdk/tmp-toolkit-helpers/test/api/diff/diff.test.ts +++ b/packages/@aws-cdk/tmp-toolkit-helpers/test/api/diff/diff.test.ts @@ -55,7 +55,7 @@ describe('formatStackDiff', () => { } as any; }); - test('returns no changes when templates are identical', () => { + test('returns no differences when templates are identical', () => { // WHEN const formatter = new DiffFormatter({ ioHelper: mockIoHelper, @@ -76,8 +76,12 @@ describe('formatStackDiff', () => { // THEN expect(result.numStacksWithChanges).toBe(0); - expect(result.formattedDiff).toBe(''); - expect(mockIoDefaultMessages.info).toHaveBeenCalledWith(expect.stringContaining('no differences')); + expect(result.formattedDiff).toBeDefined(); + const sanitizedDiff = result.formattedDiff!.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').trim(); + expect(sanitizedDiff).toBe( + 'Stack test-stack\n' + + 'There were no differences', + ); }); test('formats differences when changes exist', () => { @@ -256,6 +260,7 @@ describe('formatSecurityDiff', () => { expect(result.formattedDiff).toBeDefined(); const sanitizedDiff = result.formattedDiff!.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').trim(); expect(sanitizedDiff).toBe( + 'Stack test-stack\n' + 'IAM Statement Changes\n' + '┌───┬─────────────┬────────┬────────────────┬──────────────────────────────┬───────────┐\n' + '│ │ Resource │ Effect │ Action │ Principal │ Condition │\n' + @@ -291,6 +296,7 @@ describe('formatSecurityDiff', () => { ); const sanitizedDiff = result.formattedDiff!.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').trim(); expect(sanitizedDiff).toBe( + 'Stack test-stack\n' + 'IAM Statement Changes\n' + '┌───┬─────────────┬────────┬────────────────┬──────────────────────────────┬───────────┐\n' + '│ │ Resource │ Effect │ Action │ Principal │ Condition │\n' + diff --git a/packages/aws-cdk/test/commands/diff.test.ts b/packages/aws-cdk/test/commands/diff.test.ts index 390252828..782c421f4 100644 --- a/packages/aws-cdk/test/commands/diff.test.ts +++ b/packages/aws-cdk/test/commands/diff.test.ts @@ -865,7 +865,7 @@ describe('nested stacks', () => { }); // THEN - const plainTextOutput = notifySpy.mock.calls[1][0].message.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').replace(/[ \t]+$/gm, ''); + const plainTextOutput = notifySpy.mock.calls[0][0].message.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').replace(/[ \t]+$/gm, ''); expect(plainTextOutput.trim()).toEqual(`Stack Parent Resources [~] AWS::CloudFormation::Stack AdditionChild @@ -906,7 +906,10 @@ Resources Stack newGrandChild Resources -[+] AWS::Something SomeResource`); +[+] AWS::Something SomeResource + +Stack UnChangedChild +There were no differences`); expect(notifySpy).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining('✨ Number of stacks with differences: 6'), @@ -926,7 +929,7 @@ Resources }); // THEN - const plainTextOutput = notifySpy.mock.calls[2][0].message.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').replace(/[ \t]+$/gm, ''); + const plainTextOutput = notifySpy.mock.calls[1][0].message.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').replace(/[ \t]+$/gm, ''); expect(plainTextOutput.trim()).toEqual(`Stack Parent Resources [~] AWS::CloudFormation::Stack AdditionChild @@ -967,7 +970,10 @@ Resources Stack newGrandChild Resources -[+] AWS::Something SomeResource`); +[+] AWS::Something SomeResource + +Stack UnChangedChild +There were no differences`); expect(notifySpy).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining('✨ Number of stacks with differences: 6'),