Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/542-repl-eof-race.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **Fixed**: `orly-repl` with piped stdin (`echo '1 + 1;' | orly-repl ...`, the docker `repl` mode's shape) aborted in-flight entries on EOF and exited 0 with no output — the result never printed and the error text was truncated by `process.exit`. EOF now quits only after pending entries drain, exit lets output flush, and the smoke gains a piped-EOF regression scenario (#542).
22 changes: 22 additions & 0 deletions clients/repl/smoke/smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,28 @@ try {
const code = await exited;
if (code !== 0) throw new Error(`repl exited ${code}\n--- transcript ---\n${transcript}`);

// Piped-EOF regression (#542): the docker/CI shape — all input arrives at
// once and stdin closes immediately, while the first entry is still
// compiling. The entry must complete, print, and exit 0 anyway.
const piped = spawn(process.execPath, ["dist/index.js"], {
stdio: ["pipe", "pipe", "pipe"],
});
let pipedOut = "";
piped.stdout.on("data", (d) => { pipedOut += d; });
piped.stderr.on("data", (d) => { pipedOut += d; });
const pipedExit = new Promise((resolve) => piped.on("exit", resolve));
piped.stdin.end("40 + 2;\n");
const pipedCode = await Promise.race([
pipedExit,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`piped-EOF: no exit\n--- transcript ---\n${pipedOut}`)), STEP_TIMEOUT_MS),
),
]);
if (pipedCode !== 0 || !pipedOut.includes("42\n")) {
piped.kill("SIGKILL");
throw new Error(`piped-EOF: exit ${pipedCode}\n--- transcript ---\n${pipedOut}`);
}

console.log("orly-repl smoke: all checks passed");
process.exit(0);
} catch (e) {
Expand Down
12 changes: 9 additions & 3 deletions clients/repl/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,10 @@ async function main(): Promise<void> {
} catch {
client.close();
}
process.exit(0);
// No process.exit(): it truncates pending pipe writes (a piped consumer
// would lose the last result/error, #542). With stdin, readline, and the
// socket all closed, the event loop drains and node exits on its own.
process.exitCode = 0;
};

const runCommand = async (line: string): Promise<void> => {
Expand Down Expand Up @@ -326,14 +329,17 @@ async function main(): Promise<void> {
prompt();
});

// stdin EOF (piped input, Ctrl-D). Quitting immediately would tear down
// the socket and scratch dir under whatever entry is still compiling
// (#542) — chain it so EOF means "quit after pending entries drain".
rl.on("close", () => {
void quit();
chain = chain.then(() => quit());
});

prompt();
}

main().catch((e) => {
console.error(e instanceof Error ? e.message : String(e));
process.exit(1);
process.exitCode = 1;
});
Loading