Skip to content

Commit

Permalink
fix: pass homedir test (#15811)
Browse files Browse the repository at this point in the history
Co-authored-by: paperdave <[email protected]>
Co-authored-by: Ashcon Partovi <[email protected]>
Co-authored-by: Dylan Conway <[email protected]>
  • Loading branch information
4 people authored Dec 19, 2024
1 parent f546a9b commit 73982bd
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 4 deletions.
24 changes: 24 additions & 0 deletions src/bun.js/bindings/JSEnvironmentVariableMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,27 @@ JSC_DEFINE_CUSTOM_SETTER(jsBunConfigVerboseFetchSetter, (JSGlobalObject * global
return true;
}

extern "C" void Bun__Process__editWindowsEnvVar(BunString, BunString);

JSC_DEFINE_HOST_FUNCTION(jsEditWindowsEnvVar, (JSGlobalObject * global, JSC::CallFrame* callFrame))
{
auto scope = DECLARE_THROW_SCOPE(global->vm());
ASSERT(callFrame->argumentCount() == 2);
ASSERT(callFrame->uncheckedArgument(0).isString());
WTF::String string1 = callFrame->uncheckedArgument(0).toWTFString(global);
RETURN_IF_EXCEPTION(scope, {});
JSValue arg2 = callFrame->uncheckedArgument(1);
ASSERT(arg2.isNull() || arg2.isString());
if (arg2.isCell()) {
WTF::String string2 = arg2.toWTFString(global);
RETURN_IF_EXCEPTION(scope, {});
Bun__Process__editWindowsEnvVar(Bun::toString(string1), Bun::toString(string2));
} else {
Bun__Process__editWindowsEnvVar(Bun::toString(string1), { .tag = BunStringTag::Dead });
}
RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
}

JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
{
VM& vm = globalObject->vm();
Expand Down Expand Up @@ -348,11 +369,14 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
Identifier::fromString(vm, BUN_CONFIG_VERBOSE_FETCH), JSC::CustomGetterSetter::create(vm, jsBunConfigVerboseFetchGetter, jsBunConfigVerboseFetchSetter), BUN_CONFIG_VERBOSE_FETCH_Attrs);

#if OS(WINDOWS)
auto editWindowsEnvVar = JSC::JSFunction::create(vm, globalObject, 0, String("editWindowsEnvVar"_s), jsEditWindowsEnvVar, ImplementationVisibility::Public);

JSC::JSFunction* getSourceEvent = JSC::JSFunction::create(vm, globalObject, processObjectInternalsWindowsEnvCodeGenerator(vm), globalObject);
RETURN_IF_EXCEPTION(scope, {});
JSC::MarkedArgumentBuffer args;
args.append(object);
args.append(keyArray);
args.append(editWindowsEnvVar);
auto clientData = WebCore::clientData(vm);
JSC::CallData callData = JSC::getCallData(getSourceEvent);
NakedPtr<JSC::Exception> returnedException = nullptr;
Expand Down
32 changes: 32 additions & 0 deletions src/bun.js/node/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2133,6 +2133,38 @@ pub const Process = struct {
vm.globalExit();
}

pub fn Bun__Process__editWindowsEnvVar(k: bun.String, v: bun.String) callconv(.C) void {
const wtf = k.value.WTFStringImpl;
var buf1: [32768]u16 = undefined;
var buf2: [32768]u16 = undefined;
const len1: usize = switch (wtf.is8Bit()) {
true => bun.strings.copyLatin1IntoUTF16([]u16, &buf1, []const u8, wtf.latin1Slice()).written,
false => b: {
@memcpy(buf1[0..wtf.length()], wtf.utf16Slice());
break :b wtf.length();
},
};
buf1[len1] = 0;
const str2: ?[*:0]const u16 = if (v.tag != .Dead) str: {
const len2: usize = switch (wtf.is8Bit()) {
true => bun.strings.copyLatin1IntoUTF16([]u16, &buf2, []const u8, wtf.latin1Slice()).written,
false => b: {
@memcpy(buf2[0..wtf.length()], wtf.utf16Slice());
break :b wtf.length();
},
};
buf2[len2] = 0;
break :str buf2[0..len2 :0].ptr;
} else null;
_ = bun.windows.SetEnvironmentVariableW(buf1[0..len1 :0].ptr, str2);
}

comptime {
if (Environment.export_cpp_apis and Environment.isWindows) {
@export(Bun__Process__editWindowsEnvVar, .{ .name = "Bun__Process__editWindowsEnvVar" });
}
}

pub export const Bun__version: [*:0]const u8 = "v" ++ bun.Global.package_json_version;
pub export const Bun__version_with_sha: [*:0]const u8 = "v" ++ bun.Global.package_json_version_with_sha;
pub export const Bun__versions_boringssl: [*:0]const u8 = bun.Global.versions.boringssl;
Expand Down
3 changes: 2 additions & 1 deletion src/http.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,8 @@ pub const HTTPThread = struct {

if (Environment.isWindows) {
_ = std.process.getenvW(comptime bun.strings.w("SystemRoot")) orelse {
std.debug.panic("The %SystemRoot% environment variable is not set. Bun needs this set in order for network requests to work.", .{});
bun.Output.errGeneric("The %SystemRoot% environment variable is not set. Bun needs this set in order for network requests to work.", .{});
Global.crash();
};
}

Expand Down
14 changes: 12 additions & 2 deletions src/js/builtins/ProcessObjectInternals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,13 @@ export function setMainModule(value) {
}

type InternalEnvMap = Record<string, string>;
type EditWindowsEnvVarCb = (key: string, value: null | string) => void;

export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array<string>) {
export function windowsEnv(
internalEnv: InternalEnvMap,
envMapList: Array<string>,
editWindowsEnvVar: EditWindowsEnvVarCb,
) {
// The use of String(key) here is intentional because Node.js as of v21.5.0 will throw
// on symbol keys as it seems they assume the user uses string keys:
//
Expand Down Expand Up @@ -371,7 +376,10 @@ export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array<string
if (!(k in internalEnv) && !envMapList.includes(p)) {
envMapList.push(p);
}
internalEnv[k] = value;
if (internalEnv[k] !== value) {
editWindowsEnvVar(k, value);
internalEnv[k] = value;
}
return true;
},
has(_, p) {
Expand All @@ -383,6 +391,7 @@ export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array<string
if (i !== -1) {
envMapList.splice(i, 1);
}
editWindowsEnvVar(k, null);
return typeof p !== "symbol" ? delete internalEnv[k] : false;
},
defineProperty(_, p, attributes) {
Expand All @@ -391,6 +400,7 @@ export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array<string
if (!(k in internalEnv) && !envMapList.includes(p)) {
envMapList.push(p);
}
editWindowsEnvVar(k, internalEnv[k]);
return $Object.$defineProperty(internalEnv, k, attributes);
},
getOwnPropertyDescriptor(target, p) {
Expand Down
31 changes: 31 additions & 0 deletions test/js/node/test/parallel/test-os-homedir-no-envvar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const cp = require('child_process');
const os = require('os');
const path = require('path');


if (process.argv[2] === 'child') {
if (common.isWindows)
assert.strictEqual(process.env.USERPROFILE, undefined);
else
assert.strictEqual(process.env.HOME, undefined);

const home = os.homedir();

assert.strictEqual(typeof home, 'string');
assert(home.includes(path.sep));
} else {
if (common.isWindows)
delete process.env.USERPROFILE;
else
delete process.env.HOME;

const child = cp.spawnSync(process.execPath, [__filename, 'child'], {
env: process.env,
stdio: 'inherit',
});

assert.strictEqual(child.status, 0);
}
3 changes: 2 additions & 1 deletion test/preload.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as harness from "./harness";
console.log(harness.bunEnv);

// We make Bun.env read-only
// so process.env = {} causes them to be out of sync and we assume Bun.env is
for (let key in process.env) {
if (key === "TZ") continue;
if (key in harness.bunEnv) continue;
delete process.env[key];
}

Expand All @@ -12,7 +14,6 @@ for (let key in harness.bunEnv) {
if (harness.bunEnv[key] === undefined) {
continue;
}

process.env[key] = harness.bunEnv[key] + "";
}

Expand Down

0 comments on commit 73982bd

Please sign in to comment.