mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix: BUN_OPTIONS bare flags getting trailing whitespace (#26464)
## Summary Fix a bug in `appendOptionsEnv` where bare flags (no `=`) that aren't the last option get a trailing space appended, causing the argument parser to not recognize them. For example, `BUN_OPTIONS="--cpu-prof --cpu-prof-dir=profiles"` would parse `--cpu-prof` as `"--cpu-prof "` (trailing space), so CPU profiling was never enabled. ## Root Cause When `appendOptionsEnv` encounters a `--flag` followed by whitespace, it advances past the whitespace looking for a possible quoted value (e.g. `--flag "quoted"`). If no quote is found and there's no `=`, it falls through without resetting `j`, so the emitted argument includes the trailing whitespace. ## Fix Save `end_of_flag = j` after scanning the flag name. Add an `else` branch that resets `j = end_of_flag` when no value (quote or `=`) is found after the whitespace. This is a 3-line change. Also fixes a separate bug in `BunCPUProfiler.zig` where `--cpu-prof-dir` with an absolute path would hit a debug assertion (`path.append` on an already-rooted path with an absolute input). Changed to `path.join` which handles both relative and absolute paths correctly. ## Tests - `test/cli/env/bun-options.test.ts`: Two new tests verifying `--cpu-prof --cpu-prof-dir=<abs-path>` produces a `.cpuprofile` file, for both normal and standalone compiled executables.
This commit is contained in:
58
test/cli/env/bun-options.test.ts
vendored
58
test/cli/env/bun-options.test.ts
vendored
@@ -1,6 +1,7 @@
|
||||
import { spawnSync } from "bun";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "../../harness";
|
||||
import { readdirSync } from "fs";
|
||||
import { bunEnv, bunExe, tempDir } from "harness";
|
||||
|
||||
describe("BUN_OPTIONS environment variable", () => {
|
||||
test("basic usage - passes options to bun command", () => {
|
||||
@@ -56,6 +57,61 @@ describe("BUN_OPTIONS environment variable", () => {
|
||||
expect(result.stdout.toString()).toContain("COMMAND LINE");
|
||||
});
|
||||
|
||||
test("bare flag before flag with value is recognized", () => {
|
||||
// Bare flags (no =) that aren't the last option must not get a
|
||||
// trailing space appended. --cpu-prof is a bare flag; --cpu-prof-dir
|
||||
// uses = syntax. If --cpu-prof isn't recognized, no profile is written.
|
||||
using dir = tempDir("bun-options-cpu-prof", {});
|
||||
|
||||
const result = spawnSync({
|
||||
cmd: [bunExe(), "-e", "1"],
|
||||
env: {
|
||||
...bunEnv,
|
||||
BUN_OPTIONS: `--cpu-prof --cpu-prof-dir=${dir}`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
|
||||
// --cpu-prof should have produced a .cpuprofile file in the dir
|
||||
const files = readdirSync(String(dir));
|
||||
const cpuProfiles = files.filter((f: string) => f.endsWith(".cpuprofile"));
|
||||
expect(cpuProfiles.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
test("bare flag before flag with value is recognized (standalone executable)", () => {
|
||||
// Same test as above but with a compiled standalone executable.
|
||||
using dir = tempDir("bun-options-cpu-prof-compile", {
|
||||
"entry.ts": "console.log('ok');",
|
||||
});
|
||||
|
||||
const exePath = String(dir) + "/app";
|
||||
const profDir = String(dir) + "/profiles";
|
||||
|
||||
// Compile
|
||||
const build = spawnSync({
|
||||
cmd: [bunExe(), "build", "--compile", String(dir) + "/entry.ts", "--outfile", exePath],
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(build.exitCode).toBe(0);
|
||||
|
||||
// Run with BUN_OPTIONS
|
||||
const result = spawnSync({
|
||||
cmd: [exePath],
|
||||
env: {
|
||||
...bunEnv,
|
||||
BUN_OPTIONS: `--cpu-prof --cpu-prof-dir=${profDir}`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.stdout.toString()).toContain("ok");
|
||||
expect(result.exitCode).toBe(0);
|
||||
|
||||
const files = readdirSync(profDir);
|
||||
const cpuProfiles = files.filter((f: string) => f.endsWith(".cpuprofile"));
|
||||
expect(cpuProfiles.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
test("empty BUN_OPTIONS - should work normally", () => {
|
||||
const result = spawnSync({
|
||||
cmd: [bunExe(), "--print='NORMAL'"],
|
||||
|
||||
Reference in New Issue
Block a user