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

Improve UX for shell env sync failure (#6644)

* Move files to be under a feature folder

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

* Add error notification on shell sync failure

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

* Improve error handling of case where match is not found

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

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-11-25 04:53:09 -08:00 committed by GitHub
parent 27fb128c05
commit 65b14b9e7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 85 additions and 21 deletions

View File

@ -10,6 +10,12 @@ export interface SendMessageToChannel {
<Message>(channel: MessageChannel<Message>, message: Message): void; <Message>(channel: MessageChannel<Message>, message: Message): void;
} }
export type MessageChannelSender<Channel> = Channel extends MessageChannel<void | undefined>
? () => void
: Channel extends MessageChannel<infer Message>
? (message: Message) => void
: never;
export const sendMessageToChannelInjectionToken = getInjectionToken<SendMessageToChannel>({ export const sendMessageToChannelInjectionToken = getInjectionToken<SendMessageToChannel>({
id: "send-message-to-message-channel", id: "send-message-to-message-channel",
}); });

View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { MessageChannel } from "../../../common/utils/channel/message-channel-listener-injection-token";
export const shellSyncFailedChannel: MessageChannel<string> = {
id: "shell-sync-failed-channel",
};

View File

@ -26,10 +26,8 @@ const computeShellEnvironmentInjectable = getInjectable({
return async (shell) => { return async (shell) => {
const controller = new AbortController(); const controller = new AbortController();
const shellEnv = computeUnixShellEnvironment(shell, { signal: controller.signal });
const timeoutHandle = setTimeout(() => controller.abort(), 30_000); const timeoutHandle = setTimeout(() => controller.abort(), 30_000);
const result = await computeUnixShellEnvironment(shell, { signal: controller.signal });
const result = await shellEnv;
clearTimeout(timeoutHandle); clearTimeout(timeoutHandle);

View File

@ -5,8 +5,8 @@
import type { EnvironmentVariables } from "./compute-shell-environment.injectable"; import type { EnvironmentVariables } from "./compute-shell-environment.injectable";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable"; import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable";
import spawnInjectable from "../../child-process/spawn.injectable"; import spawnInjectable from "../../../main/child-process/spawn.injectable";
import randomUUIDInjectable from "../../crypto/random-uuid.injectable"; import randomUUIDInjectable from "../../../main/crypto/random-uuid.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import processExecPathInjectable from "./execPath.injectable"; import processExecPathInjectable from "./execPath.injectable";
import processEnvInjectable from "./env.injectable"; import processEnvInjectable from "./env.injectable";
@ -149,11 +149,18 @@ const computeUnixShellEnvironmentInjectable = getInjectable({
try { try {
const rawOutput = Buffer.concat(stdout).toString("utf-8"); const rawOutput = Buffer.concat(stdout).toString("utf-8");
logger.info(`[UNIX-SHELL-ENV]: got the following output`, { rawOutput }); logger.debug(`[UNIX-SHELL-ENV]: got the following output`, { rawOutput });
const match = regex.exec(rawOutput); const matchedOutput = regex.exec(rawOutput)?.[1];
const strippedRawOutput = match ? match[1] : "{}";
const resolvedEnv = JSON.parse(strippedRawOutput) as Partial<Record<string, string>>; if (!matchedOutput) {
return resolve({
callWasSuccessful: false,
error: "Something has blocked the shell from producing the environement variables",
});
}
const resolvedEnv = JSON.parse(matchedOutput) as Partial<Record<string, string>>;
resetEnvPairs(resolvedEnv); resetEnvPairs(resolvedEnv);
resolve({ resolve({

View File

@ -7,10 +7,10 @@ import type { DiContainer } from "@ogre-tools/injectable";
import type { ChildProcessWithoutNullStreams } from "child_process"; import type { ChildProcessWithoutNullStreams } from "child_process";
import EventEmitter from "events"; import EventEmitter from "events";
import { flushPromises } from "../../../common/test-utils/flush-promises"; import { flushPromises } from "../../../common/test-utils/flush-promises";
import type { Spawn } from "../../child-process/spawn.injectable"; import type { Spawn } from "../../../main/child-process/spawn.injectable";
import spawnInjectable from "../../child-process/spawn.injectable"; import spawnInjectable from "../../../main/child-process/spawn.injectable";
import randomUUIDInjectable from "../../crypto/random-uuid.injectable"; import randomUUIDInjectable from "../../../main/crypto/random-uuid.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting";
import type { ComputeUnixShellEnvironment } from "./compute-unix-shell-environment.injectable"; import type { ComputeUnixShellEnvironment } from "./compute-unix-shell-environment.injectable";
import computeUnixShellEnvironmentInjectable from "./compute-unix-shell-environment.injectable"; import computeUnixShellEnvironmentInjectable from "./compute-unix-shell-environment.injectable";
import processEnvInjectable from "./env.injectable"; import processEnvInjectable from "./env.injectable";

View File

@ -0,0 +1,19 @@
/**
* 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 type { MessageChannelSender } from "../../../common/utils/channel/message-to-channel-injection-token";
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
import { shellSyncFailedChannel } from "../common/failure-channel";
const emitShellSyncFailedInjectable = getInjectable({
id: "emit-shell-sync-failed",
instantiate: (di): MessageChannelSender<typeof shellSyncFailedChannel> => {
const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken);
return (error) => sendMessageToChannel(shellSyncFailedChannel, error);
},
});
export default emitShellSyncFailedInjectable;

View File

@ -4,12 +4,13 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token"; import { onLoadOfApplicationInjectionToken } from "../../../main/start-main-application/runnable-tokens/on-load-of-application-injection-token";
import { unionPATHs } from "../../../common/utils/union-env-path"; import { unionPATHs } from "../../../common/utils/union-env-path";
import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable"; import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable";
import electronAppInjectable from "../../electron-app/electron-app.injectable"; import electronAppInjectable from "../../../main/electron-app/electron-app.injectable";
import computeShellEnvironmentInjectable from "../../utils/shell-env/compute-shell-environment.injectable"; import computeShellEnvironmentInjectable from "./compute-shell-environment.injectable";
import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable"; import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable";
import emitShellSyncFailedInjectable from "./emit-failure.injectable";
const setupShellInjectable = getInjectable({ const setupShellInjectable = getInjectable({
id: "setup-shell", id: "setup-shell",
@ -20,6 +21,7 @@ const setupShellInjectable = getInjectable({
const electronApp = di.inject(electronAppInjectable); const electronApp = di.inject(electronAppInjectable);
const resolvedUserShellSetting = di.inject(userShellSettingInjectable); const resolvedUserShellSetting = di.inject(userShellSettingInjectable);
const computeShellEnvironment = di.inject(computeShellEnvironmentInjectable); const computeShellEnvironment = di.inject(computeShellEnvironmentInjectable);
const emitShellSyncFailed = di.inject(emitShellSyncFailedInjectable);
return { return {
id: "setup-shell", id: "setup-shell",
@ -29,7 +31,10 @@ const setupShellInjectable = getInjectable({
const result = await computeShellEnvironment(resolvedUserShellSetting.get()); const result = await computeShellEnvironment(resolvedUserShellSetting.get());
if (!result.callWasSuccessful) { if (!result.callWasSuccessful) {
return void logger.error(`[SHELL-SYNC]: ${result.error}`); logger.error(`[SHELL-SYNC]: ${result.error}`);
emitShellSyncFailed(result.error);
return;
} }
const env = result.response; const env = result.response;

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getMessageChannelListenerInjectable } from "../../../common/utils/channel/message-channel-listener-injection-token";
import showErrorNotificationInjectable from "../../../renderer/components/notifications/show-error-notification.injectable";
import { shellSyncFailedChannel } from "../common/failure-channel";
const shellSyncFailureListenerInjectable = getMessageChannelListenerInjectable({
id: "notification",
channel: shellSyncFailedChannel,
handler: (di) => {
const showErrorNotification = di.inject(showErrorNotificationInjectable);
return (errorMessage) => showErrorNotification(`Failed to sync shell environment: ${errorMessage}`);
},
});
export default shellSyncFailureListenerInjectable;

View File

@ -25,7 +25,7 @@ import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.inject
import environmentVariablesInjectable from "../common/utils/environment-variables.injectable"; import environmentVariablesInjectable from "../common/utils/environment-variables.injectable";
import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable"; import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable";
import setupLensProxyInjectable from "./start-main-application/runnables/setup-lens-proxy.injectable"; import setupLensProxyInjectable from "./start-main-application/runnables/setup-lens-proxy.injectable";
import setupShellInjectable from "./start-main-application/runnables/setup-shell.injectable"; import setupShellInjectable from "../features/shell-sync/main/setup-shell.injectable";
import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable"; import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable";
import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable"; import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable";
import isDevelopmentInjectable from "../common/vars/is-development.injectable"; import isDevelopmentInjectable from "../common/vars/is-development.injectable";

View File

@ -17,7 +17,7 @@ import type WebSocket from "ws";
import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable"; import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable";
import joinPathsInjectable from "../../../common/path/join-paths.injectable"; import joinPathsInjectable from "../../../common/path/join-paths.injectable";
import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable"; import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable";
import computeShellEnvironmentInjectable from "../../utils/shell-env/compute-shell-environment.injectable"; import computeShellEnvironmentInjectable from "../../../features/shell-sync/main/compute-shell-environment.injectable";
import spawnPtyInjectable from "../spawn-pty.injectable"; import spawnPtyInjectable from "../spawn-pty.injectable";
import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable"; import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable";
import appNameInjectable from "../../../common/vars/app-name.injectable"; import appNameInjectable from "../../../common/vars/app-name.injectable";

View File

@ -12,7 +12,7 @@ import isMacInjectable from "../../../common/vars/is-mac.injectable";
import isWindowsInjectable from "../../../common/vars/is-windows.injectable"; import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import createKubeJsonApiForClusterInjectable from "../../../common/k8s-api/create-kube-json-api-for-cluster.injectable"; import createKubeJsonApiForClusterInjectable from "../../../common/k8s-api/create-kube-json-api-for-cluster.injectable";
import computeShellEnvironmentInjectable from "../../utils/shell-env/compute-shell-environment.injectable"; import computeShellEnvironmentInjectable from "../../../features/shell-sync/main/compute-shell-environment.injectable";
import spawnPtyInjectable from "../spawn-pty.injectable"; import spawnPtyInjectable from "../spawn-pty.injectable";
import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable"; import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable";
import appNameInjectable from "../../../common/vars/app-name.injectable"; import appNameInjectable from "../../../common/vars/app-name.injectable";

View File

@ -13,7 +13,7 @@ import type * as pty from "node-pty";
import { getOrInsertWith } from "../../common/utils"; import { getOrInsertWith } from "../../common/utils";
import { type TerminalMessage, TerminalChannels } from "../../common/terminal/channels"; import { type TerminalMessage, TerminalChannels } from "../../common/terminal/channels";
import type { Logger } from "../../common/logger"; import type { Logger } from "../../common/logger";
import type { ComputeShellEnvironment } from "../utils/shell-env/compute-shell-environment.injectable"; import type { ComputeShellEnvironment } from "../../features/shell-sync/main/compute-shell-environment.injectable";
import type { SpawnPty } from "./spawn-pty.injectable"; import type { SpawnPty } from "./spawn-pty.injectable";
import type { InitializableState } from "../../common/initializable-state/create"; import type { InitializableState } from "../../common/initializable-state/create";
import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable"; import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable";