-
Notifications
You must be signed in to change notification settings - Fork 5k
install: stop panicking on workspaces entries longer than the path buffer #37531
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 all commits
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,9 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { spawnSync } from "bun"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { beforeEach, expect, setDefaultTimeout, test } from "bun:test"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { spawn, spawnSync } from "bun"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { install_test_helpers } from "bun:internal-for-testing"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { beforeEach, describe, expect, setDefaultTimeout, test } from "bun:test"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { mkdirSync, writeFileSync } from "fs"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { bunEnv, bunExe, tempDir, tmpdirSync } from "harness"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { bunEnv, bunExe, isLinux, isWindows, tempDir, tmpdirSync } from "harness"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { dirname, join } from "path"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let cwd: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -143,3 +145,156 @@ test("workspace with .\\ should not crash", () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(text).not.toContain("panic"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(text).not.toContain("Internal assertion failure"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Each `workspaces` entry is joined onto the project directory in a path buffer of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // MAX_PATH_BYTES: 4096 bytes on Linux, 1024 on macOS (on Windows 32767 * 3 + 1, more than | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // any path the OS accepts, so the tests around the buffer size are POSIX only). Glob | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // entries are first joined onto "package.json" in a 4096 byte buffer on every platform. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const POSIX_PATH_BUFFER_BYTES = isLinux ? 4096 : 1024; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Longer than either buffer on every platform. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const LONG_ENTRY_BYTES = 100_000; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PKG1 = { "pkgs/pkg1/package.json": JSON.stringify({ name: "pkg1" }) }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function rootPackageJson(workspaces: string[]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "root", workspaces }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function runInstall(cwd: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await using proc = spawn({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd: [bunExe(), "install"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: bunEnv, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stdout: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stderr: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { stdout, stderr, exitCode }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Installs and asserts that pkgs/pkg1 is the only workspace package that was found. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function expectOnlyPkg1Found(dir: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { stderr, exitCode } = await runInstall(dir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(stderr).not.toContain("error:"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(exitCode).toBe(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(Object.values(install_test_helpers.parseLockfile(dir).workspace_paths)).toEqual(["pkgs/pkg1"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe.concurrent("workspaces entries longer than the path buffer", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("path entry fails with ENAMETOOLONG", async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entry = Buffer.alloc(LONG_ENTRY_BYTES, "a").toString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using dir = tempDir("bad-workspace-long-path", { "package.json": rootPackageJson([entry]) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { stderr, exitCode } = await runInstall(String(dir)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(stderr).toContain(`error: ENAMETOOLONG reading package.json for workspace package "${entry}"`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(exitCode).toBe(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // A relative path of exactly `bytes` bytes made of one letter directory names, so that a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // path which fits the buffer is looked up by the OS (ENOENT) instead of exceeding its | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // limit on the length of a single name. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function pathOfLength(bytes: number) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const tail = bytes % 2 === 0 ? "dd" : "d"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.alloc(bytes - tail.length, "d/").toString() + tail; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The entry is read from `${dir}/${entry}/package.json`. One byte below the buffer size | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // that path still reaches the OS; from the buffer size on it is rejected before that. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test.skipIf(isWindows).each([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ["one byte below", -1, "Workspace not found"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ["exactly", 0, "ENAMETOOLONG reading package.json for workspace package"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ["one byte above", 1, "ENAMETOOLONG reading package.json for workspace package"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ])("path entry whose package.json path is %s the path buffer size", async (_, offset, message) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using dir = tempDir("bad-workspace-path-buffer-edge", {}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prefixBytes = Buffer.byteLength(String(dir)) + "/".length; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entry = pathOfLength(POSIX_PATH_BUFFER_BYTES + offset - prefixBytes - "/package.json".length); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writeFileSync(join(String(dir), "package.json"), rootPackageJson([entry])); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { stderr, exitCode } = await runInstall(String(dir)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(stderr).toContain(`error: ${message} "${entry}"`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(exitCode).toBe(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("glob entry is still matched", async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // A brace group padded far past the buffer size; its first alternative matches `pkgs`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entry = "{pkgs," + Buffer.alloc(LONG_ENTRY_BYTES, "x,").toString() + "x}/*"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using dir = tempDir("bad-workspace-long-glob", { "package.json": rootPackageJson([entry]), ...PKG1 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await expectOnlyPkg1Found(String(dir)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("glob entry matching nothing is skipped like any other glob", async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entry = Buffer.alloc(LONG_ENTRY_BYTES, "a").toString() + "/*"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using dir = tempDir("bad-workspace-long-glob-no-match", { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "package.json": rootPackageJson(["pkgs/*", entry]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...PKG1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await expectOnlyPkg1Found(String(dir)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // What has to fit the buffer is the normalized path, not the entry as written. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test.each([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ["path", "pkgs/pkg1"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ["glob", "pkgs/*"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ])("%s entry that only fits the path buffer once normalized resolves", async (_, suffix) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const entry = Buffer.alloc(LONG_ENTRY_BYTES, "x/../").toString() + suffix; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using dir = tempDir("bad-workspace-long-normalized", { "package.json": rootPackageJson([entry]), ...PKG1 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await expectOnlyPkg1Found(String(dir)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Directory names for a relative path of exactly `bytes` bytes: "deep" followed by names | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // of at most NAME_MAX (255) bytes. The last name is at least 128 bytes long so that its | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // parent directory, used as a cwd below, stays well within PATH_MAX. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function deepDirectoryNames(bytes: number) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const names = ["deep"]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let remaining = bytes - names[0].length; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (remaining > 256) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const length = remaining - 256 >= 129 ? 255 : remaining - 130; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| names.push(Buffer.alloc(length, "a").toString()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remaining -= "/".length + length; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| names.push(Buffer.alloc(remaining - "/".length, "b").toString()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return names; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Globs are walked relative to the project directory, so a match can lie deeper than the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // buffer holds once the project directory is put in front of it. Windows has no such | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // depth: the buffer there holds more than the longest path the OS accepts. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test.skipIf(isWindows)( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "glob match whose absolute package.json path does not fit fails with ENAMETOOLONG", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using dir = tempDir("bad-workspace-deep-glob-match", { "package.json": rootPackageJson(["deep/**"]) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The directory itself fits PATH_MAX (so it can be created), `<dir>/package.json` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // does not fit the buffer. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const absoluteDirBytes = POSIX_PATH_BUFFER_BYTES - 6; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const names = deepDirectoryNames(absoluteDirBytes - Buffer.byteLength(String(dir)) - "/".length); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const workspaceDir = join(String(dir), ...names); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(Buffer.byteLength(workspaceDir)).toBe(absoluteDirBytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mkdirSync(workspaceDir, { recursive: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Too long to be written by its absolute path: write it relative to the parent directory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await using writer = spawn({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bunExe(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "-e", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `require("fs").writeFileSync(process.argv.at(-1) + "/package.json", JSON.stringify({ name: "deep" }))`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| names.at(-1)!, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd: dirname(workspaceDir), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: bunEnv, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stdout: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stderr: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(await writer.stderr.text()).toBe(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(await writer.exited).toBe(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+279
to
+292
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. 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win Drain the writer subprocess streams concurrently and assert the combined result. The writer subprocess pipes As per coding guidelines: "Subprocess tests must drain stdout, stderr, and process exit concurrently and assert the combined result and ordered stage outputs." 🧹 Proposed fix- expect(await writer.stderr.text()).toBe("");
- expect(await writer.exited).toBe(0);
+ const [writerStdout, writerStderr, writerExitCode] = await Promise.all([
+ writer.stdout.text(),
+ writer.stderr.text(),
+ writer.exited,
+ ]);
+ expect({ stdout: writerStdout, stderr: writerStderr, exitCode: writerExitCode }).toEqual({
+ stdout: "",
+ stderr: "",
+ exitCode: 0,
+ });📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { stderr, exitCode } = await runInstall(String(dir)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(stderr).toContain(`error: ENAMETOOLONG reading package.json for workspace package "${names.join("/")}"`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(exitCode).toBe(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: oven-sh/bun
Length of output: 2234
🏁 Script executed:
Repository: oven-sh/bun
Length of output: 21198
🏁 Script executed:
Repository: oven-sh/bun
Length of output: 11877
🏁 Script executed:
Repository: oven-sh/bun
Length of output: 10578
Handle
getcwdfailure without panicking. The newENAMETOOLONGresult enters this branch, andgetcwdcan fail when the current directory was removed. Log the workspace error without the cwd, or propagate thegetcwderror, instead of callingexpect("unreachable").🤖 Prompt for AI Agents
Source: Coding guidelines