Skip to content
Closed
48 changes: 24 additions & 24 deletions src/bunfig/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,24 @@ use crate::bunfig::Bunfig;
fn get_home_config_path(buf: &mut PathBuffer) -> Option<&ZStr> {
let paths: [&[u8]; 1] = [b".bunfig.toml"];

if let Some(data_dir) = env_var::XDG_CONFIG_HOME.get() {
return Some(resolve_path::join_abs_string_buf_z::<platform::Auto>(
data_dir, &mut **buf, &paths,
));
// `$XDG_CONFIG_HOME/.bunfig.toml` takes precedence, but only when the file
// actually exists there; otherwise fall through to `$HOME/.bunfig.toml`.
if let Some(data_dir) = env_var::XDG_CONFIG_HOME.get_not_empty() {
let len = {
let path =
resolve_path::join_abs_string_buf_z::<platform::Auto>(data_dir, &mut **buf, &paths);
if bun_sys::exists_z(path) {
path.len()
} else {
0
}
};
if len > 0 {
return Some(ZStr::from_buf(&buf[..], len));
}
}

if let Some(home_dir) = env_var::HOME.get() {
if let Some(home_dir) = env_var::HOME.get_not_empty() {
return Some(resolve_path::join_abs_string_buf_z::<platform::Auto>(
home_dir, &mut **buf, &paths,
));
Expand Down Expand Up @@ -76,7 +87,6 @@ fn load_bunfig(
// SAFETY: same as above; runs on the same thread.
unsafe { (*log_ptr).level = lvl };
});
ctx.debug.loaded_bunfig = true;
Bunfig::parse(cmd, &source, ctx)
}

Expand Down Expand Up @@ -105,19 +115,15 @@ pub fn load_config_path(
// lookup so the dead arm is still a single branch.
if cmd.read_global_config() {
if let Err(err) = load_global_bunfig(cmd, ctx) {
if auto_loaded {
return Ok(());
}

bun_core::pretty_errorln!(
"{}\nreading global config \"{}\"",
err,
BStr::new(config_path.as_bytes()),
);
Global::exit(1);
// A malformed global config is reported the same way `load_config`
// would; swallowing it here would also skip the local load below.
report_bunfig_load_failure(ctx.log, err);
}
}

// `loaded_bunfig` tracks whether the local-config load has been attempted
// so the `run_command.rs`/`repl_command.rs` fallbacks don't repeat it.
ctx.debug.loaded_bunfig = true;
load_bunfig(cmd, auto_loaded, config_path, ctx)
}

Expand Down Expand Up @@ -153,14 +159,8 @@ pub fn load_config(

let mut config_buf = PathBuffer::uninit();
if cmd.read_global_config() {
if !ctx.has_loaded_global_config {
ctx.has_loaded_global_config = true;

if let Some(path) = get_home_config_path(&mut config_buf) {
if let Err(err) = load_config_path(cmd, true, path, ctx) {
report_bunfig_load_failure(ctx.log, err);
}
}
if let Err(err) = load_global_bunfig(cmd, ctx) {
report_bunfig_load_failure(ctx.log, err);
}
}

Expand Down
51 changes: 29 additions & 22 deletions src/install/PackageManager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1841,35 +1841,42 @@ pub fn init(

initialize_store();

if let Some(data_dir) = bun_core::env_var::XDG_CONFIG_HOME
.get()
.or_else(|| bun_core::env_var::HOME.get())
{
let mut buf = PathBuffer::uninit();
let install_ref = ctx.install.get_or_insert_with(|| {
// `Api::BunInstall` derives `Default` (all fields `None`/empty).
// Own via `Box` — never `Box::leak`.
Box::new(Api::BunInstall::default())
});
let npmrc_local = ZBox::from_bytes(b".npmrc");
let mut buf = PathBuffer::uninit();
// `$XDG_CONFIG_HOME/.npmrc` if it exists, else `$HOME/.npmrc`.
let global_npmrc_len = {
let parts = [b"./.npmrc" as &[u8]];

let install_ref = ctx.install.get_or_insert_with(|| {
// `Api::BunInstall` derives `Default` (all fields `None`/empty).
// Own via `Box` — never `Box::leak`.
Box::new(Api::BunInstall::default())
});
let npmrc_local = ZBox::from_bytes(b".npmrc");
let mut len = 0usize;
if let Some(data_dir) = bun_core::env_var::XDG_CONFIG_HOME.get_not_empty() {
let p =
resolve_path::join_abs_string_buf_z::<platform::Auto>(data_dir, &mut buf, &parts);
if bun_sys::exists_z(p) {
len = p.len();
}
}
if len == 0 {
if let Some(home_dir) = bun_core::env_var::HOME.get_not_empty() {
len = resolve_path::join_abs_string_buf_z::<platform::Auto>(
home_dir, &mut buf, &parts,
)
.len();
}
}
len
};
Comment thread
robobun marked this conversation as resolved.
Outdated
if global_npmrc_len > 0 {
ini::load_npmrc_config(
&mut **install_ref,
env,
true,
&[
resolve_path::join_abs_string_buf_z::<platform::Auto>(data_dir, &mut buf, &parts),
&*npmrc_local,
],
&[ZStr::from_buf(&buf[..], global_npmrc_len), &*npmrc_local],
);
} else {
let install_ref = ctx.install.get_or_insert_with(|| {
// `Api::BunInstall` derives `Default` (all fields `None`/empty).
// Own via `Box` — never `Box::leak`.
Box::new(Api::BunInstall::default())
});
let npmrc_local = ZBox::from_bytes(b".npmrc");
ini::load_npmrc_config(&mut **install_ref, env, true, &[&*npmrc_local]);
}
let cpu_count: u32 = u32::from(bun_core::get_thread_count());
Expand Down
18 changes: 4 additions & 14 deletions src/options_types/command_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,10 @@ impl Tag {
}

pub fn read_global_config(self) -> bool {
matches!(
self,
Tag::BunxCommand
| Tag::PackageManagerCommand
| Tag::InstallCommand
| Tag::AddCommand
| Tag::RemoveCommand
| Tag::UpdateCommand
| Tag::PatchCommand
| Tag::PatchCommitCommand
| Tag::OutdatedCommand
| Tag::PublishCommand
| Tag::AuditCommand
)
// Every command that loads a local `bunfig.toml` also loads the global
// one first so the documented shallow merge (local overrides global)
// applies uniformly to runtime and install settings alike.
LOADS_CONFIG[self]
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.
}
Comment thread
robobun marked this conversation as resolved.

pub fn is_npm_related(self) -> bool {
Expand Down
24 changes: 17 additions & 7 deletions test/cli/install/bun-run-bunfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { realpathSync } from "fs";
import { bunEnv, bunExe, isWindows, tempDirWithFiles, toTOMLString } from "harness";
import { join as pathJoin } from "node:path";

// `bun run` / `bun <script>` now read the global `~/.bunfig.toml`; keep these
// assertions independent of whatever the developer has in theirs.
const isolatedEnv = { ...bunEnv, HOME: undefined, USERPROFILE: undefined, XDG_CONFIG_HOME: undefined };
Comment thread
robobun marked this conversation as resolved.

describe.each(["bun run", "bun"])(`%s`, cmd => {
const runCmd = cmd === "bun" ? ["-c=bunfig.toml", "run"] : ["-c=bunfig.toml"];
const node = Bun.which("node")!;
Expand Down Expand Up @@ -33,7 +37,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {

const result = Bun.spawnSync({
cmd: [bunExe(), "--silent", ...bunFlag, ...runCmd, "where-node"],
env: bunEnv,
env: isolatedEnv,
stderr: "inherit",
stdout: "pipe",
stdin: "ignore",
Expand Down Expand Up @@ -83,7 +87,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {

const result = Bun.spawnSync({
cmd: [bunExe(), ...runCmd, "startScript"],
env: bunEnv,
env: isolatedEnv,
stderr: "pipe",
stdout: "pipe",
stdin: "ignore",
Expand Down Expand Up @@ -119,7 +123,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {

const result = Bun.spawnSync({
cmd: [bunExe(), "--silent", ...runCmd, "start"],
env: bunEnv,
env: isolatedEnv,
stderr: "pipe",
stdout: "inherit",
stdin: "ignore",
Expand Down Expand Up @@ -157,7 +161,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {

const result = Bun.spawnSync({
cmd: [bunExe(), "--silent", ...runCmd, "where-node"],
env: bunEnv,
env: isolatedEnv,
stderr: "inherit",
stdout: "pipe",
stdin: "ignore",
Expand Down Expand Up @@ -197,7 +201,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {

const result = Bun.spawnSync({
cmd: [bunExe(), "--silent", ...runCmd, "where-node"],
env: bunEnv,
env: isolatedEnv,
stderr: "inherit",
stdout: "pipe",
stdin: "ignore",
Expand All @@ -209,7 +213,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
expect(result.success).toBeTrue();
});

test("NOT autoload home bunfig.toml", async () => {
test("autoload home .bunfig.toml", async () => {
const runCmd = cmd === "bun" ? ["run"] : [];

const bunfig = toTOMLString({
Expand All @@ -236,6 +240,8 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
env: {
...bunEnv,
HOME: pathJoin(cwd, "./my-home"),
USERPROFILE: pathJoin(cwd, "./my-home"),
XDG_CONFIG_HOME: undefined,
Comment thread
robobun marked this conversation as resolved.
},
stderr: "inherit",
stdout: "pipe",
Expand All @@ -244,7 +250,11 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
});
const nodeBin = result.stdout.toString().trim();

expect(realpathSync(nodeBin)).toBe(realpathSync(node));
if (isWindows) {
expect(realpathSync(nodeBin)).toContain("\\bun-node-");
} else {
expect(realpathSync(nodeBin)).toBe(realpathSync(execPath));
}
expect(result.success).toBeTrue();
});
});
Loading
Loading