Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions apps/desktop/src/shell/DesktopShellEnvironment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,108 @@ describe("DesktopShellEnvironment", () => {
}),
);

it.effect("hydrates the locale from the login shell on macOS", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
};

yield* runShellEnvironment({
env,
platform: "darwin",
handler: () =>
envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
LANG: "de_DE.UTF-8",
}),
});

assert.equal(env.LANG, "de_DE.UTF-8");
}),
);

it.effect("preserves an inherited locale over the login shell on macOS", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
LANG: "en_US.UTF-8",
};

yield* runShellEnvironment({
env,
platform: "darwin",
handler: () =>
envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
LANG: "de_DE.UTF-8",
}),
});

assert.equal(env.LANG, "en_US.UTF-8");
}),
);

it.effect("does not mix login-shell locale categories into an inherited locale", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
LANG: "en_US.UTF-8",
};

yield* runShellEnvironment({
env,
platform: "darwin",
handler: () =>
envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
LC_ALL: "de_DE.UTF-8",
}),
});

assert.equal(env.LANG, "en_US.UTF-8");
assert.equal(env.LC_ALL, undefined);
}),
);

it.effect("falls back to a UTF-8 LC_CTYPE when no locale is available on macOS", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
};

yield* runShellEnvironment({
env,
platform: "darwin",
handler: () => envOutput({ PATH: "/opt/homebrew/bin:/usr/bin" }),
});

assert.equal(env.LANG, undefined);
assert.equal(env.LC_ALL, undefined);
assert.equal(env.LC_CTYPE, "en_US.UTF-8");
}),
);

it.effect("does not apply the locale fallback on linux", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
};

yield* runShellEnvironment({
env,
platform: "linux",
handler: () => envOutput({ PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin" }),
});

assert.equal(env.LANG, undefined);
}),
);

it.effect("hydrates PATH and missing SSH_AUTH_SOCK from the login shell on linux", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
Expand Down
28 changes: 28 additions & 0 deletions apps/desktop/src/shell/DesktopShellEnvironment.ts
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ const LOGIN_SHELL_ENV_NAMES = [
"PATH",
"DBUS_SESSION_BUS_ADDRESS",
"DISPLAY",
"LANG",
"LC_ALL",
"LC_CTYPE",
"SSH_AUTH_SOCK",
"HOMEBREW_PREFIX",
"HOMEBREW_CELLAR",
Expand All @@ -84,6 +87,8 @@ const LOGIN_SHELL_ENV_NAMES = [
"WAYLAND_DISPLAY",
] as const;
const WINDOWS_PROFILE_ENV_NAMES = ["PATH", "FNM_DIR", "FNM_MULTISHELL_PATH"] as const;
const LOCALE_ENV_NAMES = ["LANG", "LC_ALL", "LC_CTYPE"] as const;
const FALLBACK_LC_CTYPE = "en_US.UTF-8";
const WINDOWS_SHELL_CANDIDATES = ["pwsh.exe", "powershell.exe"] as const;
const LOGIN_SHELL_TIMEOUT = Duration.seconds(5);
const LAUNCHCTL_TIMEOUT = Duration.seconds(2);
Expand Down Expand Up @@ -472,6 +477,29 @@ const installPosixEnvironment = Effect.fn("desktop.shellEnvironment.installPosix
}
}

// Locale variables form one precedence group: LC_ALL can override an inherited
// LANG or LC_CTYPE, so only hydrate the group when the process has none of them.
if (
config.platform === "darwin" &&
LOCALE_ENV_NAMES.every((name) => Option.isNone(trimNonEmpty(config.env[name])))
) {
for (const name of LOCALE_ENV_NAMES) {
const value = trimNonEmpty(shellEnvironment[name]);
if (Option.isSome(value)) {
config.env[name] = value.value;
}
}

// GUI launches inherit no locale from launchd, so spawned agents land in the C
// locale and pbcopy decodes their UTF-8 output as MacRoman. Older supported
// macOS releases do not provide C.UTF-8, so set only LC_CTYPE to a UTF-8 locale
// available on those releases. Leaving LANG unset keeps C-stable collation and
// formatting, so output parsing is unaffected.
if (LOCALE_ENV_NAMES.every((name) => Option.isNone(trimNonEmpty(config.env[name])))) {
config.env.LC_CTYPE = FALLBACK_LC_CTYPE;
}
}

if (
config.platform === "linux" &&
Option.isNone(trimNonEmpty(config.env.DBUS_SESSION_BUS_ADDRESS))
Expand Down
Loading