-
Notifications
You must be signed in to change notification settings - Fork 5k
shell: fold write_no_io ENOSPC into builtin exit codes #34699
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,8 +147,8 @@ | |
| .stdout | ||
| .enqueue(child, &buf, safeguard); | ||
| } | ||
| let _ = Builtin::write_no_io(interp, cmd, IoKind::Stdout, &buf); | ||
| return Builtin::done(interp, cmd, 0); | ||
| let code = Builtin::write_no_io_exit(interp, cmd, IoKind::Stdout, &buf, 0); | ||
| return Builtin::done(interp, cmd, code); | ||
|
Check failure on line 151 in src/runtime/shell/builtin/cat.rs
|
||
|
Comment on lines
+150
to
+151
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 The stdin-sync path here is fixed, but Extended reasoning...What the bug isThis PR folds let _ = Builtin::write_no_io(interp, cmd, IoKind::Stdout, chunk);
Yield::done()This is the async twin of the path the PR did fix. Code path that triggers it
In both, Why nothing catches itWith this PR's own tightening of
Exit code 0, output silently truncated. Step-by-step proofOn Windows (cat builtin always enabled — import { $ } from "bun";
await Bun.write("big.txt", "a".repeat(100));
const buf = new Uint8Array(2);
const r = await $`cat big.txt > ${buf}`.nothrow().quiet();
console.log(r.exitCode, new TextDecoder().decode(buf));
// → 0 "aa" (should be 1 "aa", like echo/seq/pwd now do)
Why this belongs in scopePer REVIEW.md: "Fix the whole class in the same PR … sync/async twins … If a site is intentionally excluded, say so in the PR." The PR description lists cat under fixed builtins as " How to fixThe plumbing already exists. In
Alternatively, if threading it through the multi-chunk state machine is out of scope for this PR, add cat's streaming path to the explicit exclusion list in the PR description alongside the |
||
| } | ||
| // Clone the `Arc<IOReader>` | ||
| // out of `stdin` so we hold no borrow of `interp` across | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -472,6 +472,39 @@ | |
| expect(new TextDecoder().decode(buffer.slice(0, sentinelByte(buffer)))).toEqual(await thisFile.text()); | ||
| }); | ||
|
|
||
| describe("redirect to ArrayBuffer that is too small", () => { | ||
| // A builtin whose output does not fit in the ArrayBuffer sink must exit | ||
| // nonzero, like `echo > /dev/full` does. Whatever fits is still written. | ||
| type Run = (b: ArrayBufferView) => ReturnType<typeof $>; | ||
| const cases: Array<[string, Run, string | undefined]> = [ | ||
| ["echo", b => $`echo hello > ${b}`, "hello\n"], | ||
| ["seq", b => $`seq 1 3 > ${b}`, "1\n2\n3\n"], | ||
| ["basename", b => $`basename /a/b/cde > ${b}`, "cde\n"], | ||
| ["dirname", b => $`dirname /a/b/cde > ${b}`, "/a/b\n"], | ||
| ["pwd", b => $`pwd > ${b}`, undefined], | ||
| ]; | ||
|
Check warning on line 485 in test/js/bun/shell/bunshell.test.ts
|
||
|
Comment on lines
+479
to
+485
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 The test.each table covers echo/seq/basename/dirname/pwd but not the other three sites this PR modifies: Extended reasoning...What's missingThe PR folds ENOSPC into the exit code at 8 call sites — echo, seq, basename, dirname, pwd, export (print-all), which (sync loop), and cat (stdin-sync) — but the new REVIEW.md is direct on this: "Cover the variant matrix, not just the repro. Every sibling entry point receiving the same fix" and "Confirm deleting each load-bearing clause of your fix breaks at least one test." Why
|
||
| test.concurrent.each(cases)("%s", async (_name, run, full) => { | ||
| const zero = new Uint8Array(0); | ||
| const small = new Uint8Array(2); | ||
| const big = new Uint8Array(1 << 10); | ||
| const [r0, rSmall, rBig] = await Promise.all([ | ||
| run(zero).nothrow().quiet(), | ||
| run(small).nothrow().quiet(), | ||
| run(big).nothrow().quiet(), | ||
| ]); | ||
| const bigText = new TextDecoder().decode(big.slice(0, sentinelByte(big))); | ||
| expect({ | ||
| zero: { exitCode: r0.exitCode }, | ||
| small: { exitCode: rSmall.exitCode, buf: new TextDecoder().decode(small) }, | ||
| big: { exitCode: rBig.exitCode, buf: full === undefined ? "<dynamic>" : bigText }, | ||
| }).toEqual({ | ||
| zero: { exitCode: 1 }, | ||
| small: { exitCode: 1, buf: (full ?? bigText).slice(0, 2) }, | ||
| big: { exitCode: 0, buf: full ?? "<dynamic>" }, | ||
| }); | ||
| }); | ||
|
robobun marked this conversation as resolved.
|
||
| }); | ||
|
|
||
| test("redirect Bun.File", async () => { | ||
| const filepath = join(temp_dir, "lmao.txt"); | ||
| const file = Bun.file(filepath); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.