Skip to content

Commit 574ce65

Browse files
authored
Merge pull request microsoft#1102 from hogmoru/taskrunner-empty-stderr-fallback-stdout
[rush] On errors, have `rush build` print underlying tool's stdout when stderr is empty
2 parents 463cf93 + 1fb09b5 commit 574ce65

4 files changed

Lines changed: 136 additions & 12 deletions

File tree

apps/rush-lib/src/logic/taskRunner/TaskRunner.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -428,13 +428,13 @@ export class TaskRunner {
428428
}
429429

430430
if (task.writer) {
431-
let stderr: string = task.writer.getStdError();
432-
if (stderr && (task.status === TaskStatus.Failure || task.status === TaskStatus.SuccessWithWarning)) {
433-
stderr = stderr.split(os.EOL)
434-
.map(text => text.trim())
435-
.filter(text => text)
436-
.join(os.EOL);
437-
this._terminal.writeLine(stderr + (i !== tasks.length - 1 ? os.EOL : ''));
431+
const stderr: string = task.writer.getStdError();
432+
const shouldPrintDetails: boolean =
433+
task.status === TaskStatus.Failure || task.status === TaskStatus.SuccessWithWarning;
434+
let details: string = stderr ? stderr : task.writer.getStdOutput();
435+
if (details && shouldPrintDetails) {
436+
details = this._abridgeTaskReport(details);
437+
this._terminal.writeLine(details + (i !== tasks.length - 1 ? os.EOL : ''));
438438
}
439439
}
440440
}
@@ -443,4 +443,21 @@ export class TaskRunner {
443443
}
444444
}
445445

446+
/**
447+
* Remove trailing blanks, and all middle lines if text is large
448+
*/
449+
private _abridgeTaskReport(text: string): string {
450+
const headSize: number = 10;
451+
const tailSize: number = 20;
452+
const margin: number = 10;
453+
const lines: Array<string> = text.split(/\s*\r?\n/).filter(line => line);
454+
if (lines.length < headSize + tailSize + margin) {
455+
return lines.join(os.EOL);
456+
}
457+
const amountRemoved: number = lines.length - headSize - tailSize;
458+
const head: string = lines.splice(0, headSize).join(os.EOL);
459+
const tail: string = lines.splice(-tailSize).join(os.EOL);
460+
return `${head}${os.EOL}[...${amountRemoved} lines omitted...]${os.EOL}${tail}`;
461+
}
462+
446463
}

apps/rush-lib/src/logic/taskRunner/test/TaskRunner.test.ts

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ describe('TaskRunner', () => {
9292
name: 'stdout+stderr',
9393
isIncrementalBuildAllowed: false,
9494
execute: (writer: ITaskWriter) => {
95-
writer.write('Hold my beer...' + EOL);
96-
writer.writeError('Woops' + EOL);
95+
writer.write('Build step 1' + EOL);
96+
writer.writeError('Error: step 1 failed' + EOL);
9797
return Promise.resolve(TaskStatus.Failure);
9898
}
9999
});
@@ -103,8 +103,74 @@ describe('TaskRunner', () => {
103103
.catch(err => {
104104
expect(err.message).toMatchSnapshot();
105105
const allMessages: string = terminalProvider.getOutput();
106-
expect(allMessages).not.toContain('Hold my beer...');
107-
expect(allMessages).toContain('Woops');
106+
expect(allMessages).not.toContain('Build step 1');
107+
expect(allMessages).toContain('Error: step 1 failed');
108+
checkConsoleOutput(terminalProvider);
109+
});
110+
});
111+
112+
it('printedStdoutAfterErrorWithEmptyStderr', () => {
113+
taskRunner.addTask({
114+
name: 'stdout only',
115+
isIncrementalBuildAllowed: false,
116+
execute: (writer: ITaskWriter) => {
117+
writer.write('Build step 1' + EOL);
118+
writer.write('Error: step 1 failed' + EOL);
119+
return Promise.resolve(TaskStatus.Failure);
120+
}
121+
});
122+
return taskRunner
123+
.execute()
124+
.then(() => fail(EXPECTED_FAIL))
125+
.catch(err => {
126+
expect(err.message).toMatchSnapshot();
127+
expect(terminalProvider.getOutput()).toMatch(/Build step 1.*Error: step 1 failed/);
128+
checkConsoleOutput(terminalProvider);
129+
});
130+
});
131+
132+
it('printedAbridgedStdoutAfterErrorWithEmptyStderr', () => {
133+
taskRunner.addTask({
134+
name: 'large stdout only',
135+
isIncrementalBuildAllowed: false,
136+
execute: (writer: ITaskWriter) => {
137+
writer.write(`Building units...${EOL}`);
138+
for (let i: number = 1; i <= 50; i++) {
139+
writer.write(` - unit #${i};${EOL}`);
140+
}
141+
return Promise.resolve(TaskStatus.Failure);
142+
}
143+
});
144+
return taskRunner
145+
.execute()
146+
.then(() => fail(EXPECTED_FAIL))
147+
.catch(err => {
148+
expect(err.message).toMatchSnapshot();
149+
expect(terminalProvider.getOutput())
150+
.toMatch(/Building units.* - unit #1;.* - unit #3;.*lines omitted.* - unit #48;.* - unit #50;/);
151+
checkConsoleOutput(terminalProvider);
152+
});
153+
});
154+
155+
it('preservedLeadingBlanksButTrimmedTrailingBlanks', () => {
156+
taskRunner.addTask({
157+
name: 'large stderr with leading and trailing blanks',
158+
isIncrementalBuildAllowed: false,
159+
execute: (writer: ITaskWriter) => {
160+
writer.writeError(`List of errors: ${EOL}`);
161+
for (let i: number = 1; i <= 50; i++) {
162+
writer.writeError(` - error #${i}; ${EOL}`);
163+
}
164+
return Promise.resolve(TaskStatus.Failure);
165+
}
166+
});
167+
return taskRunner
168+
.execute()
169+
.then(() => fail(EXPECTED_FAIL))
170+
.catch(err => {
171+
expect(err.message).toMatchSnapshot();
172+
expect(terminalProvider.getOutput())
173+
.toMatch(/List of errors:\S.* - error #1;\S.*lines omitted.* - error #48;\S.* - error #50;\S/);
108174
checkConsoleOutput(terminalProvider);
109175
});
110176
});

apps/rush-lib/src/logic/taskRunner/test/__snapshots__/TaskRunner.test.ts.snap

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,42 @@ exports[`TaskRunner Dependencies throwsErrorOnNonExistentDependency 1`] = `"The
2222

2323
exports[`TaskRunner Dependencies throwsErrorOnNonExistentTask 1`] = `"The task 'foo' has not been registered"`;
2424

25+
exports[`TaskRunner Error logging preservedLeadingBlanksButTrimmedTrailingBlanks 1`] = `"Project(s) failed to build"`;
26+
27+
exports[`TaskRunner Error logging preservedLeadingBlanksButTrimmedTrailingBlanks 2`] = `"Registered large stderr with leading and trailing blanks[n]Executing a maximum of 1 simultaneous processes...[-n-][n][x][37m[large stderr with leading and trailing blanks] started[x][39m[n][n][x][31mFAILURE (1)[x][39m[n][x][31m================================[x][39m[n][x][31mlarge stderr with leading and trailing blanks (0.00 seconds)[x][39m[n]List of errors:[-n-] - error #1;[-n-] - error #2;[-n-] - error #3;[-n-] - error #4;[-n-] - error #5;[-n-] - error #6;[-n-] - error #7;[-n-] - error #8;[-n-] - error #9;[-n-][...21 lines omitted...][-n-] - error #31;[-n-] - error #32;[-n-] - error #33;[-n-] - error #34;[-n-] - error #35;[-n-] - error #36;[-n-] - error #37;[-n-] - error #38;[-n-] - error #39;[-n-] - error #40;[-n-] - error #41;[-n-] - error #42;[-n-] - error #43;[-n-] - error #44;[-n-] - error #45;[-n-] - error #46;[-n-] - error #47;[-n-] - error #48;[-n-] - error #49;[-n-] - error #50;[n][x][31m================================[-n-][x][39m[n][n]"`;
28+
29+
exports[`TaskRunner Error logging preservedLeadingBlanksButTrimmedTrailingBlanks 3`] = `""`;
30+
31+
exports[`TaskRunner Error logging preservedLeadingBlanksButTrimmedTrailingBlanks 4`] = `""`;
32+
33+
exports[`TaskRunner Error logging preservedLeadingBlanksButTrimmedTrailingBlanks 5`] = `"[x][31m[-n-]1 of 1: [large stderr with leading and trailing blanks] failed to build![x][39m[n]"`;
34+
35+
exports[`TaskRunner Error logging printedAbridgedStdoutAfterErrorWithEmptyStderr 1`] = `"Project(s) failed to build"`;
36+
37+
exports[`TaskRunner Error logging printedAbridgedStdoutAfterErrorWithEmptyStderr 2`] = `"Registered large stdout only[n]Executing a maximum of 1 simultaneous processes...[-n-][n][x][37m[large stdout only] started[x][39m[n][n][x][31mFAILURE (1)[x][39m[n][x][31m================================[x][39m[n][x][31mlarge stdout only (0.00 seconds)[x][39m[n]Building units...[-n-] - unit #1;[-n-] - unit #2;[-n-] - unit #3;[-n-] - unit #4;[-n-] - unit #5;[-n-] - unit #6;[-n-] - unit #7;[-n-] - unit #8;[-n-] - unit #9;[-n-][...21 lines omitted...][-n-] - unit #31;[-n-] - unit #32;[-n-] - unit #33;[-n-] - unit #34;[-n-] - unit #35;[-n-] - unit #36;[-n-] - unit #37;[-n-] - unit #38;[-n-] - unit #39;[-n-] - unit #40;[-n-] - unit #41;[-n-] - unit #42;[-n-] - unit #43;[-n-] - unit #44;[-n-] - unit #45;[-n-] - unit #46;[-n-] - unit #47;[-n-] - unit #48;[-n-] - unit #49;[-n-] - unit #50;[n][x][31m================================[-n-][x][39m[n][n]"`;
38+
39+
exports[`TaskRunner Error logging printedAbridgedStdoutAfterErrorWithEmptyStderr 3`] = `""`;
40+
41+
exports[`TaskRunner Error logging printedAbridgedStdoutAfterErrorWithEmptyStderr 4`] = `""`;
42+
43+
exports[`TaskRunner Error logging printedAbridgedStdoutAfterErrorWithEmptyStderr 5`] = `"[x][31m[-n-]1 of 1: [large stdout only] failed to build![x][39m[n]"`;
44+
2545
exports[`TaskRunner Error logging printedStderrAfterError 1`] = `"Project(s) failed to build"`;
2646

27-
exports[`TaskRunner Error logging printedStderrAfterError 2`] = `"Registered stdout+stderr[n]Executing a maximum of 1 simultaneous processes...[-n-][n][x][37m[stdout+stderr] started[x][39m[n][n][x][31mFAILURE (1)[x][39m[n][x][31m================================[x][39m[n][x][31mstdout+stderr (0.00 seconds)[x][39m[n]Woops[n][x][31m================================[-n-][x][39m[n][n]"`;
47+
exports[`TaskRunner Error logging printedStderrAfterError 2`] = `"Registered stdout+stderr[n]Executing a maximum of 1 simultaneous processes...[-n-][n][x][37m[stdout+stderr] started[x][39m[n][n][x][31mFAILURE (1)[x][39m[n][x][31m================================[x][39m[n][x][31mstdout+stderr (0.00 seconds)[x][39m[n]Error: step 1 failed[n][x][31m================================[-n-][x][39m[n][n]"`;
2848

2949
exports[`TaskRunner Error logging printedStderrAfterError 3`] = `""`;
3050

3151
exports[`TaskRunner Error logging printedStderrAfterError 4`] = `""`;
3252

3353
exports[`TaskRunner Error logging printedStderrAfterError 5`] = `"[x][31m[-n-]1 of 1: [stdout+stderr] failed to build![x][39m[n]"`;
54+
55+
exports[`TaskRunner Error logging printedStdoutAfterErrorWithEmptyStderr 1`] = `"Project(s) failed to build"`;
56+
57+
exports[`TaskRunner Error logging printedStdoutAfterErrorWithEmptyStderr 2`] = `"Registered stdout only[n]Executing a maximum of 1 simultaneous processes...[-n-][n][x][37m[stdout only] started[x][39m[n][n][x][31mFAILURE (1)[x][39m[n][x][31m================================[x][39m[n][x][31mstdout only (0.00 seconds)[x][39m[n]Build step 1[-n-]Error: step 1 failed[n][x][31m================================[-n-][x][39m[n][n]"`;
58+
59+
exports[`TaskRunner Error logging printedStdoutAfterErrorWithEmptyStderr 3`] = `""`;
60+
61+
exports[`TaskRunner Error logging printedStdoutAfterErrorWithEmptyStderr 4`] = `""`;
62+
63+
exports[`TaskRunner Error logging printedStdoutAfterErrorWithEmptyStderr 5`] = `"[x][31m[-n-]1 of 1: [stdout only] failed to build![x][39m[n]"`;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"comment": "Make `rush build` print stdout if stderr is empty. Improves Webpack support.",
5+
"packageName": "@microsoft/rush",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "hogmoru@users.noreply.github.com"
11+
}

0 commit comments

Comments
 (0)