mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
improve shellEnv (#6351)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
cdeededa82
commit
388c38fc5e
4 files changed
+87
-105
No files matched your search
@@ -10,7 +10,7 @@ import { shellEnv } from "../utils/shell-env";
|
||||
import { app } from "electron";
|
||||
import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import os, { userInfo } from "os";
|
||||
import { UserStore } from "../../common/user-store";
|
||||
import * as pty from "node-pty";
|
||||
import { appEventBus } from "../../common/app-event-bus/event-bus";
|
||||
@@ -331,9 +331,9 @@ export abstract class ShellSession {
|
||||
}
|
||||
|
||||
protected async getShellEnv() {
|
||||
const env = clearKubeconfigEnvVars(JSON.parse(JSON.stringify(await shellEnv())));
|
||||
const pathStr = [await this.kubectlBinDirP, ...this.getPathEntries(), process.env.PATH].join(path.delimiter);
|
||||
const shell = UserStore.getInstance().resolvedShell;
|
||||
const env = clearKubeconfigEnvVars(JSON.parse(JSON.stringify(await shellEnv(shell || userInfo().shell))));
|
||||
const pathStr = [await this.kubectlBinDirP, ...this.getPathEntries(), process.env.PATH].join(path.delimiter);
|
||||
|
||||
delete env.DEBUG; // don't pass DEBUG into shells
|
||||
|
||||
|
||||
@@ -3,11 +3,85 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import shellEnvironment from "shell-env";
|
||||
import { spawn } from "child_process";
|
||||
import { randomUUID } from "crypto";
|
||||
import { basename } from "path";
|
||||
import { isWindows } from "../../common/vars";
|
||||
import logger from "../logger";
|
||||
|
||||
export type EnvironmentVariables = Partial<Record<string, string>>;
|
||||
|
||||
|
||||
async function unixShellEnvironment(shell: string): Promise<EnvironmentVariables> {
|
||||
const runAsNode = process.env["ELECTRON_RUN_AS_NODE"];
|
||||
const noAttach = process.env["ELECTRON_NO_ATTACH_CONSOLE"];
|
||||
const env = {
|
||||
...process.env,
|
||||
ELECTRON_RUN_AS_NODE: "1",
|
||||
ELECTRON_NO_ATTACH_CONSOLE: "1",
|
||||
};
|
||||
const mark = randomUUID().replace(/-/g, "");
|
||||
const regex = new RegExp(`${mark}(.*)${mark}`);
|
||||
const shellName = basename(shell);
|
||||
let command: string;
|
||||
let shellArgs: string[];
|
||||
|
||||
if (/^pwsh(-preview)?$/.test(shellName)) {
|
||||
// Older versions of PowerShell removes double quotes sometimes so we use "double single quotes" which is how
|
||||
// you escape single quotes inside of a single quoted string.
|
||||
command = `& '${process.execPath}' -p '''${mark}'' + JSON.stringify(process.env) + ''${mark}'''`;
|
||||
shellArgs = ["-Login", "-Command"];
|
||||
} else {
|
||||
command = `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`;
|
||||
|
||||
if (shellName === "tcsh") {
|
||||
shellArgs = ["-ic"];
|
||||
} else {
|
||||
shellArgs = ["-ilc"];
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const shellProcess = spawn(shell, [...shellArgs, command], {
|
||||
detached: true,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env,
|
||||
});
|
||||
const stdout: Buffer[] = [];
|
||||
|
||||
shellProcess.on("error", (err) => reject(err));
|
||||
shellProcess.stdout.on("data", b => stdout.push(b));
|
||||
shellProcess.on("close", (code, signal) => {
|
||||
if (code || signal) {
|
||||
return reject(new Error(`Unexpected return code from spawned shell (code: ${code}, signal: ${signal})`));
|
||||
}
|
||||
|
||||
try {
|
||||
const rawOutput = Buffer.concat(stdout).toString("utf-8");
|
||||
const match = regex.exec(rawOutput);
|
||||
const strippedRawOutput = match ? match[1] : "{}";
|
||||
const resolvedEnv = JSON.parse(strippedRawOutput);
|
||||
|
||||
if (runAsNode) {
|
||||
resolvedEnv["ELECTRON_RUN_AS_NODE"] = runAsNode;
|
||||
} else {
|
||||
delete resolvedEnv["ELECTRON_RUN_AS_NODE"];
|
||||
}
|
||||
|
||||
if (noAttach) {
|
||||
resolvedEnv["ELECTRON_NO_ATTACH_CONSOLE"] = noAttach;
|
||||
} else {
|
||||
delete resolvedEnv["ELECTRON_NO_ATTACH_CONSOLE"];
|
||||
}
|
||||
|
||||
resolve(resolvedEnv);
|
||||
} catch(err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let shellSyncFailed = false;
|
||||
|
||||
/**
|
||||
@@ -16,20 +90,18 @@ let shellSyncFailed = false;
|
||||
* Subsequent calls after such a timeout simply log an error message without trying
|
||||
* to get the environment, unless forceRetry is true.
|
||||
* @param shell the shell to get the environment from
|
||||
* @param forceRetry if true will always try to get the environment, otherwise if
|
||||
* a previous call to this function failed then this call will fail too.
|
||||
* @returns object containing the shell's environment variables. An empty object is
|
||||
* returned if the call fails.
|
||||
*/
|
||||
export async function shellEnv(shell?: string, forceRetry = false) : Promise<EnvironmentVariables> {
|
||||
if (forceRetry) {
|
||||
shellSyncFailed = false;
|
||||
export async function shellEnv(shell: string) : Promise<EnvironmentVariables> {
|
||||
if (isWindows) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
if (!shellSyncFailed) {
|
||||
try {
|
||||
return await Promise.race([
|
||||
shellEnvironment(shell),
|
||||
unixShellEnvironment(shell),
|
||||
new Promise<EnvironmentVariables>((_resolve, reject) => setTimeout(() => {
|
||||
reject(new Error("Resolving shell environment is taking very long. Please review your shell configuration."));
|
||||
}, 30_000)),
|
||||
|
||||
Reference in new issue
Block a user