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:
Jari Kolehmainen authored and GitHub committed 2022-10-07 09:19:10 -04:00
1 parent cdeededa82
commit 388c38fc5e
4 files changed
+87 -105

No files matched your search

+3 -3
View File
@@ -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
+80 -8
View File
@@ -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)),