Skip to content

Commit b8a9847

Browse files
committed
fix(server): close stdin for noninteractive process runs
1 parent 0d34579 commit b8a9847

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

‎apps/server/src/processRunner.test.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { TestClock } from "effect/testing";
1111
import { ChildProcessSpawner } from "effect/unstable/process";
1212
import { HostProcessEnvironment, HostProcessPlatform } from "@t3tools/shared/hostProcess";
1313
import { SpawnExecutableResolution } from "@t3tools/shared/shell";
14+
import * as NodeServices from "@effect/platform-node/NodeServices";
1415

1516
import * as ProcessRunner from "./processRunner.ts";
1617

@@ -80,6 +81,28 @@ const runWith =
8081
);
8182

8283
describe("runProcess", () => {
84+
for (const stdin of [undefined, "", "hello\n世界\n"]) {
85+
it.effect(
86+
`delivers EOF to a real child with ${stdin === undefined ? "omitted" : stdin === "" ? "empty" : "provided"} stdin`,
87+
() =>
88+
Effect.gen(function* () {
89+
const runner = yield* ProcessRunner.ProcessRunner;
90+
const result = yield* runner.run({
91+
command: process.execPath,
92+
args: [
93+
"-e",
94+
"process.stdin.setEncoding('utf8'); let input = ''; process.stdin.on('data', chunk => { input += chunk }); process.stdin.on('end', () => { process.stdout.write('EOF:' + input) });",
95+
],
96+
...(stdin !== undefined ? { stdin } : {}),
97+
});
98+
expect(result.code).toBe(0);
99+
expect(result.stdout).toBe(`EOF:${stdin ?? ""}`);
100+
expect(result.timedOut).toBe(false);
101+
}).pipe(Effect.provide(ProcessRunner.layer.pipe(Layer.provide(NodeServices.layer)))),
102+
10_000,
103+
);
104+
}
105+
83106
it.effect("collects stdout through an injected ChildProcessSpawner", () =>
84107
Effect.gen(function* () {
85108
const spawner = makeSpawner((command) =>

‎apps/server/src/processRunner.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (
311311
}
312312
: {}),
313313
shell: spawnCommand.shell,
314+
// This runner has no interactive input channel. An unused pipe would
315+
// leave commands waiting for stdin until the process timeout.
316+
stdin: input.stdin === undefined ? "ignore" : "pipe",
314317
}),
315318
)
316319
.pipe(

0 commit comments

Comments
 (0)