From 9361965db91ca9f63b24bbd2068fb4a66fbfd464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 4 Mar 2026 17:33:29 +0100 Subject: [PATCH 1/2] Fix runCheck to ignore stdio of spawned process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 Signed-off-by: Fredrik Adelöw --- .changeset/cli-common-runcheck-stdio-ignore.md | 5 +++++ packages/cli-common/src/run.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/cli-common-runcheck-stdio-ignore.md diff --git a/.changeset/cli-common-runcheck-stdio-ignore.md b/.changeset/cli-common-runcheck-stdio-ignore.md new file mode 100644 index 0000000000..1d2986eaf0 --- /dev/null +++ b/.changeset/cli-common-runcheck-stdio-ignore.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli-common': patch +--- + +Fixed `runCheck` to ignore stdio of the spawned process, preventing unwanted output from leaking to the terminal. diff --git a/packages/cli-common/src/run.ts b/packages/cli-common/src/run.ts index 23e71460e4..0e95bd517b 100644 --- a/packages/cli-common/src/run.ts +++ b/packages/cli-common/src/run.ts @@ -211,7 +211,7 @@ export async function runOutput( */ export async function runCheck(args: string[]): Promise { try { - await run(args).waitForExit(); + await run(args, { stdio: 'ignore' }).waitForExit(); return true; } catch { return false; From 918b697cbd6d443afa5dc63b6e18f9c297ac607a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 4 Mar 2026 17:41:08 +0100 Subject: [PATCH 2/2] Add test verifying runCheck does not leak stdio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 Signed-off-by: Fredrik Adelöw --- packages/cli-common/src/run.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/cli-common/src/run.test.ts b/packages/cli-common/src/run.test.ts index 01c6c915d6..f38c1659ba 100644 --- a/packages/cli-common/src/run.test.ts +++ b/packages/cli-common/src/run.test.ts @@ -338,5 +338,26 @@ describe('run', () => { const result = await runCheck(['nonexistent-command-12345']); expect(result).toBe(false); }); + + it('should not leak stdout or stderr from the child process', async () => { + const stdoutSpy = jest.spyOn(process.stdout, 'write'); + const stderrSpy = jest.spyOn(process.stderr, 'write'); + const stdoutBefore = stdoutSpy.mock.calls.length; + const stderrBefore = stderrSpy.mock.calls.length; + + await runCheck([ + 'node', + '--eval', + 'console.log("leaked stdout"); console.error("leaked stderr")', + ]); + + const stdoutCalls = stdoutSpy.mock.calls.slice(stdoutBefore); + const stderrCalls = stderrSpy.mock.calls.slice(stderrBefore); + const stdout = stdoutCalls.map(c => String(c[0])).join(''); + const stderr = stderrCalls.map(c => String(c[0])).join(''); + + expect(stdout).not.toContain('leaked stdout'); + expect(stderr).not.toContain('leaked stderr'); + }); }); });