From f5a038687ac73027d8a608c34116888670b6f8ff Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 12 Apr 2022 12:15:43 +0200 Subject: [PATCH] fix(cli): stack monitor prints over error messages (#19859) In #19742, we limited the amount of text that the status printer would produce, and tried to get rid of the empty lines at the end of the rewritable block as well. Unfortunately, we scrolled up too far, overwriting the error messages that may occur during resource provisioning. Since these were the only place where error messages were displayed, it is now not possible to see why a stack deployment fails. This PR fixes that. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cloudformation/stack-activity-monitor.ts | 4 +-- packages/aws-cdk/lib/api/util/display.ts | 33 +++++++++---------- .../aws-cdk/test/api/util/display.test.ts | 23 +++++++++++++ 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.ts b/packages/aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.ts index dd422d3f0e6fd..a299811803f92 100644 --- a/packages/aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.ts +++ b/packages/aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.ts @@ -682,7 +682,7 @@ export class CurrentActivityPrinter extends ActivityPrinterBase { // Display in the same block space, otherwise we're going to have silly empty lines. this.block.displayLines(lines); - this.block.removeEmptyLines(lines); + this.block.removeEmptyLines(); } private progressBar(width: number) { @@ -745,7 +745,7 @@ function colorFromStatusActivity(status?: string) { return chalk.red; } - if (status.startsWith('CREATE_') || status.startsWith('UPDATE_')) { + if (status.startsWith('CREATE_') || status.startsWith('UPDATE_') || status.startsWith('IMPORT_')) { return chalk.green; } // For stacks, it may also be 'UPDDATE_ROLLBACK_IN_PROGRESS' diff --git a/packages/aws-cdk/lib/api/util/display.ts b/packages/aws-cdk/lib/api/util/display.ts index a7071ed6a12a4..44e5b6c9fb1c0 100644 --- a/packages/aws-cdk/lib/api/util/display.ts +++ b/packages/aws-cdk/lib/api/util/display.ts @@ -7,6 +7,7 @@ const wrapAnsi = require('wrap-ansi'); */ export class RewritableBlock { private lastHeight = 0; + private trailingEmptyLines = 0; constructor(private readonly stream: NodeJS.WriteStream) { } @@ -22,14 +23,18 @@ export class RewritableBlock { } public displayLines(lines: string[]) { - lines = terminalWrap(this.width, expandNewlines(lines)).slice(0, getMaxBlockHeight(this.height, this.lastHeight, lines)); + lines = terminalWrap(this.width, expandNewlines(lines)); + lines = lines.slice(0, getMaxBlockHeight(this.height, this.lastHeight, lines)); this.stream.write(cursorUp(this.lastHeight)); for (const line of lines) { this.stream.write(cll() + line + '\n'); } + + this.trailingEmptyLines = Math.max(0, this.lastHeight - lines.length); + // Clear remainder of unwritten lines - for (let i = 0; i < this.lastHeight - lines.length; i++) { + for (let i = 0; i < this.trailingEmptyLines; i++) { this.stream.write(cll() + '\n'); } @@ -37,8 +42,8 @@ export class RewritableBlock { this.lastHeight = Math.max(this.lastHeight, lines.length); } - public removeEmptyLines(lines: string[]) { - this.stream.write(cursorUp(this.lastHeight - lines.length)); + public removeEmptyLines() { + this.stream.write(cursorUp(this.trailingEmptyLines)); } } @@ -62,26 +67,18 @@ function cll() { function terminalWrap(width: number | undefined, lines: string[]) { if (width === undefined) { return lines; } - const ret = new Array(); - for (const line of lines) { - ret.push(...wrapAnsi(line, width - 1, { - hard: true, - trim: true, - wordWrap: false, - }).split('\n')); - } - return ret; + return lines.flatMap(line => wrapAnsi(line, width - 1, { + hard: true, + trim: true, + wordWrap: false, + }).split('\n')); } /** * Make sure there are no hidden newlines in the gin strings */ function expandNewlines(lines: string[]): string[] { - const ret = new Array(); - for (const line of lines) { - ret.push(...line.split('\n')); - } - return ret; + return lines.flatMap(line => line.split('\n')); } function getMaxBlockHeight(windowHeight: number | undefined, lastHeight: number, lines: string[]): number { diff --git a/packages/aws-cdk/test/api/util/display.test.ts b/packages/aws-cdk/test/api/util/display.test.ts index 26b54b736b104..4d116078f18df 100644 --- a/packages/aws-cdk/test/api/util/display.test.ts +++ b/packages/aws-cdk/test/api/util/display.test.ts @@ -36,4 +36,27 @@ describe('Rewritable Block Tests', () => { expect(output.length).toEqual(6); }); + + test('display accounts for newlines in output', () => { + const output = stderr.inspectSync(() => { + block.displayLines(['before\nafter']); + }); + expect(output.length).toEqual(3); // cursorup + 2 lines + }); + + test('removeEmptyLines only removes trailing lines', () => { + stderr.inspectSync(() => { + block.displayLines(Array.from(Array(5).keys()).map(x => `${x}`)); + }); + stderr.inspectSync(() => { + // Leaves 3 empty lines + block.displayLines(Array.from(Array(2).keys()).map(x => `${x}`)); + }); + + const output = stderr.inspectSync(() => { + block.removeEmptyLines(); + }); + const expectedEmptyLines = 3; + expect(JSON.stringify(output)).toEqual(JSON.stringify([`\u001b[${expectedEmptyLines}A`])); + }); }); \ No newline at end of file