1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix syncing shell env on TCSH and CSH (#6453)

* Fix syncing shell env on TCSH and CSH

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Refactor computeUnixShellEnvironment to be clearer

Signed-off-by: Sebastian Malton <sebastian@malton.name>

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-10-24 11:36:12 -04:00 committed by GitHub
parent dbed58cfe5
commit d84d2e28a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 62 deletions

View File

@ -0,0 +1,11 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getGlobalOverride } from "../../common/test-utils/get-global-override";
import randomUUIDInjectable from "./random-uuid.injectable";
export default getGlobalOverride(randomUUIDInjectable, () => () => {
throw new Error("Tried to get a randomUUID without override");
});

View File

@ -0,0 +1,14 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { randomUUID } from "crypto";
const randomUUIDInjectable = getInjectable({
id: "random-uuid",
instantiate: () => randomUUID,
causesSideEffects: true,
});
export default randomUUIDInjectable;

View File

@ -2,13 +2,13 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { spawn } from "child_process";
import { randomUUID } from "crypto";
import { basename } from "path";
import type { EnvironmentVariables } from "./compute-shell-environment.injectable";
import { getInjectable } from "@ogre-tools/injectable";
import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable";
import spawnInjectable from "../../child-process/spawn.injectable";
import randomUUIDInjectable from "../../crypto/random-uuid.injectable";
interface UnixShellEnvOptions {
export interface UnixShellEnvOptions {
signal?: AbortSignal;
}
@ -16,7 +16,37 @@ export type ComputeUnixShellEnvironment = (shell: string, opts?: UnixShellEnvOpt
const computeUnixShellEnvironmentInjectable = getInjectable({
id: "compute-unix-shell-environment",
instantiate: (): ComputeUnixShellEnvironment => async (shell, opts) => {
instantiate: (di): ComputeUnixShellEnvironment => {
const powerShellName = /^pwsh(-preview)?$/;
const nonBashLikeShellName = /^t?csh$/;
const getBasenameOfPath = di.inject(getBasenameOfPathInjectable);
const spawn = di.inject(spawnInjectable);
const randomUUID = di.inject(randomUUIDInjectable);
const getShellSpecifices = (shellPath: string, mark: string) => {
const shellName = getBasenameOfPath(shellPath);
if (powerShellName.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.
return {
command: `Command '${process.execPath}' -p '\\"${mark}\\" + JSON.stringify(process.env) + \\"${mark}\\"'`,
shellArgs: ["-Login"],
};
}
return {
command: `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`,
shellArgs: nonBashLikeShellName.test(shellName)
// tcsh and csh don't support any other options when providing the -l (login) shell option
? ["-l"]
// zsh (at least, maybe others) don't load RC files when in non-interactive mode, even when using -l (login) option
: ["-li"],
};
};
return async (shellPath, opts = {}) => {
const runAsNode = process.env["ELECTRON_RUN_AS_NODE"];
const noAttach = process.env["ELECTRON_NO_ATTACH_CONSOLE"];
const env = {
@ -25,38 +55,21 @@ const computeUnixShellEnvironmentInjectable = getInjectable({
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"];
}
}
const regex = new RegExp(`${mark}(\\{.*\\})${mark}`);
const { command, shellArgs } = getShellSpecifices(shellPath, mark);
return new Promise((resolve, reject) => {
const shellProcess = spawn(shell, [...shellArgs, command], {
const shellProcess = spawn(shellPath, shellArgs, {
detached: true,
stdio: ["ignore", "pipe", "pipe"],
env,
});
const stdout: Buffer[] = [];
opts?.signal?.addEventListener("abort", () => shellProcess.kill());
opts.signal?.addEventListener("abort", () => shellProcess.kill());
shellProcess.stdout.on("data", b => stdout.push(b));
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})`));
@ -85,7 +98,9 @@ const computeUnixShellEnvironmentInjectable = getInjectable({
reject(err);
}
});
shellProcess.stdin.end(command);
});
};
},
causesSideEffects: true,
});