(deps: LoginShellPathDeps)
| 45 | * error, timeout, no PATH in the output). |
| 46 | */ |
| 47 | export async function probeLoginShellPath(deps: LoginShellPathDeps): Promise<string | undefined> { |
| 48 | if (deps.platform === 'win32') return undefined; |
| 49 | // A set-but-blank $SHELL (some daemon/launchd envs) must also fall back. |
| 50 | const envShell = deps.env['SHELL']?.trim(); |
| 51 | const shell = envShell === undefined || envShell.length === 0 ? deps.userShell() : envShell; |
| 52 | if (shell === undefined || shell.length === 0) return undefined; |
| 53 | |
| 54 | // `env` prints the resolved environment in every shell dialect, unlike |
| 55 | // `echo $PATH`, which fish would join with spaces. Invoke it by absolute |
| 56 | // path: a bare `env` resolves through the inherited PATH — which may |
| 57 | // carry cwd-dependent components — from the workspace cwd, so a |
| 58 | // repo-planted `env` binary could run at session startup and feed us an |
| 59 | // arbitrary PATH. The absolute path also bypasses profile function |
| 60 | // shadowing, and /usr/bin/env is guaranteed on every mainstream POSIX |
| 61 | // system (it is the canonical shebang interpreter path). |
| 62 | const stdout = await deps.execFileText( |
| 63 | shell, |
| 64 | ['-l', '-c', '/usr/bin/env'], |
| 65 | LOGIN_SHELL_ENV_TIMEOUT_MS, |
| 66 | ); |
| 67 | if (stdout === undefined) return undefined; |
| 68 | |
| 69 | // Profile output lands on stdout before `env` runs, so keep the last |
| 70 | // PATH= line. |
| 71 | let path: string | undefined; |
| 72 | for (const line of stdout.split('\n')) { |
| 73 | if (line.startsWith('PATH=')) { |
| 74 | path = line.slice('PATH='.length).trim(); |
| 75 | } |
| 76 | } |
| 77 | if (path === undefined || path.length === 0) return undefined; |
| 78 | return path; |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Union of the current PATH and the login-shell PATH: the current PATH |
no outgoing calls
no test coverage detected