From ba426210c28a43a3d36db504523617fd0202070e Mon Sep 17 00:00:00 2001 From: robobun Date: Mon, 26 Jan 2026 16:04:45 -0800 Subject: [PATCH] fix(shell): handle ".", "", "./" in cwd() by using process.cwd() (#26461) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Fix `$`...`.cwd(".")` causing ENOENT error with path ending in "undefined" - The same fix applies to `.cwd("")` and `.cwd("./")` - Falls back to `process.cwd()` when `defaultCwd` is undefined Closes #26460 ## Test plan - [x] Added regression test in `test/regression/issue/26460.test.ts` - [x] Verified test fails with `USE_SYSTEM_BUN=1` (reproduces the bug) - [x] Verified test passes with `bun bd test` (fix works) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot Co-authored-by: Claude Opus 4.5 --- src/js/builtins/shell.ts | 4 +- test/regression/issue/26460.test.ts | 103 ++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 test/regression/issue/26460.test.ts diff --git a/src/js/builtins/shell.ts b/src/js/builtins/shell.ts index 40f3e7d3a0..f1d19bb84a 100644 --- a/src/js/builtins/shell.ts +++ b/src/js/builtins/shell.ts @@ -149,7 +149,7 @@ export function createBunShellTemplateFunction(createShellInterpreter_, createPa cwd(newCwd?: string): this { this.#throwIfRunning(); if (typeof newCwd === "undefined" || newCwd === "." || newCwd === "" || newCwd === "./") { - newCwd = defaultCwd; + newCwd = defaultCwd ?? process.cwd(); } this.#args!.setCwd(newCwd); return this; @@ -277,7 +277,7 @@ export function createBunShellTemplateFunction(createShellInterpreter_, createPa cwd(newCwd: string | undefined) { if (typeof newCwd === "undefined" || typeof newCwd === "string") { if (newCwd === "." || newCwd === "" || newCwd === "./") { - newCwd = defaultCwd; + newCwd = defaultCwd ?? process.cwd(); } this[cwdSymbol] = newCwd; diff --git a/test/regression/issue/26460.test.ts b/test/regression/issue/26460.test.ts new file mode 100644 index 0000000000..b62bce7040 --- /dev/null +++ b/test/regression/issue/26460.test.ts @@ -0,0 +1,103 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDir } from "harness"; + +// Regression test for https://github.com/oven-sh/bun/issues/26460 +// Using $`...`.cwd(".") should work and use the current working directory + +test("shell cwd('.') should use current working directory", async () => { + using dir = tempDir("shell-cwd-dot", { + "test.js": ` + import { $ } from "bun"; + const result = await $\`pwd\`.cwd(".").text(); + console.log(result.trim()); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "test.js"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout.trim()).toBe(String(dir)); + expect(exitCode).toBe(0); +}); + +test("shell cwd('') should use current working directory", async () => { + using dir = tempDir("shell-cwd-empty", { + "test.js": ` + import { $ } from "bun"; + const result = await $\`pwd\`.cwd("").text(); + console.log(result.trim()); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "test.js"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout.trim()).toBe(String(dir)); + expect(exitCode).toBe(0); +}); + +test("shell cwd('./') should use current working directory", async () => { + using dir = tempDir("shell-cwd-dotslash", { + "test.js": ` + import { $ } from "bun"; + const result = await $\`pwd\`.cwd("./").text(); + console.log(result.trim()); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "test.js"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout.trim()).toBe(String(dir)); + expect(exitCode).toBe(0); +}); + +test("Shell prototype cwd('.') should use current working directory", async () => { + using dir = tempDir("shell-proto-cwd-dot", { + "test.js": ` + import { $ } from "bun"; + const shell = new $.Shell(); + shell.cwd("."); + const result = await shell\`pwd\`.text(); + console.log(result.trim()); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "test.js"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout.trim()).toBe(String(dir)); + expect(exitCode).toBe(0); +});