Conversation
Load a system-wide bunfig.toml before user and project configs. Config merge order: system → home → project (later values win). Paths: - POSIX: /etc/bunfig.toml - Windows: %ALLUSERSPROFILE%\bunfig.toml - Override: BUN_SYSTEM_CONFIG env var Enables corporate environments to enforce settings like minimumReleaseAge without modifying per-user config files.
There was a problem hiding this comment.
Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.
Once credits are available, push a new commit or reopen this pull request to trigger a review.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds support for a system-wide bunfig.toml (auto-discovered or via BUN_SYSTEM_CONFIG), changes global config load order to load system config first, introduces a per-process flag tracking system-config load, adds environment variable constants, and includes regression tests for precedence and failure modes. Changes
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli/Arguments.zig`:
- Around line 330-345: The getSystemConfigPath function currently calls
bun.getenvZ("BUN_SYSTEM_CONFIG") and bun.getenvZ("ALLUSERSPROFILE") directly;
add cached, type-safe accessors for these names in src/env_var.zig (following
the existing HOME/XDG_CONFIG_HOME PlatformSpecificNew pattern) as
bun.env_var.BUN_SYSTEM_CONFIG and bun.env_var.ALLUSERSPROFILE, then update
getSystemConfigPath to call bun.env_var.BUN_SYSTEM_CONFIG.get() and
bun.env_var.ALLUSERSPROFILE.get() instead of bun.getenvZ() so the environment
lookups are cached and consistent with the rest of the codebase.
- Around line 248-255: The function loadSystemBunfig currently marks every
discovered system config as auto_loaded (calling loadBunfig with auto_loaded =
true), which hides failures for an explicit BUN_SYSTEM_CONFIG override; change
getSystemConfigPath to return both the PathBuffer and a flag indicating whether
the path was provided via BUN_SYSTEM_CONFIG, then update loadSystemBunfig to
call loadBunfig with auto_loaded = false when the path came from the environment
(and true only for the OS default path), adjusting the same pattern in the
analogous code block around the 330-357 range so explicit overrides fail loudly
while the auto-discovered default remains optional.
In `@test/regression/issue/28726.test.ts`:
- Around line 4-87: Add a new test in test/regression/issue/28726.test.ts that
actually exercises the system→home merge path by spawning Bun with a command
that bypasses AutoCommand (e.g., a package-manager or bunx-style invocation) so
readGlobalConfig()/loadGlobalBunfig() runs; create a tempDir, write a home-level
bunfig (or XDG config) plus a system-level system-bunfig.toml, set env vars HOME
or XDG_CONFIG_HOME and BUN_SYSTEM_CONFIG appropriately, spawn Bun with cmd like
[bunExe(), "package-manager", "some-args"] (or the bunx equivalent) and assert
that values from the home config override/merge as expected; reference the
existing tests for patterns (tempDir usage, Bun.spawn) and ensure the new test
name mentions "system→home merge" so it’s clear what is being exercised.
- Around line 25-45: Replace the fragile "from:project" check with a proof that
the system preload never produced an irreversible side effect: change
"system-preload.ts" to perform an irreversible action (for example, write a file
like "system-ran.txt" into the temp dir) and keep "project-preload.ts" writing
the global FROM value; after process exit assert stdout is "from:project" and
also assert that the irreversible artifact from system-preload (e.g., the file
"system-ran.txt") does NOT exist in the temp dir (use the existing dir/tempDir
variable and the proc/exitCode results to locate where to check), ensuring
overrides truly replace earlier state rather than merely running later.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: bf722b42-b784-4f21-b389-f4205fe3608b
📒 Files selected for processing (3)
src/cli.zigsrc/cli/Arguments.zigtest/regression/issue/28726.test.ts
There was a problem hiding this comment.
♻️ Duplicate comments (2)
test/regression/issue/28726.test.ts (2)
1-2:⚠️ Potential issue | 🟠 MajorStrengthen the preload override test to prove replacement, not just final assignment.
Line 44 can still pass if both preloads run and project executes last. Add an irreversible side effect in
system-preload.ts(e.g., create a file) and assert it never occurs.Suggested diff
+import { existsSync } from "node:fs"; import { describe, expect, test } from "bun:test"; import { bunEnv, bunExe, tempDir } from "harness"; @@ - "system-preload.ts": `(globalThis as any).FROM = "system";`, + "system-preload.ts": `import { writeFileSync } from "node:fs"; writeFileSync("system-ran.txt", "1"); (globalThis as any).FROM = "system";`, "project-preload.ts": `(globalThis as any).FROM = "project";`, @@ // Project-level bunfig overrides system-level preload expect(stdout.trim()).toBe("from:project"); + expect(existsSync(`${dir}/system-ran.txt`)).toBe(false); expect(exitCode).toBe(0);Also applies to: 25-45
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/regression/issue/28726.test.ts` around lines 1 - 2, The current test can pass even if both preloads run; modify the preload override test so system-preload.ts performs an irreversible side effect (e.g., write a sentinel file) and assert that sentinel never exists after the process runs to prove the system preload was replaced. Concretely: update system-preload.ts to create a file (use tempDir/bunEnv helpers or fs.writeFileSync) and update test/regression/issue/28726.test.ts to check for that sentinel (fs.existsSync or similar) and fail if it exists; reference the system-preload.ts sender and the test function in the file (the test that currently asserts on Line 44) so the assertion checks absence of the sentinel after running with the project preload override. Ensure any created temp paths are unique and cleaned up in test setup/teardown.
4-87:⚠️ Potential issue | 🟠 MajorAdd a regression case that exercises the system→home merge path.
Current coverage validates system-only and system→project behavior, but not system→home precedence (a core requirement from Issue
#28726/ PR objective). Please add a case with a temporary home config (HOME/XDG_CONFIG_HOME) plusBUN_SYSTEM_CONFIG, and assert home values override system values.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/regression/issue/28726.test.ts` around lines 4 - 87, Add a new test that exercises the system→home merge path similar to the existing tests: create a temp dir for system config (e.g., "system-bunfig-home-override") with "system-bunfig.toml" containing preload/define or a value (e.g., set FROM="system"), create a separate temp dir to act as HOME (or XDG_CONFIG_HOME) containing "bunfig.toml" that sets the same key to a different value (e.g., FROM="home"), then spawn Bun via Bun.spawn (as in other tests) with env { ...bunEnv, BUN_SYSTEM_CONFIG: `${systemDir}/system-bunfig.toml`, HOME: String(homeDir) } (or XDG_CONFIG_HOME) and assert stdout shows the home value (e.g., expect(stdout.trim()).toBe("from:home") and exitCode 0); add this new test case in the same describe block using the same patterns and helpers (tempDir, Bun.spawn) so home-level bunfig overrides the system-level bunfig.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@test/regression/issue/28726.test.ts`:
- Around line 1-2: The current test can pass even if both preloads run; modify
the preload override test so system-preload.ts performs an irreversible side
effect (e.g., write a sentinel file) and assert that sentinel never exists after
the process runs to prove the system preload was replaced. Concretely: update
system-preload.ts to create a file (use tempDir/bunEnv helpers or
fs.writeFileSync) and update test/regression/issue/28726.test.ts to check for
that sentinel (fs.existsSync or similar) and fail if it exists; reference the
system-preload.ts sender and the test function in the file (the test that
currently asserts on Line 44) so the assertion checks absence of the sentinel
after running with the project preload override. Ensure any created temp paths
are unique and cleaned up in test setup/teardown.
- Around line 4-87: Add a new test that exercises the system→home merge path
similar to the existing tests: create a temp dir for system config (e.g.,
"system-bunfig-home-override") with "system-bunfig.toml" containing
preload/define or a value (e.g., set FROM="system"), create a separate temp dir
to act as HOME (or XDG_CONFIG_HOME) containing "bunfig.toml" that sets the same
key to a different value (e.g., FROM="home"), then spawn Bun via Bun.spawn (as
in other tests) with env { ...bunEnv, BUN_SYSTEM_CONFIG:
`${systemDir}/system-bunfig.toml`, HOME: String(homeDir) } (or XDG_CONFIG_HOME)
and assert stdout shows the home value (e.g.,
expect(stdout.trim()).toBe("from:home") and exitCode 0); add this new test case
in the same describe block using the same patterns and helpers (tempDir,
Bun.spawn) so home-level bunfig overrides the system-level bunfig.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 746e082e-d072-46a4-9062-43afb7b97b74
📒 Files selected for processing (1)
test/regression/issue/28726.test.ts
…, stronger tests - Add BUN_SYSTEM_CONFIG and ALLUSERSPROFILE to src/env_var.zig for cached lookups - getSystemConfigPath returns SystemConfigResult with is_explicit flag - Explicit BUN_SYSTEM_CONFIG fails loudly on missing file; default path is optional - Test: override proof via irreversible side effect (system-ran.txt sentinel) - Test: explicit bad path fails loudly - Test: system→home merge via XDG_CONFIG_HOME with bun install
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli/Arguments.zig`:
- Around line 248-257: The code currently treats an explicit BUN_SYSTEM_CONFIG
that couldn't be stored in the PathBuffer as "no system config" and silently
continues; in loadSystemBunfig (and similarly where getSystemConfigPath is used)
detect the case where result.is_explicit is true but result.path is null and
fail loudly (return an error or call ctx.logger/error and propagate failure)
instead of skipping loadBunfig; update loadSystemBunfig to check "if
(result.is_explicit and result.path == null) return error.InvalidConfigPath" (or
an appropriate error) so explicit overrides behave like other explicit-override
failures, referencing getSystemConfigPath, loadSystemBunfig, and loadBunfig to
locate the change.
In `@test/regression/issue/28726.test.ts`:
- Around line 94-128: The test "system→home config merge via readGlobalConfig
path" can pass without loading the system config because home dryRun=true alone
prevents node_modules creation; fix by making the system config produce a
visible effect independent of dryRun or by removing dryRun from the home config
so both files' settings are observable. Update the test setup that writes
"system-bunfig.toml" (and/or "home-bunfig.toml") used by the test and change the
system config to include an observable, read-only setting (for example a logging
level or other config inspected at runtime such as install.logLevel = "debug")
and then assert on that observable (e.g., check stderr/stdout for the expected
log) or alternatively remove dryRun from the home file and keep system exact =
true so the node_modules behavior verifies the merge; refer to the test name and
loadGlobalBunfig/loadGlobalConfig path to locate where to change the config
strings.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0ab460d1-43e6-43ab-aec1-7466de099215
📒 Files selected for processing (3)
src/cli/Arguments.zigsrc/env_var.zigtest/regression/issue/28726.test.ts
- Fail loudly when BUN_SYSTEM_CONFIG path exceeds buffer size - Move dryRun to system config in merge test so it only passes if system config is actually loaded
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli/Arguments.zig`:
- Around line 383-391: The code calls loadSystemBunfig only after the standalone
autoload early return, so an explicit BUN_SYSTEM_CONFIG is ignored when
disable_autoload_bunfig causes the function to return early; change the flow so
loadSystemBunfig(allocator, ctx, cmd) is invoked when an explicit
BUN_SYSTEM_CONFIG is present or move the call above the disable_autoload_bunfig
early return—i.e., detect env BUN_SYSTEM_CONFIG (or equivalent flag) and always
call loadSystemBunfig even if disable_autoload_bunfig is set, preserving the
existing error handling around ctx.log, Output.err, and Global.crash.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: f673e059-db5c-4fa6-a5c2-6b6d99fcde0b
📒 Files selected for processing (2)
src/cli/Arguments.zigtest/regression/issue/28726.test.ts
System config from default path (/etc/bunfig.toml) is now only loaded for commands with readGlobalConfig (package manager commands), matching home config behavior. BUN_SYSTEM_CONFIG override still works for all commands.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/cli/Arguments.zig (1)
371-379:⚠️ Potential issue | 🟠 MajorMove explicit system-config detection above the standalone autoload check.
Lines 373-378 still return before Lines 383-385 run, so a standalone binary with
disable_autoload_bunfignever honorsBUN_SYSTEM_CONFIG. Also,get() != nulltreatsBUN_SYSTEM_CONFIG=""as explicit, which incorrectly falls back to the default system path on commands that should skip global config.Suggested fix
pub fn loadConfig(allocator: std.mem.Allocator, user_config_path_: ?string, ctx: Command.Context, comptime cmd: Command.Tag) OOM!void { + const has_explicit_system_config = brk: { + if (bun.env_var.BUN_SYSTEM_CONFIG.get()) |path| break :brk path.len > 0; + break :brk false; + }; + // If running as a standalone executable with autoloadBunfig disabled, skip config loading - // unless an explicit config path was provided via --config - if (user_config_path_ == null) { + // unless an explicit config path was provided via --config or BUN_SYSTEM_CONFIG + if (user_config_path_ == null and !has_explicit_system_config) { if (bun.StandaloneModuleGraph.get()) |graph| { if (graph.flags.disable_autoload_bunfig) { return; } } @@ - if (bun.env_var.BUN_SYSTEM_CONFIG.get() != null or comptime cmd.readGlobalConfig()) { + if (has_explicit_system_config or comptime cmd.readGlobalConfig()) { loadSystemBunfig(allocator, ctx, cmd) catch |err| { if (ctx.log.hasAny()) { ctx.log.print(Output.errorWriter()) catch {}; }Also applies to: 383-385
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/Arguments.zig` around lines 371 - 379, The standalone-autoload early return currently runs before honoring an explicitly provided system config and treats an empty BUN_SYSTEM_CONFIG as explicit; move the logic that detects an explicit system config (the environment variable BUN_SYSTEM_CONFIG) above the bun.StandaloneModuleGraph.get() / graph.flags.disable_autoload_bunfig early return and only consider the system-config explicit when the env value is non-empty (i.e., treat "" as not provided). Update both places referenced around user_config_path_ and the later duplicate block so the code checks for a non-empty BUN_SYSTEM_CONFIG first, and only if there is no explicit system config then allow the disable_autoload_bunfig early return to return.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli/Arguments.zig`:
- Around line 343-348: The BUN_SYSTEM_CONFIG env var is accepted verbatim which
allows relative values to make the “system” config cwd-dependent; update the
branch that handles bun.env_var.BUN_SYSTEM_CONFIG to either (A) canonicalize the
value to an absolute path before returning (use the std.fs APIs to join with cwd
and normalize/realpath) or (B) reject non-absolute values with an explicit
error; specifically, in the block that reads custom_path and returns .{ .path =
buf[...], .is_explicit = true }, check whether custom_path is absolute (e.g.,
starts with the platform path separator or otherwise detect absolute on
Windows), and if not either construct an absolute path using std.fs.cwd()/path
normalization or fail loudly and return an error so loadBunfig only ever
receives absolute paths.
---
Duplicate comments:
In `@src/cli/Arguments.zig`:
- Around line 371-379: The standalone-autoload early return currently runs
before honoring an explicitly provided system config and treats an empty
BUN_SYSTEM_CONFIG as explicit; move the logic that detects an explicit system
config (the environment variable BUN_SYSTEM_CONFIG) above the
bun.StandaloneModuleGraph.get() / graph.flags.disable_autoload_bunfig early
return and only consider the system-config explicit when the env value is
non-empty (i.e., treat "" as not provided). Update both places referenced around
user_config_path_ and the later duplicate block so the code checks for a
non-empty BUN_SYSTEM_CONFIG first, and only if there is no explicit system
config then allow the disable_autoload_bunfig early return to return.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1c3535a3-aa2c-4162-895f-9bc9e24b1ae2
📒 Files selected for processing (1)
src/cli/Arguments.zig
| if (bun.env_var.BUN_SYSTEM_CONFIG.get()) |custom_path| { | ||
| if (custom_path.len > 0) { | ||
| if (custom_path.len < bun.MAX_PATH_BYTES) { | ||
| @memcpy(buf[0..custom_path.len], custom_path); | ||
| buf[custom_path.len] = 0; | ||
| return .{ .path = buf[0..custom_path.len :0], .is_explicit = true }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Reject or canonicalize relative BUN_SYSTEM_CONFIG values.
This copies the env var straight into loadBunfig. If the override is relative, the “system” config becomes cwd-dependent, unlike --config, which is normalized later. Either resolve it to an absolute path before returning it here or fail loudly on non-absolute values. As per coding guidelines, "Always use absolute paths in file operations."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/cli/Arguments.zig` around lines 343 - 348, The BUN_SYSTEM_CONFIG env var
is accepted verbatim which allows relative values to make the “system” config
cwd-dependent; update the branch that handles bun.env_var.BUN_SYSTEM_CONFIG to
either (A) canonicalize the value to an absolute path before returning (use the
std.fs APIs to join with cwd and normalize/realpath) or (B) reject non-absolute
values with an explicit error; specifically, in the block that reads custom_path
and returns .{ .path = buf[...], .is_explicit = true }, check whether
custom_path is absolute (e.g., starts with the platform path separator or
otherwise detect absolute on Windows), and if not either construct an absolute
path using std.fs.cwd()/path normalization or fail loudly and return an error so
loadBunfig only ever receives absolute paths.
Closes #28726
Problem
Corporate environments need to enforce bunfig settings (e.g.
minimumReleaseAge) system-wide without modifying per-user or per-project config files.Solution
Add a system-wide bunfig.toml that is loaded before user and project configs. The config merge order is:
~/.bunfig.toml)./bunfig.toml) (highest priority)Later values override earlier ones.
Default paths
/etc/bunfig.toml%ALLUSERSPROFILE%\bunfig.toml(typicallyC:\ProgramData\bunfig.toml)Override
Set
BUN_SYSTEM_CONFIGenvironment variable to use a custom path.Changes
src/cli/Arguments.zig: AddedgetSystemConfigPath()andloadSystemBunfig()functions. System config is loaded for all commands that load any config.src/cli.zig: Addedhas_loaded_system_configguard field.Verification