fix(opencode): bound shell output after exit
This commit is contained in:
parent
849c2598ab
commit
cb1cef6747
4 changed files with 101 additions and 5 deletions
|
|
@ -268,17 +268,16 @@ export const make = Effect.gen(function* () {
|
|||
Effect.callback<readonly [NodeChildProcess.ChildProcess, ExitSignal], PlatformError.PlatformError>((resume) => {
|
||||
const signal = Deferred.makeUnsafe<readonly [code: number | null, signal: NodeJS.Signals | null]>()
|
||||
const proc = launch(command.command, command.args, opts)
|
||||
let end = false
|
||||
let exit: readonly [code: number | null, signal: NodeJS.Signals | null] | undefined
|
||||
proc.on("error", (err) => {
|
||||
resume(Effect.fail(toPlatformError("spawn", err, command)))
|
||||
})
|
||||
proc.on("exit", (...args) => {
|
||||
exit = args
|
||||
Deferred.doneUnsafe(signal, Exit.succeed(args))
|
||||
})
|
||||
proc.on("close", (...args) => {
|
||||
if (end) return
|
||||
end = true
|
||||
// cross-spawn can suppress `exit` for a Windows ENOENT and only emit `close`.
|
||||
Deferred.doneUnsafe(signal, Exit.succeed(exit ?? args))
|
||||
})
|
||||
proc.on("spawn", () => {
|
||||
|
|
|
|||
|
|
@ -285,6 +285,45 @@ describe("cross-spawn spawner", () => {
|
|||
expect(running).toBe(false)
|
||||
}),
|
||||
)
|
||||
|
||||
fx.effect(
|
||||
"resolves exit before inherited output pipes close",
|
||||
Effect.gen(function* () {
|
||||
const tmp = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => tmpdir()),
|
||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
||||
)
|
||||
const file = path.join(tmp.path, "child.pid")
|
||||
const child = "setInterval(() => {}, 10_000)"
|
||||
const handle = yield* ChildProcess.make(process.execPath, [
|
||||
"-e",
|
||||
[
|
||||
'const { spawn } = require("node:child_process")',
|
||||
'const { writeFileSync } = require("node:fs")',
|
||||
`const child = spawn(process.execPath, ["-e", ${JSON.stringify(child)}], { detached: true, stdio: "inherit" })`,
|
||||
"child.unref()",
|
||||
`writeFileSync(${JSON.stringify(file)}, String(child.pid))`,
|
||||
].join("\n"),
|
||||
])
|
||||
const code = yield* handle.exitCode.pipe(
|
||||
Effect.timeoutOrElse({
|
||||
duration: "2 seconds",
|
||||
orElse: () => Effect.die("exitCode waited for inherited output pipes"),
|
||||
}),
|
||||
)
|
||||
const pid = Number(yield* Effect.promise(() => fs.readFile(file, "utf8")))
|
||||
yield* Effect.addFinalizer(() =>
|
||||
Effect.sync(() => {
|
||||
try {
|
||||
process.kill(pid, "SIGKILL")
|
||||
} catch {}
|
||||
}),
|
||||
)
|
||||
|
||||
expect(code).toBe(ChildProcessSpawner.ExitCode(0))
|
||||
expect(yield* handle.isRunning).toBe(false)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe("error handling", () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue