From b166995d5d6aff55cb71e78a1ed8a33f0f9301db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:28:25 +0000 Subject: [PATCH 1/6] fix: add preventAutoclose to ProblemCollector, inject strip-colors transform, make line/column optional in patterns, always wire problemCollector Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/d3363f24-2939-4850-85fb-b8b3eafa7a1e --- libraries/node-core-library/src/FileError.ts | 4 ++-- .../operations/OperationExecutionRecord.ts | 21 +++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/libraries/node-core-library/src/FileError.ts b/libraries/node-core-library/src/FileError.ts index 32fac1b5742..0e07ecf3719 100644 --- a/libraries/node-core-library/src/FileError.ts +++ b/libraries/node-core-library/src/FileError.ts @@ -50,7 +50,7 @@ const uuidFileError: string = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1'; const baseFolderEnvVar: string = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER'; const unixProblemMatcherPattern: IProblemPattern = { - regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+):(\\d+)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:\\s]+)(?::(\\d+)(?::(\\d+))?)?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -61,7 +61,7 @@ const unixProblemMatcherPattern: IProblemPattern = { const vsProblemMatcherPattern: IProblemPattern = { regexp: - '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+),(\\d+)\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(\\s]+)(?:\\((\\d+)(?:,(\\d+))?\\))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, diff --git a/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts b/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts index 63da9421b54..301690ff933 100644 --- a/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts +++ b/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts @@ -114,6 +114,8 @@ export class OperationExecutionRecord implements IOperationRunnerContext, IOpera preventAutoclose: true }); public readonly problemCollector: ProblemCollector = new ProblemCollector({ + // Allow writing to this object after transforms have been closed. We clean it up manually in a finally block. + preventAutoclose: true, matcherJson: [ { name: 'rushstack-file-error-unix', @@ -322,14 +324,21 @@ export class OperationExecutionRecord implements IOperationRunnerContext, IOpera // // +--> quietModeTransform? --> collatedWriter // | - // normalizeNewlineTransform --1--> stderrLineTransform --2--> projectLogWritable + // normalizeNewlineTransform --1--> stderrLineTransform --2--> projectLogWritable? // | // +--> stdioSummarizer - const destination: TerminalWritable = projectLogWritable - ? new SplitterTransform({ - destinations: [projectLogWritable, stdioSummarizer, problemCollector] - }) - : stdioSummarizer; + // | + // +--> removeColorsTransform --> problemCollector + const removeColorsTransform: TextRewriterTransform = new TextRewriterTransform({ + destination: problemCollector, + removeColors: true + }); + + const destination: TerminalWritable = new SplitterTransform({ + destinations: projectLogWritable + ? [projectLogWritable, stdioSummarizer, removeColorsTransform] + : [stdioSummarizer, removeColorsTransform] + }); const stderrLineTransform: StderrLineTransform = new StderrLineTransform({ destination, From 99ad6759db4e5ab92f41ea0080719bbfdd41df35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:32:47 +0000 Subject: [PATCH 2/6] fix: use non-greedy matching in FileError problem matcher patterns to handle paths with spaces Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/d3363f24-2939-4850-85fb-b8b3eafa7a1e --- libraries/node-core-library/src/FileError.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/node-core-library/src/FileError.ts b/libraries/node-core-library/src/FileError.ts index 0e07ecf3719..ae64da24858 100644 --- a/libraries/node-core-library/src/FileError.ts +++ b/libraries/node-core-library/src/FileError.ts @@ -50,7 +50,7 @@ const uuidFileError: string = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1'; const baseFolderEnvVar: string = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER'; const unixProblemMatcherPattern: IProblemPattern = { - regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:\\s]+)(?::(\\d+)(?::(\\d+))?)?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+?)(?::(\\d+)(?::(\\d+))?)?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -61,7 +61,7 @@ const unixProblemMatcherPattern: IProblemPattern = { const vsProblemMatcherPattern: IProblemPattern = { regexp: - '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(\\s]+)(?:\\((\\d+)(?:,(\\d+))?\\))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+?)(?:\\((\\d+)(?:,(\\d+))?\\))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, From 3fe471a27a095182623e310f524e9548b6036041 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:40:10 +0000 Subject: [PATCH 3/6] fix: default line and column to 1 in FileError.getFormattedErrorMessage; revert optional regex groups Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/191532d8-1356-4937-9fe4-bddc8b3fdcaf --- libraries/node-core-library/src/FileError.ts | 8 ++-- .../src/test/FileError.test.ts | 38 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/libraries/node-core-library/src/FileError.ts b/libraries/node-core-library/src/FileError.ts index ae64da24858..7857bc8b036 100644 --- a/libraries/node-core-library/src/FileError.ts +++ b/libraries/node-core-library/src/FileError.ts @@ -50,7 +50,7 @@ const uuidFileError: string = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1'; const baseFolderEnvVar: string = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER'; const unixProblemMatcherPattern: IProblemPattern = { - regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+?)(?::(\\d+)(?::(\\d+))?)?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+):(\\d+)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -61,7 +61,7 @@ const unixProblemMatcherPattern: IProblemPattern = { const vsProblemMatcherPattern: IProblemPattern = { regexp: - '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+?)(?:\\((\\d+)(?:,(\\d+))?\\))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+),(\\d+)\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -145,8 +145,8 @@ export class FileError extends Error { baseFolder: this._evaluateBaseFolder(), pathToFormat: this.absolutePath, message: this.message, - line: this.line, - column: this.column + line: this.line ?? 1, + column: this.column ?? 1 }); } diff --git a/libraries/node-core-library/src/test/FileError.test.ts b/libraries/node-core-library/src/test/FileError.test.ts index 03887d624fd..9daee44a6b5 100644 --- a/libraries/node-core-library/src/test/FileError.test.ts +++ b/libraries/node-core-library/src/test/FileError.test.ts @@ -29,7 +29,7 @@ describe(FileError.name, () => { absolutePath: `/path/to/project/path/to/file`, projectFolder: '/path/to/project' }); - expect(error1.toString()).toMatchInlineSnapshot(`"path/to/file - message"`); + expect(error1.toString()).toMatchInlineSnapshot(`"path/to/file:1:1 - message"`); }); it('correctly performs Unix-style relative file path formatting', () => { @@ -49,7 +49,7 @@ describe(FileError.name, () => { line: 5 }); expect(error2.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file:5 - message"` + `"path/to/file:5:1 - message"` ); const error3: FileError = new FileError('message', { @@ -59,7 +59,7 @@ describe(FileError.name, () => { column: 12 }); expect(error3.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file - message"` + `"path/to/file:1:1 - message"` ); const error4: FileError = new FileError('message', { @@ -67,7 +67,7 @@ describe(FileError.name, () => { projectFolder: '/path/to/project' }); expect(error4.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file - message"` + `"path/to/file:1:1 - message"` ); }); @@ -93,8 +93,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error2Message: string = error2.getFormattedErrorMessage({ format: 'Unix' }); - expect(error2Message).toMatch(/.+:5 - message$/); - const error2Path: string = error2Message.slice(0, error2Message.length - ':5 - message'.length); + expect(error2Message).toMatch(/.+:5:1 - message$/); + const error2Path: string = error2Message.slice(0, error2Message.length - ':5:1 - message'.length); expect(path.isAbsolute(error2Path)).toEqual(true); const error3: FileError = new FileError('message', { @@ -106,8 +106,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error3Message: string = error3.getFormattedErrorMessage({ format: 'Unix' }); - expect(error3Message).toMatch(/.+ - message$/); - const error3Path: string = error3Message.slice(0, error3Message.length - ' - message'.length); + expect(error3Message).toMatch(/.+:1:1 - message$/); + const error3Path: string = error3Message.slice(0, error3Message.length - ':1:1 - message'.length); expect(path.isAbsolute(error3Path)).toEqual(true); const error4: FileError = new FileError('message', { @@ -117,8 +117,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error4Message: string = error4.getFormattedErrorMessage({ format: 'Unix' }); - expect(error4Message).toMatch(/.+ - message$/); - const error4Path: string = error4Message.slice(0, error4Message.length - ' - message'.length); + expect(error4Message).toMatch(/.+:1:1 - message$/); + const error4Path: string = error4Message.slice(0, error4Message.length - ':1:1 - message'.length); expect(path.isAbsolute(error4Path)).toEqual(true); }); @@ -139,7 +139,7 @@ describe(FileError.name, () => { line: 5 }); expect(error2.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file(5) - message"` + `"path/to/file(5,1) - message"` ); const error3: FileError = new FileError('message', { @@ -149,7 +149,7 @@ describe(FileError.name, () => { column: 12 }); expect(error3.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file - message"` + `"path/to/file(1,1) - message"` ); const error4: FileError = new FileError('message', { @@ -157,7 +157,7 @@ describe(FileError.name, () => { projectFolder: '/path/to/project' }); expect(error4.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file - message"` + `"path/to/file(1,1) - message"` ); }); @@ -183,8 +183,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error2Message: string = error2.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error2Message).toMatch(/.+\(5\) - message$/); - const error2Path: string = error2Message.slice(0, error2Message.length - '(5) - message'.length); + expect(error2Message).toMatch(/.+\(5,1\) - message$/); + const error2Path: string = error2Message.slice(0, error2Message.length - '(5,1) - message'.length); expect(path.isAbsolute(error2Path)).toEqual(true); const error3: FileError = new FileError('message', { @@ -196,8 +196,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error3Message: string = error3.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error3Message).toMatch(/.+ - message$/); - const error3Path: string = error3Message.slice(0, error3Message.length - ' - message'.length); + expect(error3Message).toMatch(/.+\(1,1\) - message$/); + const error3Path: string = error3Message.slice(0, error3Message.length - '(1,1) - message'.length); expect(path.isAbsolute(error3Path)).toEqual(true); const error4: FileError = new FileError('message', { @@ -207,8 +207,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error4Message: string = error4.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error4Message).toMatch(/.+ - message$/); - const error4Path: string = error4Message.slice(0, error4Message.length - ' - message'.length); + expect(error4Message).toMatch(/.+\(1,1\) - message$/); + const error4Path: string = error4Message.slice(0, error4Message.length - '(1,1) - message'.length); expect(path.isAbsolute(error4Path)).toEqual(true); }); }); From 2e8acaa5d38c62f81d3adb6c0191051ea768c92c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:45:44 +0000 Subject: [PATCH 4/6] fix: line mandatory (defaults to 1), column optional in FileError.getFormattedErrorMessage Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/e0b803db-46de-4bf5-a531-31105741fbf8 --- libraries/node-core-library/src/FileError.ts | 6 +-- .../src/test/FileError.test.ts | 38 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/libraries/node-core-library/src/FileError.ts b/libraries/node-core-library/src/FileError.ts index 7857bc8b036..5d7e3a1005a 100644 --- a/libraries/node-core-library/src/FileError.ts +++ b/libraries/node-core-library/src/FileError.ts @@ -50,7 +50,7 @@ const uuidFileError: string = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1'; const baseFolderEnvVar: string = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER'; const unixProblemMatcherPattern: IProblemPattern = { - regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+):(\\d+)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+)(?::(\\d+))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -61,7 +61,7 @@ const unixProblemMatcherPattern: IProblemPattern = { const vsProblemMatcherPattern: IProblemPattern = { regexp: - '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+),(\\d+)\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+)(?:,(\\d+))?\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -146,7 +146,7 @@ export class FileError extends Error { pathToFormat: this.absolutePath, message: this.message, line: this.line ?? 1, - column: this.column ?? 1 + column: this.column }); } diff --git a/libraries/node-core-library/src/test/FileError.test.ts b/libraries/node-core-library/src/test/FileError.test.ts index 9daee44a6b5..35c49e885d2 100644 --- a/libraries/node-core-library/src/test/FileError.test.ts +++ b/libraries/node-core-library/src/test/FileError.test.ts @@ -29,7 +29,7 @@ describe(FileError.name, () => { absolutePath: `/path/to/project/path/to/file`, projectFolder: '/path/to/project' }); - expect(error1.toString()).toMatchInlineSnapshot(`"path/to/file:1:1 - message"`); + expect(error1.toString()).toMatchInlineSnapshot(`"path/to/file:1 - message"`); }); it('correctly performs Unix-style relative file path formatting', () => { @@ -49,7 +49,7 @@ describe(FileError.name, () => { line: 5 }); expect(error2.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file:5:1 - message"` + `"path/to/file:5 - message"` ); const error3: FileError = new FileError('message', { @@ -59,7 +59,7 @@ describe(FileError.name, () => { column: 12 }); expect(error3.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file:1:1 - message"` + `"path/to/file:1:12 - message"` ); const error4: FileError = new FileError('message', { @@ -67,7 +67,7 @@ describe(FileError.name, () => { projectFolder: '/path/to/project' }); expect(error4.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file:1:1 - message"` + `"path/to/file:1 - message"` ); }); @@ -93,8 +93,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error2Message: string = error2.getFormattedErrorMessage({ format: 'Unix' }); - expect(error2Message).toMatch(/.+:5:1 - message$/); - const error2Path: string = error2Message.slice(0, error2Message.length - ':5:1 - message'.length); + expect(error2Message).toMatch(/.+:5 - message$/); + const error2Path: string = error2Message.slice(0, error2Message.length - ':5 - message'.length); expect(path.isAbsolute(error2Path)).toEqual(true); const error3: FileError = new FileError('message', { @@ -106,8 +106,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error3Message: string = error3.getFormattedErrorMessage({ format: 'Unix' }); - expect(error3Message).toMatch(/.+:1:1 - message$/); - const error3Path: string = error3Message.slice(0, error3Message.length - ':1:1 - message'.length); + expect(error3Message).toMatch(/.+:1:12 - message$/); + const error3Path: string = error3Message.slice(0, error3Message.length - ':1:12 - message'.length); expect(path.isAbsolute(error3Path)).toEqual(true); const error4: FileError = new FileError('message', { @@ -117,8 +117,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error4Message: string = error4.getFormattedErrorMessage({ format: 'Unix' }); - expect(error4Message).toMatch(/.+:1:1 - message$/); - const error4Path: string = error4Message.slice(0, error4Message.length - ':1:1 - message'.length); + expect(error4Message).toMatch(/.+:1 - message$/); + const error4Path: string = error4Message.slice(0, error4Message.length - ':1 - message'.length); expect(path.isAbsolute(error4Path)).toEqual(true); }); @@ -139,7 +139,7 @@ describe(FileError.name, () => { line: 5 }); expect(error2.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file(5,1) - message"` + `"path/to/file(5) - message"` ); const error3: FileError = new FileError('message', { @@ -149,7 +149,7 @@ describe(FileError.name, () => { column: 12 }); expect(error3.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file(1,1) - message"` + `"path/to/file(1,12) - message"` ); const error4: FileError = new FileError('message', { @@ -157,7 +157,7 @@ describe(FileError.name, () => { projectFolder: '/path/to/project' }); expect(error4.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file(1,1) - message"` + `"path/to/file(1) - message"` ); }); @@ -183,8 +183,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error2Message: string = error2.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error2Message).toMatch(/.+\(5,1\) - message$/); - const error2Path: string = error2Message.slice(0, error2Message.length - '(5,1) - message'.length); + expect(error2Message).toMatch(/.+\(5\) - message$/); + const error2Path: string = error2Message.slice(0, error2Message.length - '(5) - message'.length); expect(path.isAbsolute(error2Path)).toEqual(true); const error3: FileError = new FileError('message', { @@ -196,8 +196,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error3Message: string = error3.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error3Message).toMatch(/.+\(1,1\) - message$/); - const error3Path: string = error3Message.slice(0, error3Message.length - '(1,1) - message'.length); + expect(error3Message).toMatch(/.+\(1,12\) - message$/); + const error3Path: string = error3Message.slice(0, error3Message.length - '(1,12) - message'.length); expect(path.isAbsolute(error3Path)).toEqual(true); const error4: FileError = new FileError('message', { @@ -207,8 +207,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error4Message: string = error4.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error4Message).toMatch(/.+\(1,1\) - message$/); - const error4Path: string = error4Message.slice(0, error4Message.length - '(1,1) - message'.length); + expect(error4Message).toMatch(/.+\(1\) - message$/); + const error4Path: string = error4Message.slice(0, error4Message.length - '(1) - message'.length); expect(path.isAbsolute(error4Path)).toEqual(true); }); }); From 2de5738ad43b2a3bb674b043aa13082c2cc6dc8c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:48:21 +0000 Subject: [PATCH 5/6] fix: update problem matcher regexes in rush-vscode-extension package.json to make column optional Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/8b2eb275-c191-44e9-8d69-dd1c166c7985 --- vscode-extensions/rush-vscode-extension/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vscode-extensions/rush-vscode-extension/package.json b/vscode-extensions/rush-vscode-extension/package.json index cc524ace78e..e7df616b98c 100644 --- a/vscode-extensions/rush-vscode-extension/package.json +++ b/vscode-extensions/rush-vscode-extension/package.json @@ -228,7 +228,7 @@ ], "applyTo": "allDocuments", "pattern": { - "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+):(\\d+)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", + "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+)(?::(\\d+))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", "severity": 1, "file": 2, "line": 3, @@ -247,7 +247,7 @@ ], "applyTo": "allDocuments", "pattern": { - "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+),(\\d+)\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", + "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+)(?:,(\\d+))?\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", "severity": 1, "file": 2, "line": 3, From 3cfd6ed729913b262599eaa9fce0a76a3e0c1f12 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:39:17 +0000 Subject: [PATCH 6/6] fix: scope PR to OperationExecutionRecord only; revert FileError changes; add @microsoft/rush patch change file Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/1bcedf3e-f266-4978-ad80-c7ccb325056a --- ...m-collector-pipeline_2026-03-20-22-38.json | 10 +++++++ libraries/node-core-library/src/FileError.ts | 6 ++--- .../src/test/FileError.test.ts | 26 +++++++++---------- .../rush-vscode-extension/package.json | 4 +-- 4 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 common/changes/@microsoft/rush/fix-problem-collector-pipeline_2026-03-20-22-38.json diff --git a/common/changes/@microsoft/rush/fix-problem-collector-pipeline_2026-03-20-22-38.json b/common/changes/@microsoft/rush/fix-problem-collector-pipeline_2026-03-20-22-38.json new file mode 100644 index 00000000000..84060c0c50b --- /dev/null +++ b/common/changes/@microsoft/rush/fix-problem-collector-pipeline_2026-03-20-22-38.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Fix ProblemCollector wiring in OperationExecutionRecord: add preventAutoclose, strip ANSI colors before matching, and always include problemCollector in the terminal pipeline", + "type": "patch" + } + ], + "packageName": "@microsoft/rush" +} diff --git a/libraries/node-core-library/src/FileError.ts b/libraries/node-core-library/src/FileError.ts index 5d7e3a1005a..32fac1b5742 100644 --- a/libraries/node-core-library/src/FileError.ts +++ b/libraries/node-core-library/src/FileError.ts @@ -50,7 +50,7 @@ const uuidFileError: string = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1'; const baseFolderEnvVar: string = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER'; const unixProblemMatcherPattern: IProblemPattern = { - regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+)(?::(\\d+))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+):(\\d+)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -61,7 +61,7 @@ const unixProblemMatcherPattern: IProblemPattern = { const vsProblemMatcherPattern: IProblemPattern = { regexp: - '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+)(?:,(\\d+))?\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+),(\\d+)\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', severity: 1, file: 2, line: 3, @@ -145,7 +145,7 @@ export class FileError extends Error { baseFolder: this._evaluateBaseFolder(), pathToFormat: this.absolutePath, message: this.message, - line: this.line ?? 1, + line: this.line, column: this.column }); } diff --git a/libraries/node-core-library/src/test/FileError.test.ts b/libraries/node-core-library/src/test/FileError.test.ts index 35c49e885d2..03887d624fd 100644 --- a/libraries/node-core-library/src/test/FileError.test.ts +++ b/libraries/node-core-library/src/test/FileError.test.ts @@ -29,7 +29,7 @@ describe(FileError.name, () => { absolutePath: `/path/to/project/path/to/file`, projectFolder: '/path/to/project' }); - expect(error1.toString()).toMatchInlineSnapshot(`"path/to/file:1 - message"`); + expect(error1.toString()).toMatchInlineSnapshot(`"path/to/file - message"`); }); it('correctly performs Unix-style relative file path formatting', () => { @@ -59,7 +59,7 @@ describe(FileError.name, () => { column: 12 }); expect(error3.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file:1:12 - message"` + `"path/to/file - message"` ); const error4: FileError = new FileError('message', { @@ -67,7 +67,7 @@ describe(FileError.name, () => { projectFolder: '/path/to/project' }); expect(error4.getFormattedErrorMessage({ format: 'Unix' })).toMatchInlineSnapshot( - `"path/to/file:1 - message"` + `"path/to/file - message"` ); }); @@ -106,8 +106,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error3Message: string = error3.getFormattedErrorMessage({ format: 'Unix' }); - expect(error3Message).toMatch(/.+:1:12 - message$/); - const error3Path: string = error3Message.slice(0, error3Message.length - ':1:12 - message'.length); + expect(error3Message).toMatch(/.+ - message$/); + const error3Path: string = error3Message.slice(0, error3Message.length - ' - message'.length); expect(path.isAbsolute(error3Path)).toEqual(true); const error4: FileError = new FileError('message', { @@ -117,8 +117,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error4Message: string = error4.getFormattedErrorMessage({ format: 'Unix' }); - expect(error4Message).toMatch(/.+:1 - message$/); - const error4Path: string = error4Message.slice(0, error4Message.length - ':1 - message'.length); + expect(error4Message).toMatch(/.+ - message$/); + const error4Path: string = error4Message.slice(0, error4Message.length - ' - message'.length); expect(path.isAbsolute(error4Path)).toEqual(true); }); @@ -149,7 +149,7 @@ describe(FileError.name, () => { column: 12 }); expect(error3.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file(1,12) - message"` + `"path/to/file - message"` ); const error4: FileError = new FileError('message', { @@ -157,7 +157,7 @@ describe(FileError.name, () => { projectFolder: '/path/to/project' }); expect(error4.getFormattedErrorMessage({ format: 'VisualStudio' })).toMatchInlineSnapshot( - `"path/to/file(1) - message"` + `"path/to/file - message"` ); }); @@ -196,8 +196,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error3Message: string = error3.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error3Message).toMatch(/.+\(1,12\) - message$/); - const error3Path: string = error3Message.slice(0, error3Message.length - '(1,12) - message'.length); + expect(error3Message).toMatch(/.+ - message$/); + const error3Path: string = error3Message.slice(0, error3Message.length - ' - message'.length); expect(path.isAbsolute(error3Path)).toEqual(true); const error4: FileError = new FileError('message', { @@ -207,8 +207,8 @@ describe(FileError.name, () => { // Because the file path is resolved on disk, the output will vary based on platform. // Only check that it ends as expected and is an absolute path. const error4Message: string = error4.getFormattedErrorMessage({ format: 'VisualStudio' }); - expect(error4Message).toMatch(/.+\(1\) - message$/); - const error4Path: string = error4Message.slice(0, error4Message.length - '(1) - message'.length); + expect(error4Message).toMatch(/.+ - message$/); + const error4Path: string = error4Message.slice(0, error4Message.length - ' - message'.length); expect(path.isAbsolute(error4Path)).toEqual(true); }); }); diff --git a/vscode-extensions/rush-vscode-extension/package.json b/vscode-extensions/rush-vscode-extension/package.json index e7df616b98c..cc524ace78e 100644 --- a/vscode-extensions/rush-vscode-extension/package.json +++ b/vscode-extensions/rush-vscode-extension/package.json @@ -228,7 +228,7 @@ ], "applyTo": "allDocuments", "pattern": { - "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+)(?::(\\d+))?\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", + "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+):(\\d+)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", "severity": 1, "file": 2, "line": 3, @@ -247,7 +247,7 @@ ], "applyTo": "allDocuments", "pattern": { - "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+)(?:,(\\d+))?\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", + "regexp": "^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+),(\\d+)\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$", "severity": 1, "file": 2, "line": 3,