Bun.spawn: truncate stdout/stderr when redirected to Bun.file(path)
What does this PR do?
Bun.spawn({ stdout: Bun.file(path) }) (and spawnSync, and stderr:) opened the destination with O_WRONLY|O_CREAT but without O_TRUNC. If the file already existed and the child wrote fewer bytes than were already there, the stale tail of the old content was left in place.
writeFileSync(p, "OLD-CONTENT-0123456789ABCDEF");
await Bun.spawn([bunExe(), "-e", "process.stdout.write('NEW')"], { stdout: Bun.file(p) }).exited;
readFileSync(p, "utf8");
// before: "NEW-CONTENT-0123456789ABCDEF"
// after: "NEW"
Every other "write these bytes to this path" surface already truncates: Bun.write(path, data), Bun.$\cmd > ${path}`, and fs.openSync(path, 'w')`. This brings the spawn redirect in line.
How did you verify your code works?
New tests in test/js/bun/spawn/spawn.test.ts for spawn stdout/stderr and spawnSync stdout. Each pre-fills the destination with a longer string and asserts the exact short output afterwards.
(fail -> pass) gcTick > spawnSync > Bun.file() as stdout truncates the destination
(fail -> pass) gcTick > spawn > Bun.file() as stdout truncates the destination
(fail -> pass) gcTick > spawn > Bun.file() as stderr truncates the destination
Existing Bun.file() works as stdin and Bun.file(path) at index >= 3 is readable in the child still pass: stdin stays O_RDONLY and extra stdio slots stay O_RDWR|O_CREAT (no truncation), since the established use case there is reading from the file in the child.
Fix applied on both the posix_spawn path (src/spawn_sys/spawn_process.rs) and the Windows libuv path (src/spawn/process.rs).
no test proof · iteration 3 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/bun/spawn/spawn.test.ts