Skip to content

Commit ccb8b25

Browse files
Support Linux login-shell PATH hydration
- Extend PATH sync to Linux desktop and server startup - Add tests for Linux handling and non-matching platforms
1 parent ee0a191 commit ccb8b25

File tree

4 files changed

+83
-9
lines changed

4 files changed

+83
-9
lines changed

apps/desktop/src/syncShellEnvironment.test.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,13 @@ describe("syncShellEnvironment", () => {
6262
expect(env.SSH_AUTH_SOCK).toBe("/tmp/inherited.sock");
6363
});
6464

65-
it("does nothing outside macOS", () => {
65+
it("hydrates PATH and missing SSH_AUTH_SOCK from the login shell on linux", () => {
6666
const env: NodeJS.ProcessEnv = {
6767
SHELL: "/bin/zsh",
6868
PATH: "/usr/bin",
69-
SSH_AUTH_SOCK: "/tmp/inherited.sock",
7069
};
7170
const readEnvironment = vi.fn(() => ({
72-
PATH: "/opt/homebrew/bin:/usr/bin",
71+
PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin",
7372
SSH_AUTH_SOCK: "/tmp/secretive.sock",
7473
}));
7574

@@ -78,8 +77,29 @@ describe("syncShellEnvironment", () => {
7877
readEnvironment,
7978
});
8079

80+
expect(readEnvironment).toHaveBeenCalledWith("/bin/zsh", ["PATH", "SSH_AUTH_SOCK"]);
81+
expect(env.PATH).toBe("/home/linuxbrew/.linuxbrew/bin:/usr/bin");
82+
expect(env.SSH_AUTH_SOCK).toBe("/tmp/secretive.sock");
83+
});
84+
85+
it("does nothing outside macOS and linux", () => {
86+
const env: NodeJS.ProcessEnv = {
87+
SHELL: "C:/Program Files/Git/bin/bash.exe",
88+
PATH: "C:\\Windows\\System32",
89+
SSH_AUTH_SOCK: "/tmp/inherited.sock",
90+
};
91+
const readEnvironment = vi.fn(() => ({
92+
PATH: "/usr/local/bin:/usr/bin",
93+
SSH_AUTH_SOCK: "/tmp/secretive.sock",
94+
}));
95+
96+
syncShellEnvironment(env, {
97+
platform: "win32",
98+
readEnvironment,
99+
});
100+
81101
expect(readEnvironment).not.toHaveBeenCalled();
82-
expect(env.PATH).toBe("/usr/bin");
102+
expect(env.PATH).toBe("C:\\Windows\\System32");
83103
expect(env.SSH_AUTH_SOCK).toBe("/tmp/inherited.sock");
84104
});
85105
});

apps/desktop/src/syncShellEnvironment.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { readEnvironmentFromLoginShell, resolveLoginShell, ShellEnvironmentReader } from "@t3tools/shared/shell";
1+
import {
2+
readEnvironmentFromLoginShell,
3+
resolveLoginShell,
4+
ShellEnvironmentReader,
5+
} from "@t3tools/shared/shell";
26

37
export function syncShellEnvironment(
48
env: NodeJS.ProcessEnv = process.env,

apps/server/src/os-jank.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
import { fixPath } from "./os-jank";
4+
5+
describe("fixPath", () => {
6+
it("hydrates PATH on linux using the resolved login shell", () => {
7+
const env: NodeJS.ProcessEnv = {
8+
SHELL: "/bin/zsh",
9+
PATH: "/usr/bin",
10+
};
11+
const readPath = vi.fn(() => "/opt/homebrew/bin:/usr/bin");
12+
13+
fixPath({
14+
env,
15+
platform: "linux",
16+
readPath,
17+
});
18+
19+
expect(readPath).toHaveBeenCalledWith("/bin/zsh");
20+
expect(env.PATH).toBe("/opt/homebrew/bin:/usr/bin");
21+
});
22+
23+
it("does nothing outside macOS and linux even when SHELL is set", () => {
24+
const env: NodeJS.ProcessEnv = {
25+
SHELL: "C:/Program Files/Git/bin/bash.exe",
26+
PATH: "C:\\Windows\\System32",
27+
};
28+
const readPath = vi.fn(() => "/usr/local/bin:/usr/bin");
29+
30+
fixPath({
31+
env,
32+
platform: "win32",
33+
readPath,
34+
});
35+
36+
expect(readPath).not.toHaveBeenCalled();
37+
expect(env.PATH).toBe("C:\\Windows\\System32");
38+
});
39+
});

apps/server/src/os-jank.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@ import * as OS from "node:os";
22
import { Effect, Path } from "effect";
33
import { readPathFromLoginShell, resolveLoginShell } from "@t3tools/shared/shell";
44

5-
export function fixPath(): void {
5+
export function fixPath(
6+
options: {
7+
env?: NodeJS.ProcessEnv;
8+
platform?: NodeJS.Platform;
9+
readPath?: typeof readPathFromLoginShell;
10+
} = {},
11+
): void {
12+
const platform = options.platform ?? process.platform;
13+
if (platform !== "darwin" && platform !== "linux") return;
14+
15+
const env = options.env ?? process.env;
16+
617
try {
7-
const shell = resolveLoginShell(process.platform, process.env.SHELL);
18+
const shell = resolveLoginShell(platform, env.SHELL);
819
if (!shell) return;
9-
const result = readPathFromLoginShell(shell);
20+
const result = (options.readPath ?? readPathFromLoginShell)(shell);
1021
if (result) {
11-
process.env.PATH = result;
22+
env.PATH = result;
1223
}
1324
} catch {
1425
// Silently ignore — keep default PATH

0 commit comments

Comments
 (0)