diff --git a/apps/server/src/os-jank.test.ts b/apps/server/src/os-jank.test.ts index 346c711ba6346..e83d4fe2d9be8 100644 --- a/apps/server/src/os-jank.test.ts +++ b/apps/server/src/os-jank.test.ts @@ -189,6 +189,48 @@ effectIt.effect("hydrates a missing JAVA_HOME from the login shell and keeps an ), ); +effectIt.effect( + "hydrates a missing SSH_AUTH_SOCK from the login shell and keeps an explicit one", + () => + Effect.acquireUseRelease( + Effect.sync(() => + vi + .spyOn(NodeChildProcess, "execFileSync") + .mockReturnValue( + [ + "__T3CODE_ENV_PATH_START__", + "/usr/local/bin", + "__T3CODE_ENV_PATH_END__", + "__T3CODE_ENV_SSH_AUTH_SOCK_START__", + "/run/user/1000/ssh-agent.sock", + "__T3CODE_ENV_SSH_AUTH_SOCK_END__", + ].join("\n"), + ), + ), + () => + Effect.gen(function* () { + const missing: NodeJS.ProcessEnv = { SHELL: "/bin/bash", PATH: "/usr/bin" }; + const explicit: NodeJS.ProcessEnv = { + SHELL: "/bin/bash", + PATH: "/usr/bin", + SSH_AUTH_SOCK: "/custom/agent.sock", + }; + + for (const env of [missing, explicit]) { + yield* fixPath().pipe( + Effect.provideService(HostProcess.Platform, "linux"), + Effect.provideService(HostProcess.Environment, env), + Effect.provide(NodeServices.layer), + ); + } + + assert.equal(missing.SSH_AUTH_SOCK, "/run/user/1000/ssh-agent.sock"); + assert.equal(explicit.SSH_AUTH_SOCK, "/custom/agent.sock"); + }), + (probe) => Effect.sync(() => probe.mockRestore()), + ), +); + effectIt.effect("replaces a Path-spelled Windows PATH instead of adding a second key", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { Path: "C:\\Windows" }; diff --git a/apps/server/src/os-jank.ts b/apps/server/src/os-jank.ts index d28d3f80261f4..e2f8eb3dbc8cb 100644 --- a/apps/server/src/os-jank.ts +++ b/apps/server/src/os-jank.ts @@ -30,7 +30,7 @@ function logPathHydrationWarning(message: string, error?: unknown): void { // __MISE_DIFF is mise's record of the PATH entries it added. It must travel // with the shell's PATH, or `mise x` can rebuild PATH behind a same-named // wrapper (`exec mise x claude -- claude`) that then re-runs itself forever. -const LOGIN_SHELL_ENV_NAMES = ["PATH", "__MISE_DIFF", "JAVA_HOME"] as const; +const LOGIN_SHELL_ENV_NAMES = ["PATH", "__MISE_DIFF", "JAVA_HOME", "SSH_AUTH_SOCK"] as const; const LOGIN_SHELL_TIMEOUT = Duration.seconds(5); const SLOW_LOGIN_SHELL_TIMEOUT_MS = 20_000; @@ -62,6 +62,10 @@ function applyLoginShellEnvironment( if (!env.JAVA_HOME && shellEnvironment.JAVA_HOME) { env.JAVA_HOME = shellEnvironment.JAVA_HOME; } + // SSH-signed commits need the agent socket; `wsl.exe --exec` skips the profile that exports it. + if (!env.SSH_AUTH_SOCK && shellEnvironment.SSH_AUTH_SOCK) { + env.SSH_AUTH_SOCK = shellEnvironment.SSH_AUTH_SOCK; + } } function hydratePosixPath(env: NodeJS.ProcessEnv, platform: NodeJS.Platform): void {