diff --git a/src/common/utils/paths.ts b/src/common/utils/paths.ts index 68d8f61ffa..d7b7e41fbb 100644 --- a/src/common/utils/paths.ts +++ b/src/common/utils/paths.ts @@ -22,9 +22,9 @@ import path from "path"; import os from "os"; -function resolveTilde(filePath: string) { +export function resolveTilde(filePath: string, home?: string) { if (filePath[0] === "~" && (filePath[1] === "/" || filePath.length === 1)) { - return filePath.replace("~", os.homedir()); + return filePath.replace("~", home || os.homedir()); } return filePath; diff --git a/src/main/__test__/shell-session.test.ts b/src/main/__test__/shell-session.test.ts index e58b54ee5b..95111953d1 100644 --- a/src/main/__test__/shell-session.test.ts +++ b/src/main/__test__/shell-session.test.ts @@ -24,6 +24,7 @@ */ import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars"; +import { resolveEnv } from "../utils/shell-env"; describe("clearKubeconfigEnvVars tests", () => { it("should not touch non kubeconfig keys", () => { @@ -38,3 +39,29 @@ describe("clearKubeconfigEnvVars tests", () => { expect(clearKubeconfigEnvVars({ a: 1, kubeconfig: "1", kUbeconfig: "1" })).toStrictEqual({ a: 1 }); }); }); + +describe("resolveEnv tests", () => { + const env = {"HOME": "/home/user", "FOO": "foo", "bar": "bar"}; + + it("should resolve 1 var", () => { + expect(resolveEnv("$HOME", env, env.HOME)).toStrictEqual("/home/user"); + }); + it("should resolve multiple vars", () => { + expect(resolveEnv("$HOME/$FOO", env, env.HOME)).toStrictEqual("/home/user/foo"); + }); + it("should resolve lowercase var", () => { + expect(resolveEnv("$bar", env, env.HOME)).toStrictEqual("bar"); + }); + it("should resolve ~ as $HOME", () => { + expect(resolveEnv("~/foo", env, env.HOME)).toStrictEqual("/home/user/foo"); + }); + it("should not resolve missing var", () => { + expect(resolveEnv("$BAR", env, env.HOME)).toStrictEqual("$BAR"); + }); + it("should not resolve var with brackets", () => { + expect(resolveEnv("${HOME}", env, env.HOME)).toStrictEqual("${HOME}"); + }); + it("should not resolve invalid var (starts with digit)", () => { + expect(resolveEnv("$0HOME", {"0HOME": "/home/user"})).toStrictEqual("$0HOME"); + }); +}); diff --git a/src/main/shell-session/shell-session.ts b/src/main/shell-session/shell-session.ts index 4771ec8b77..a9374ab014 100644 --- a/src/main/shell-session/shell-session.ts +++ b/src/main/shell-session/shell-session.ts @@ -23,7 +23,7 @@ import fse from "fs-extra"; import type { Cluster } from "../cluster"; import { Kubectl } from "../kubectl"; import type * as WebSocket from "ws"; -import { shellEnv } from "../utils/shell-env"; +import { resolveEnv, shellEnv } from "../utils/shell-env"; import { app } from "electron"; import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars"; import path from "path"; @@ -60,16 +60,22 @@ export abstract class ShellSession { } protected async open(shell: string, args: string[], env: Record) { - const cwd = (this.cwd && await fse.pathExists(this.cwd)) - ? this.cwd - : env.HOME; + let cwd = env.HOME; + + if (this.cwd) { + const resolvedCwd = resolveEnv(this.cwd, env, env.HOME); + + if (await fse.pathExists(resolvedCwd)) { + cwd = resolvedCwd; + } + } this.shellProcess = pty.spawn(shell, args, { cols: 80, + rows: 30, cwd, env, name: "xterm-256color", - rows: 30, }); this.running = true; diff --git a/src/main/utils/shell-env.ts b/src/main/utils/shell-env.ts index 18a9545af9..e6acce5e50 100644 --- a/src/main/utils/shell-env.ts +++ b/src/main/utils/shell-env.ts @@ -21,6 +21,7 @@ import shellEnvironment from "shell-env"; import logger from "../logger"; +import { resolveTilde } from "../../common/utils/paths"; export interface EnvironmentVariables { readonly [key: string]: string; @@ -64,3 +65,13 @@ export async function shellEnv(shell?: string, forceRetry = false) : Promise, home?: string): string { + const resolvedValue = value.replace(/\$([a-zA-Z_]+[a-zA-Z0-9_]*)/g, function (match) { + const sub = env[match.substring(1)]; + + return sub || match; + }); + + return resolveTilde(resolvedValue, home); +} diff --git a/src/renderer/components/cluster-settings/components/cluster-home-dir-setting.tsx b/src/renderer/components/cluster-settings/components/cluster-home-dir-setting.tsx index 55abaf5dc6..692278eab4 100644 --- a/src/renderer/components/cluster-settings/components/cluster-home-dir-setting.tsx +++ b/src/renderer/components/cluster-settings/components/cluster-home-dir-setting.tsx @@ -67,8 +67,9 @@ export class ClusterHomeDirSetting extends React.Component { placeholder="$HOME" /> - An explicit start path where the terminal will be launched,{" "} - this is used as the current working directory (cwd) for the shell process. + An explicit start path where the terminal will be launched.{" "} + This is used as the current working directory (cwd) for the shell process{" "} + and supports both ~ and environment variable expansion. );