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

Try and fix exit code issue

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-04-04 16:21:46 -04:00
parent 1e6c859539
commit f63d10d003
45 changed files with 301 additions and 229 deletions

20
package-lock.json generated
View File

@ -10241,6 +10241,15 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
}, },
"node_modules/@types/stoppable": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/stoppable/-/stoppable-1.1.1.tgz",
"integrity": "sha512-b8N+fCADRIYYrGZOcmOR8ZNBOqhktWTB/bMUl5LvGtT201QKJZOOH5UsFyI3qtteM6ZAJbJqZoBcLqqxKIwjhw==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/tar": { "node_modules/@types/tar": {
"version": "6.1.4", "version": "6.1.4",
"resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.4.tgz",
@ -34430,6 +34439,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/stoppable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
"integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
"engines": {
"node": ">=4",
"npm": ">=6"
}
},
"node_modules/stream-buffers": { "node_modules/stream-buffers": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz",
@ -38200,6 +38218,7 @@
"rfc6902": "^5.0.1", "rfc6902": "^5.0.1",
"selfsigned": "^2.1.1", "selfsigned": "^2.1.1",
"semver": "^7.3.8", "semver": "^7.3.8",
"stoppable": "^1.1.0",
"tar": "^6.1.13", "tar": "^6.1.13",
"tcp-port-used": "^1.0.2", "tcp-port-used": "^1.0.2",
"tempy": "1.0.1", "tempy": "1.0.1",
@ -38260,6 +38279,7 @@
"@types/react-window": "^1.8.5", "@types/react-window": "^1.8.5",
"@types/readable-stream": "^2.3.13", "@types/readable-stream": "^2.3.13",
"@types/semver": "^7.3.13", "@types/semver": "^7.3.13",
"@types/stoppable": "^1.1.1",
"@types/tar": "^6.1.4", "@types/tar": "^6.1.4",
"@types/tcp-port-used": "^1.0.1", "@types/tcp-port-used": "^1.0.1",
"@types/triple-beam": "^1.3.2", "@types/triple-beam": "^1.3.2",

View File

@ -167,6 +167,7 @@
"rfc6902": "^5.0.1", "rfc6902": "^5.0.1",
"selfsigned": "^2.1.1", "selfsigned": "^2.1.1",
"semver": "^7.3.8", "semver": "^7.3.8",
"stoppable": "^1.1.0",
"tar": "^6.1.13", "tar": "^6.1.13",
"tcp-port-used": "^1.0.2", "tcp-port-used": "^1.0.2",
"tempy": "1.0.1", "tempy": "1.0.1",
@ -227,6 +228,7 @@
"@types/react-window": "^1.8.5", "@types/react-window": "^1.8.5",
"@types/readable-stream": "^2.3.13", "@types/readable-stream": "^2.3.13",
"@types/semver": "^7.3.13", "@types/semver": "^7.3.13",
"@types/stoppable": "^1.1.1",
"@types/tar": "^6.1.4", "@types/tar": "^6.1.4",
"@types/tcp-port-used": "^1.0.1", "@types/tcp-port-used": "^1.0.1",
"@types/triple-beam": "^1.3.2", "@types/triple-beam": "^1.3.2",

View File

@ -10,13 +10,48 @@ const loggerInjectable = getInjectable({
id: "logger", id: "logger",
instantiate: (di): Logger => { instantiate: (di): Logger => {
const baseLogger = di.inject(winstonLoggerInjectable); const baseLogger = di.inject(winstonLoggerInjectable);
let closed = false;
baseLogger.once("close", () => {
closed = true;
});
return { return {
debug: (message, ...data) => baseLogger.debug(message, ...data), debug: (message, ...data) => {
info: (message, ...data) => baseLogger.info(message, ...data), if (closed) {
warn: (message, ...data) => baseLogger.warn(message, ...data), console.debug(message, ...data);
error: (message, ...data) => baseLogger.error(message, ...data), } else {
silly: (message, ...data) => baseLogger.silly(message, ...data), baseLogger.debug(message, ...data);
}
},
info: (message, ...data) => {
if (closed) {
console.info(message, ...data);
} else {
baseLogger.info(message, ...data);
}
},
warn: (message, ...data) => {
if (closed) {
console.warn(message, ...data);
} else {
baseLogger.warn(message, ...data);
}
},
error: (message, ...data) => {
if (closed) {
console.error(message, ...data);
} else {
baseLogger.error(message, ...data);
}
},
silly: (message, ...data) => {
if (closed) {
// DO nothing
} else {
baseLogger.silly(message, ...data);
}
},
}; };
}, },

View File

@ -8,11 +8,10 @@ import { loggerTransportInjectionToken } from "./logger/transports";
const winstonLoggerInjectable = getInjectable({ const winstonLoggerInjectable = getInjectable({
id: "winston-logger", id: "winston-logger",
instantiate: (di) => instantiate: (di) => createLogger({
createLogger({ format: format.combine(format.splat(), format.simple()),
format: format.combine(format.splat(), format.simple()), transports: di.injectMany(loggerTransportInjectionToken),
transports: di.injectMany(loggerTransportInjectionToken), }),
}),
}); });
export default winstonLoggerInjectable; export default winstonLoggerInjectable;

View File

@ -5,7 +5,7 @@
import { ipcRenderer } from "electron"; import { ipcRenderer } from "electron";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { makeObservable, observable, reaction, when } from "mobx"; import { makeObservable, observable, reaction } from "mobx";
import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc"; import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc";
import { toJS } from "../../common/utils"; import { toJS } from "../../common/utils";
import { isErrnoException } from "@k8slens/utilities"; import { isErrnoException } from "@k8slens/utilities";
@ -31,6 +31,7 @@ import type { GetRelativePath } from "../../common/path/get-relative-path.inject
import type { RemovePath } from "../../common/fs/remove.injectable"; import type { RemovePath } from "../../common/fs/remove.injectable";
import type TypedEventEmitter from "typed-emitter"; import type TypedEventEmitter from "typed-emitter";
import type { IsExtensionEnabled } from "../../features/extensions/enabled/common/is-enabled.injectable"; import type { IsExtensionEnabled } from "../../features/extensions/enabled/common/is-enabled.injectable";
import assert from "assert";
interface Dependencies { interface Dependencies {
readonly extensionLoader: ExtensionLoader; readonly extensionLoader: ExtensionLoader;
@ -101,10 +102,6 @@ export class ExtensionDiscovery {
// True if extensions have been loaded from the disk after app startup // True if extensions have been loaded from the disk after app startup
@observable isLoaded = false; @observable isLoaded = false;
get whenLoaded() {
return when(() => this.isLoaded);
}
public readonly events: TypedEventEmitter<ExtensionDiscoveryEvents> = new EventEmitter(); public readonly events: TypedEventEmitter<ExtensionDiscoveryEvents> = new EventEmitter();
constructor(protected readonly dependencies: Dependencies) { constructor(protected readonly dependencies: Dependencies) {
@ -157,11 +154,10 @@ export class ExtensionDiscovery {
* Watches for added/removed local extensions. * Watches for added/removed local extensions.
* Dependencies are installed automatically after an extension folder is copied. * Dependencies are installed automatically after an extension folder is copied.
*/ */
async watchExtensions(): Promise<void> { watchExtensions() {
this.dependencies.logger.info(`${logModule} watching extension add/remove in ${this.localFolderPath}`); assert(this.isLoaded, "This function can only be called after a call to 'load' has resolved");
// Wait until .load() has been called and has been resolved this.dependencies.logger.info(`${logModule} watching extension add/remove in ${this.localFolderPath}`);
await this.whenLoaded;
this.dependencies.watch(this.localFolderPath, { this.dependencies.watch(this.localFolderPath, {
// For adding and removing symlinks to work, the depth has to be 1. // For adding and removing symlinks to work, the depth has to be 1.

View File

@ -69,10 +69,6 @@ export class ExtensionLoader {
@observable isLoaded = false; @observable isLoaded = false;
get whenLoaded() {
return when(() => this.isLoaded);
}
constructor(protected readonly dependencies: Dependencies) { constructor(protected readonly dependencies: Dependencies) {
makeObservable(this); makeObservable(this);
@ -138,10 +134,9 @@ export class ExtensionLoader {
await this.initMain(); await this.initMain();
} else { } else {
await this.initRenderer(); await this.initRenderer();
await when(() => this.isLoaded);
} }
await Promise.all([this.whenLoaded]);
// broadcasting extensions between main/renderer processes // broadcasting extensions between main/renderer processes
reaction(() => this.toJSON(), () => this.broadcastExtensions(), { reaction(() => this.toJSON(), () => this.broadcastExtensions(), {
fireImmediately: true, fireImmediately: true,

View File

@ -4,28 +4,23 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token";
import stopServicesAndExitAppInjectable from "../../../../../../main/stop-services-and-exit-app.injectable"; import quitAppExplicitlyInjectable from "../../../../../../main/stop-services-and-exit-app.injectable";
import isMacInjectable from "../../../../../../common/vars/is-mac.injectable"; import isMacInjectable from "../../../../../../common/vars/is-mac.injectable";
const quitApplicationMenuItemInjectable = getInjectable({ const quitApplicationMenuItemInjectable = getInjectable({
id: "quit-application-menu-item", id: "quit-application-menu-item",
instantiate: (di) => { instantiate: (di) => {
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable);
const isMac = di.inject(isMacInjectable); const isMac = di.inject(isMacInjectable);
return { return {
kind: "clickable-menu-item" as const, kind: "clickable-menu-item" as const,
id: "quit", id: "quit",
label: "Quit", label: "Quit",
parentId: isMac ? "mac" : "file", parentId: isMac ? "mac" : "file",
orderNumber: isMac ? 140 : 70, orderNumber: isMac ? 140 : 70,
keyboardShortcut: isMac ? "Cmd+Q" : "Alt+F4", keyboardShortcut: isMac ? "Cmd+Q" : "Alt+F4",
onClick: di.inject(quitAppExplicitlyInjectable),
onClick: () => {
stopServicesAndExitApp();
},
}; };
}, },

View File

@ -14,8 +14,6 @@ const stopApplicationMenuInjectable = getInjectable({
const applicationMenu = di.inject(applicationMenuReactivityInjectable); const applicationMenu = di.inject(applicationMenuReactivityInjectable);
applicationMenu.stop(); applicationMenu.stop();
return undefined;
}, },
}), }),

View File

@ -16,8 +16,6 @@ const stopCheckingForUpdatesInjectable = getInjectable({
if (periodicalCheckForUpdates.started) { if (periodicalCheckForUpdates.started) {
periodicalCheckForUpdates.stop(); periodicalCheckForUpdates.stop();
} }
return undefined;
}, },
}), }),

View File

@ -14,8 +14,6 @@ const stopWatchingIfUpdateShouldHappenOnQuitInjectable = getInjectable({
const watchIfUpdateShouldHappenOnQuit = di.inject(watchIfUpdateShouldHappenOnQuitInjectable); const watchIfUpdateShouldHappenOnQuit = di.inject(watchIfUpdateShouldHappenOnQuitInjectable);
watchIfUpdateShouldHappenOnQuit.stop(); watchIfUpdateShouldHappenOnQuit.stop();
return undefined;
}, },
}), }),

View File

@ -5,21 +5,23 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import extensionInjectable from "../../../../extensions/extension-loader/extension/extension.injectable"; import extensionInjectable from "../../../../extensions/extension-loader/extension/extension.injectable";
import extensionsInjectable from "../../../../extensions/extensions.injectable"; import extensionsInjectable from "../../../../extensions/extensions.injectable";
import { beforeQuitOfBackEndInjectionToken } from "../../../../main/start-main-application/runnable-tokens/phases";
const stopAllExtensionsInjectable = getInjectable({ const stopAllExtensionsInjectable = getInjectable({
id: "stop-all-extensions", id: "stop-all-extensions",
instantiate: (di) => { instantiate: (di) => ({
const extensionInstances = di.inject(extensionsInjectable); run: async () => {
const instances = di.inject(extensionsInjectable).get();
return async () => { for (const instance of instances) {
for (const instance of extensionInstances.get()) {
const extension = di.inject(extensionInjectable, instance); const extension = di.inject(extensionInjectable, instance);
await instance.disable(); await instance.disable();
extension.deregister(); extension.deregister();
} }
}; },
}, }),
injectionToken: beforeQuitOfBackEndInjectionToken,
}); });
export default stopAllExtensionsInjectable; export default stopAllExtensionsInjectable;

View File

@ -26,7 +26,7 @@ const ipcFileLoggerInjectable = getInjectable({
() => createIpcFileTransport(fileId), () => createIpcFileTransport(fileId),
); );
transport?.log?.(entry, () => {}); transport.log?.(entry, () => {});
} }
function close(fileId: string) { function close(fileId: string) {

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../../../main/start-main-application/runnable-tokens/phases"; import { afterQuitOfFrontEndInjectionToken } from "../../../main/start-main-application/runnable-tokens/phases";
import ipcFileLoggerInjectable from "./ipc-file-logger.injectable"; import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
const stopIpcLoggingInjectable = getInjectable({ const stopIpcLoggingInjectable = getInjectable({
@ -21,7 +21,7 @@ const stopIpcLoggingInjectable = getInjectable({
}; };
}, },
injectionToken: beforeQuitOfFrontEndInjectionToken, injectionToken: afterQuitOfFrontEndInjectionToken,
}); });
export default stopIpcLoggingInjectable; export default stopIpcLoggingInjectable;

View File

@ -6,16 +6,16 @@
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import type { ClusterManager } from "../../main/cluster/manager"; import type { ClusterManager } from "../../main/cluster/manager";
import exitAppInjectable from "../../main/electron-app/features/exit-app.injectable"; import quitAppInjectable from "../../main/electron-app/features/exit-app.injectable";
import clusterManagerInjectable from "../../main/cluster/manager.injectable"; import clusterManagerInjectable from "../../main/cluster/manager.injectable";
import stopServicesAndExitAppInjectable from "../../main/stop-services-and-exit-app.injectable"; import quitAppExplicitlyInjectable from "../../main/stop-services-and-exit-app.injectable";
import { testUsingFakeTime, advanceFakeTime } from "../../test-utils/use-fake-time"; import { testUsingFakeTime, advanceFakeTime } from "../../test-utils/use-fake-time";
describe("quitting the app using application menu", () => { describe("quitting the app using application menu", () => {
describe("given application has started", () => { describe("given application has started", () => {
let builder: ApplicationBuilder; let builder: ApplicationBuilder;
let clusterManagerStub: ClusterManager; let clusterManagerStub: ClusterManager;
let exitAppMock: jest.Mock; let quitAppMock: jest.Mock;
beforeEach(async () => { beforeEach(async () => {
testUsingFakeTime("2015-10-21T07:28:00Z"); testUsingFakeTime("2015-10-21T07:28:00Z");
@ -23,13 +23,13 @@ describe("quitting the app using application menu", () => {
builder = getApplicationBuilder(); builder = getApplicationBuilder();
builder.beforeApplicationStart(({ mainDi }) => { builder.beforeApplicationStart(({ mainDi }) => {
mainDi.unoverride(stopServicesAndExitAppInjectable); mainDi.unoverride(quitAppExplicitlyInjectable);
clusterManagerStub = { stop: jest.fn() } as unknown as ClusterManager; clusterManagerStub = { stop: jest.fn() } as unknown as ClusterManager;
mainDi.override(clusterManagerInjectable, () => clusterManagerStub); mainDi.override(clusterManagerInjectable, () => clusterManagerStub);
exitAppMock = jest.fn(); quitAppMock = jest.fn();
mainDi.override(exitAppInjectable, () => exitAppMock); mainDi.override(quitAppInjectable, () => quitAppMock);
}); });
await builder.render(); await builder.render();
@ -52,14 +52,10 @@ describe("quitting the app using application menu", () => {
expect(windows).toEqual([]); expect(windows).toEqual([]);
}); });
it("disconnects all clusters", () => {
expect(clusterManagerStub.stop).toHaveBeenCalled();
});
it("after insufficient time passes, does not terminate application yet", () => { it("after insufficient time passes, does not terminate application yet", () => {
advanceFakeTime(999); advanceFakeTime(999);
expect(exitAppMock).not.toHaveBeenCalled(); expect(quitAppMock).not.toHaveBeenCalled();
}); });
describe("after sufficient time passes", () => { describe("after sufficient time passes", () => {
@ -68,7 +64,7 @@ describe("quitting the app using application menu", () => {
}); });
it("terminates application", () => { it("terminates application", () => {
expect(exitAppMock).toHaveBeenCalled(); expect(quitAppMock).toHaveBeenCalled();
}); });
}); });
}); });

View File

@ -13,8 +13,6 @@ const stopValidatingWeblinksInjectable = getInjectable({
const weblinkVerificationStartableStoppable = di.inject(weblinkVerificationStartableStoppableInjectable); const weblinkVerificationStartableStoppable = di.inject(weblinkVerificationStartableStoppableInjectable);
weblinkVerificationStartableStoppable.stop(); weblinkVerificationStartableStoppable.stop();
return undefined;
}, },
}), }),
injectionToken: beforeQuitOfBackEndInjectionToken, injectionToken: beforeQuitOfBackEndInjectionToken,

View File

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import catalogSyncToRendererInjectable from "./catalog-sync-to-renderer.injectable"; import catalogSyncToRendererInjectable from "./catalog-sync-to-renderer.injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../start-main-application/runnable-tokens/phases"; import { afterQuitOfFrontEndInjectionToken } from "../start-main-application/runnable-tokens/phases";
const stopCatalogSyncInjectable = getInjectable({ const stopCatalogSyncInjectable = getInjectable({
id: "stop-catalog-sync", id: "stop-catalog-sync",
@ -21,7 +21,7 @@ const stopCatalogSyncInjectable = getInjectable({
}, },
}), }),
injectionToken: beforeQuitOfFrontEndInjectionToken, injectionToken: afterQuitOfFrontEndInjectionToken,
}); });
export default stopCatalogSyncInjectable; export default stopCatalogSyncInjectable;

View File

@ -226,14 +226,6 @@ export class ClusterManager {
)), )),
); );
}; };
stop() {
for (const cluster of this.dependencies.clusters.get()) {
this.dependencies
.getClusterConnection(cluster)
.disconnect();
}
}
} }
export function catalogEntityFromCluster(cluster: Cluster) { export function catalogEntityFromCluster(cluster: Cluster) {

View File

@ -5,14 +5,14 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
const exitAppInjectable = getInjectable({ const quitAppInjectable = getInjectable({
id: "exit-app", id: "quit-app",
instantiate: (di) => () => { instantiate: (di) => () => {
const app = di.inject(electronAppInjectable); const app = di.inject(electronAppInjectable);
app.exit(0); app.quit();
}, },
}); });
export default exitAppInjectable; export default quitAppInjectable;

View File

@ -0,0 +1,21 @@
/**
* 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";
const isAutoUpdatingInjectable = getInjectable({
id: "is-auto-updating",
instantiate: () => {
let value = false;
return {
get: () => value,
set: (newValue: boolean) => {
value = newValue;
},
};
},
});
export default isAutoUpdatingInjectable;

View File

@ -14,8 +14,6 @@ const cleanUpDeepLinkingInjectable = getInjectable({
const lensProtocolRouterMain = di.inject(lensProtocolRouterMainInjectable); const lensProtocolRouterMain = di.inject(lensProtocolRouterMainInjectable);
lensProtocolRouterMain.cleanup(); lensProtocolRouterMain.cleanup();
return undefined;
}, },
}), }),

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../../../start-main-application/runnable-tokens/phases"; import { afterQuitOfFrontEndInjectionToken } from "../../../start-main-application/runnable-tokens/phases";
import electronAppInjectable from "../../electron-app.injectable"; import electronAppInjectable from "../../electron-app.injectable";
import { isEmpty } from "lodash/fp"; import { isEmpty } from "lodash/fp";
import getVisibleWindowsInjectable from "../../../start-main-application/lens-window/get-visible-windows.injectable"; import getVisibleWindowsInjectable from "../../../start-main-application/lens-window/get-visible-windows.injectable";
@ -25,7 +25,7 @@ const hideDockForLastClosedWindowInjectable = getInjectable({
}, },
}), }),
injectionToken: beforeQuitOfFrontEndInjectionToken, injectionToken: afterQuitOfFrontEndInjectionToken,
}); });
export default hideDockForLastClosedWindowInjectable; export default hideDockForLastClosedWindowInjectable;

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeElectronIsReadyInjectionToken } from "@k8slens/application-for-electron-main"; import { beforeElectronIsReadyInjectionToken } from "@k8slens/application-for-electron-main";
import requestSingleInstanceLockInjectable from "../features/request-single-instance-lock.injectable"; import requestSingleInstanceLockInjectable from "../features/request-single-instance-lock.injectable";
import exitAppInjectable from "../features/exit-app.injectable"; import quitAppInjectable from "../features/exit-app.injectable";
const enforceSingleApplicationInstanceInjectable = getInjectable({ const enforceSingleApplicationInstanceInjectable = getInjectable({
id: "enforce-single-application-instance", id: "enforce-single-application-instance",
@ -13,10 +13,10 @@ const enforceSingleApplicationInstanceInjectable = getInjectable({
instantiate: (di) => ({ instantiate: (di) => ({
run: () => { run: () => {
const requestSingleInstanceLock = di.inject(requestSingleInstanceLockInjectable); const requestSingleInstanceLock = di.inject(requestSingleInstanceLockInjectable);
const exitApp = di.inject(exitAppInjectable); const quitApp = di.inject(quitAppInjectable);
if (!requestSingleInstanceLock()) { if (!requestSingleInstanceLock()) {
exitApp(); quitApp();
} }
return undefined; return undefined;

View File

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import powerMonitorInjectable from "../features/power-monitor.injectable"; import powerMonitorInjectable from "../features/power-monitor.injectable";
import exitAppInjectable from "../features/exit-app.injectable"; import quitAppInjectable from "../features/exit-app.injectable";
import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; import { onLoadOfApplicationInjectionToken } from "@k8slens/application";
const setupDeviceShutdownInjectable = getInjectable({ const setupDeviceShutdownInjectable = getInjectable({
@ -13,9 +13,9 @@ const setupDeviceShutdownInjectable = getInjectable({
instantiate: (di) => ({ instantiate: (di) => ({
run: () => { run: () => {
const powerMonitor = di.inject(powerMonitorInjectable); const powerMonitor = di.inject(powerMonitorInjectable);
const exitApp = di.inject(exitAppInjectable); const quitApp = di.inject(quitAppInjectable);
powerMonitor.on("shutdown", exitApp); powerMonitor.on("shutdown", quitApp);
}, },
}), }),

View File

@ -4,11 +4,11 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeElectronIsReadyInjectionToken } from "@k8slens/application-for-electron-main"; import { beforeElectronIsReadyInjectionToken } from "@k8slens/application-for-electron-main";
import { beforeQuitOfFrontEndInjectionToken, beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/phases"; import { afterQuitOfFrontEndInjectionToken, beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/phases";
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
import isIntegrationTestingInjectable from "../../../common/vars/is-integration-testing.injectable"; import isIntegrationTestingInjectable from "../../../common/vars/is-integration-testing.injectable";
import autoUpdaterInjectable from "../features/auto-updater.injectable";
import { runManySyncFor, runManyFor } from "@k8slens/run-many"; import { runManySyncFor, runManyFor } from "@k8slens/run-many";
import quitAppInjectable from "../features/exit-app.injectable";
const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({ const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({
id: "setup-closing-of-application", id: "setup-closing-of-application",
@ -17,54 +17,51 @@ const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({
run: () => { run: () => {
const runManySync = runManySyncFor(di); const runManySync = runManySyncFor(di);
const runMany = runManyFor(di); const runMany = runManyFor(di);
const runRunnablesBeforeQuitOfFrontEnd = runManySync(beforeQuitOfFrontEndInjectionToken); const runRunnablesAfterQuitOfFrontEnd = runManySync(afterQuitOfFrontEndInjectionToken);
const runRunnablesBeforeQuitOfBackEnd = runMany(beforeQuitOfBackEndInjectionToken); const runRunnablesBeforeQuitOfBackEnd = runMany(beforeQuitOfBackEndInjectionToken);
const app = di.inject(electronAppInjectable); const app = di.inject(electronAppInjectable);
const isIntegrationTesting = di.inject(isIntegrationTestingInjectable); const isIntegrationTesting = di.inject(isIntegrationTestingInjectable);
const autoUpdater = di.inject(autoUpdaterInjectable); const quitApp = di.inject(quitAppInjectable);
let isAutoUpdating = false;
autoUpdater.on("before-quit-for-update", () => { let isAsyncQuitting = false;
isAutoUpdating = true;
const doAsyncQuit = () => {
if (isAsyncQuitting) {
return;
}
isAsyncQuitting = true;
void (async () => {
try {
console.log("before runRunnablesBeforeQuitOfBackEnd");
await runRunnablesBeforeQuitOfBackEnd();
console.log("after runRunnablesBeforeQuitOfBackEnd");
app.exit(0);
} catch (error) {
console.error("A beforeQuitOfBackEnd failed!!!!", error);
app.exit(1);
}
})();
};
app.on("window-all-closed", () => {
console.log(`app.on("window-all-closed")`);
runRunnablesAfterQuitOfFrontEnd();
if (isIntegrationTesting) {
quitApp();
}
}); });
app.on("will-quit", () => { app.on("will-quit", (event) => {
runRunnablesBeforeQuitOfFrontEnd(); console.log(`app.on("will-quit")`);
event.preventDefault();
doAsyncQuit();
});
let isAsyncQuitting = false; app.on("quit", () => {
console.log(`app.on("quit")`);
const doAsyncQuit = (event: Electron.Event, exitCode = 0) => {
if (isAsyncQuitting) {
return;
}
isAsyncQuitting = true;
void (async () => {
try {
await runRunnablesBeforeQuitOfBackEnd();
} catch (error) {
console.error("A beforeQuitOfBackEnd failed!!!!", error);
exitCode = 1;
}
app.exit(exitCode);
})();
};
app.on("will-quit", (event) => {
runRunnablesBeforeQuitOfFrontEnd();
event.preventDefault();
if (isIntegrationTesting || isAutoUpdating) {
doAsyncQuit(event);
}
});
app.on("quit", (event, exitCode) => {
event.preventDefault();
doAsyncQuit(event, exitCode);
});
}); });
return undefined; return undefined;

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { beforeElectronIsReadyInjectionToken } from "@k8slens/application-for-electron-main";
import { getInjectable } from "@ogre-tools/injectable";
import autoUpdaterInjectable from "../features/auto-updater.injectable";
import isAutoUpdatingInjectable from "../features/is-auto-updating.injectable";
const setupTrackingAutoUpdatingInjectable = getInjectable({
id: "setup-tracking-auto-updating",
instantiate: (di) => ({
run: () => {
const isAutoUpdating = di.inject(isAutoUpdatingInjectable);
const autoUpdater = di.inject(autoUpdaterInjectable);
autoUpdater.on("before-quit-for-update", () => {
isAutoUpdating.set(true);
});
return undefined;
},
}),
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupTrackingAutoUpdatingInjectable;

View File

@ -18,6 +18,7 @@ import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injecta
import type { Logger } from "../../common/logger"; import type { Logger } from "../../common/logger";
import type { SelfSignedCert } from "selfsigned"; import type { SelfSignedCert } from "selfsigned";
import type { KubeAuthProxyServer } from "../cluster/kube-auth-proxy-server.injectable"; import type { KubeAuthProxyServer } from "../cluster/kube-auth-proxy-server.injectable";
import stoppable from "stoppable";
export type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined; export type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined;
export type ServerIncomingMessage = SetRequired<http.IncomingMessage, "url" | "method">; export type ServerIncomingMessage = SetRequired<http.IncomingMessage, "url" | "method">;
@ -65,14 +66,14 @@ const disallowedPorts = new Set([
]); ]);
export class LensProxy { export class LensProxy {
protected proxyServer: https.Server; protected proxyServer: https.Server & stoppable.WithStop;
protected closed = false; protected closed = false;
protected retryCounters = new Map<string, number>(); protected retryCounters = new Map<string, number>();
constructor(private readonly dependencies: Dependencies) { constructor(private readonly dependencies: Dependencies) {
this.configureProxy(dependencies.proxy); this.configureProxy(dependencies.proxy);
this.proxyServer = https.createServer( this.proxyServer = stoppable(https.createServer(
{ {
key: dependencies.certificate.private, key: dependencies.certificate.private,
cert: dependencies.certificate.cert, cert: dependencies.certificate.cert,
@ -80,7 +81,7 @@ export class LensProxy {
(req, res) => { (req, res) => {
this.handleRequest(req as ServerIncomingMessage, res); this.handleRequest(req as ServerIncomingMessage, res);
}, },
); ), 500);
this.proxyServer this.proxyServer
.on("upgrade", (req: ServerIncomingMessage, socket: net.Socket, head: Buffer) => { .on("upgrade", (req: ServerIncomingMessage, socket: net.Socket, head: Buffer) => {
@ -175,7 +176,7 @@ export class LensProxy {
this.dependencies.logger.info("[LENS-PROXY]: Closing server"); this.dependencies.logger.info("[LENS-PROXY]: Closing server");
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
this.proxyServer.close(() => resolve()); this.proxyServer.stop(() => resolve());
}); });
} }

View File

@ -0,0 +1,27 @@
/**
* 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 { loggerTransportInjectionToken } from "../../common/logger/transports";
import winstonLoggerInjectable from "../../common/winston-logger.injectable";
import { beforeQuitOfBackEndInjectionToken } from "../start-main-application/runnable-tokens/phases";
const closeLoggerOnQuitInjectable = getInjectable({
id: "close-file-transport-on-quit",
instantiate: (di) => ({
run: () => {
const transports = di.injectMany(loggerTransportInjectionToken);
const winstonLogger = di.inject(winstonLoggerInjectable);
winstonLogger.close();
for (const transport of transports) {
transport.close?.();
}
},
}),
injectionToken: beforeQuitOfBackEndInjectionToken,
});
export default closeLoggerOnQuitInjectable;

View File

@ -13,10 +13,6 @@ const fileLoggerTransportInjectable = getInjectable({
handleExceptions: false, handleExceptions: false,
level: "debug", level: "debug",
filename: "lens.log", filename: "lens.log",
/**
* SAFTEY: the `ipcMain` check above should mean that this is only
* called in the main process
*/
dirname: di.inject(directoryForLogsInjectable), dirname: di.inject(directoryForLogsInjectable),
maxsize: 1024 * 1024, maxsize: 1024 * 1024,
maxFiles: 16, maxFiles: 16,

View File

@ -6,7 +6,6 @@ import { getInjectable } from "@ogre-tools/injectable";
import loggerInjectable from "../../../../common/logger.injectable"; import loggerInjectable from "../../../../common/logger.injectable";
import applicationWindowStateInjectable from "./application-window-state.injectable"; import applicationWindowStateInjectable from "./application-window-state.injectable";
import { BrowserWindow } from "electron"; import { BrowserWindow } from "electron";
import type { ElectronWindow } from "./create-lens-window.injectable";
import type { RequireExactlyOne } from "type-fest"; import type { RequireExactlyOne } from "type-fest";
import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-browser.injectable"; import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-browser.injectable";
import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable"; import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable";
@ -15,6 +14,7 @@ import isLinuxInjectable from "../../../../common/vars/is-linux.injectable";
import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable"; import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable";
import { applicationInformationToken } from "@k8slens/application"; import { applicationInformationToken } from "@k8slens/application";
import sessionCertificateVerifierInjectable from "./session-certificate-verifier.injectable"; import sessionCertificateVerifierInjectable from "./session-certificate-verifier.injectable";
import type { ClusterFrameInfo } from "../../../../common/cluster-frames.injectable";
export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover"; export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover";
@ -43,6 +43,21 @@ export interface ElectronWindowConfiguration {
onDomReady?: () => void; onDomReady?: () => void;
} }
export interface ElectronWindow {
show: () => void;
close: () => void;
send: (args: SendToViewArgs) => void;
loadFile: (filePath: string) => Promise<void>;
loadUrl: (url: string) => Promise<void>;
reload: () => void;
}
export interface SendToViewArgs {
channel: string;
frameInfo?: ClusterFrameInfo;
data?: unknown;
}
export type CreateElectronWindow = (config: ElectronWindowConfiguration) => ElectronWindow; export type CreateElectronWindow = (config: ElectronWindowConfiguration) => ElectronWindow;
const createElectronWindowInjectable = getInjectable({ const createElectronWindowInjectable = getInjectable({
@ -158,7 +173,6 @@ const createElectronWindowInjectable = getInjectable({
await browserWindow.loadFile(filePath); await browserWindow.loadFile(filePath);
}, },
loadUrl: async (url) => { loadUrl: async (url) => {
logger.info( logger.info(
`[CREATE-ELECTRON-WINDOW]: Loading content for window "${configuration.id}" from url: ${url}...`, `[CREATE-ELECTRON-WINDOW]: Loading content for window "${configuration.id}" from url: ${url}...`,
@ -166,7 +180,6 @@ const createElectronWindowInjectable = getInjectable({
await browserWindow.loadURL(url); await browserWindow.loadURL(url);
}, },
show: () => browserWindow.show(), show: () => browserWindow.show(),
close: () => browserWindow.close(), close: () => browserWindow.close(),
send: ({ channel, data, frameInfo }) => { send: ({ channel, data, frameInfo }) => {

View File

@ -3,24 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { ContentSource, ElectronWindowTitleBarStyle } from "./create-electron-window.injectable"; import type { ContentSource, ElectronWindow, ElectronWindowTitleBarStyle, SendToViewArgs } from "./create-electron-window.injectable";
import createElectronWindowInjectable from "./create-electron-window.injectable"; import createElectronWindowInjectable from "./create-electron-window.injectable";
import type { ClusterFrameInfo } from "../../../../common/cluster-frames.injectable";
export interface ElectronWindow {
show: () => void;
close: () => void;
send: (args: SendToViewArgs) => void;
loadFile: (filePath: string) => Promise<void>;
loadUrl: (url: string) => Promise<void>;
reload: () => void;
}
export interface SendToViewArgs {
channel: string;
frameInfo?: ClusterFrameInfo;
data?: unknown;
}
export interface LensWindow { export interface LensWindow {
id: string; id: string;
@ -91,6 +75,7 @@ const createLensWindowInjectable = getInjectable({
if (!browserWindow) { if (!browserWindow) {
windowIsStarting = true; windowIsStarting = true;
console.log("BEFORE createElectronWindow");
browserWindow = createElectronWindow({ browserWindow = createElectronWindow({
...configuration, ...configuration,
onClose: () => { onClose: () => {
@ -104,11 +89,14 @@ const createLensWindowInjectable = getInjectable({
url: urlForContent, url: urlForContent,
} = configuration.getContentSource(); } = configuration.getContentSource();
console.log("BEFORE configuration.beforeOpen?.()");
const beforeOpen = configuration.beforeOpen?.(); const beforeOpen = configuration.beforeOpen?.();
if (filePathForContent) { if (filePathForContent) {
console.log("BEFORE browserWindow.loadFile");
await browserWindow.loadFile(filePathForContent); await browserWindow.loadFile(filePathForContent);
} else if (urlForContent) { } else if (urlForContent) {
console.log("BEFORE browserWindow.loadUrl");
await browserWindow.loadUrl(urlForContent); await browserWindow.loadUrl(urlForContent);
} }

View File

@ -6,8 +6,8 @@
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable, RunnableSync } from "@k8slens/run-many"; import type { Runnable, RunnableSync } from "@k8slens/run-many";
export const beforeQuitOfFrontEndInjectionToken = getInjectionToken<RunnableSync>({ export const afterQuitOfFrontEndInjectionToken = getInjectionToken<RunnableSync>({
id: "before-quit-of-front-end", id: "after-quit-of-front-end",
}); });
export const beforeQuitOfBackEndInjectionToken = getInjectionToken<Runnable>({ export const beforeQuitOfBackEndInjectionToken = getInjectionToken<Runnable>({

View File

@ -26,8 +26,6 @@ const cleanUpShellSessionsInjectable = getInjectable({
} }
shellSessionProcesses.clear(); shellSessionProcesses.clear();
return undefined;
}, },
}), }),

View File

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../runnable-tokens/phases"; import { afterQuitOfFrontEndInjectionToken } from "../runnable-tokens/phases";
const emitCloseToEventBusInjectable = getInjectable({ const emitCloseToEventBusInjectable = getInjectable({
id: "emit-close-to-event-bus", id: "emit-close-to-event-bus",
@ -19,7 +19,7 @@ const emitCloseToEventBusInjectable = getInjectable({
}, },
}), }),
injectionToken: beforeQuitOfFrontEndInjectionToken, injectionToken: afterQuitOfFrontEndInjectionToken,
}); });
export default emitCloseToEventBusInjectable; export default emitCloseToEventBusInjectable;

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { runInAction } from "mobx"; import { runInAction } from "mobx";
import lensProtocolRouterMainInjectable from "../../../protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable"; import lensProtocolRouterMainInjectable from "../../../protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../../runnable-tokens/phases"; import { afterQuitOfFrontEndInjectionToken } from "../../runnable-tokens/phases";
const flagRendererAsNotLoadedInjectable = getInjectable({ const flagRendererAsNotLoadedInjectable = getInjectable({
id: "stop-deep-linking", id: "stop-deep-linking",
@ -23,7 +23,7 @@ const flagRendererAsNotLoadedInjectable = getInjectable({
}, },
}), }),
injectionToken: beforeQuitOfFrontEndInjectionToken, injectionToken: afterQuitOfFrontEndInjectionToken,
}); });
export default flagRendererAsNotLoadedInjectable; export default flagRendererAsNotLoadedInjectable;

View File

@ -14,8 +14,6 @@ const stopKubeConfigSyncInjectable = getInjectable({
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable); const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
kubeConfigSyncManager.stopSync(); kubeConfigSyncManager.stopSync();
return undefined;
}, },
}), }),

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import exitAppInjectable from "../../electron-app/features/exit-app.injectable"; import quitAppInjectable from "../../electron-app/features/exit-app.injectable";
import lensProxyInjectable from "../../lens-proxy/lens-proxy.injectable"; import lensProxyInjectable from "../../lens-proxy/lens-proxy.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import lensProxyPortInjectable from "../../lens-proxy/lens-proxy-port.injectable"; import lensProxyPortInjectable from "../../lens-proxy/lens-proxy-port.injectable";
@ -22,7 +22,7 @@ const setupLensProxyInjectable = getInjectable({
instantiate: (di) => ({ instantiate: (di) => ({
run: async () => { run: async () => {
const lensProxy = di.inject(lensProxyInjectable); const lensProxy = di.inject(lensProxyInjectable);
const exitApp = di.inject(exitAppInjectable); const quitApp = di.inject(quitAppInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const lensProxyPort = di.inject(lensProxyPortInjectable); const lensProxyPort = di.inject(lensProxyPortInjectable);
const isWindows = di.inject(isWindowsInjectable); const isWindows = di.inject(isWindowsInjectable);
@ -37,7 +37,7 @@ const setupLensProxyInjectable = getInjectable({
} catch (error: any) { } catch (error: any) {
showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`); showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`);
return exitApp(); return quitApp();
} }
// test proxy connection // test proxy connection
@ -54,7 +54,7 @@ const setupLensProxyInjectable = getInjectable({
if (buildVersion.get() !== versionFromProxy) { if (buildVersion.get() !== versionFromProxy) {
logger.error("Proxy server responded with invalid response"); logger.error("Proxy server responded with invalid response");
return exitApp(); return quitApp();
} }
logger.info("⚡ LensProxy connection OK"); logger.info("⚡ LensProxy connection OK");
@ -73,7 +73,7 @@ const setupLensProxyInjectable = getInjectable({
showErrorPopup("Lens Proxy Error", message.join("\n\n")); showErrorPopup("Lens Proxy Error", message.join("\n\n"));
return exitApp(); return quitApp();
} }
}, },
runAfter: initializeBuildVersionInjectable, runAfter: initializeBuildVersionInjectable,

View File

@ -3,23 +3,26 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import clusterManagerInjectable from "../../cluster/manager.injectable"; import clustersInjectable from "../../../features/cluster/storage/common/clusters.injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../runnable-tokens/phases"; import clusterConnectionInjectable from "../../cluster/cluster-connection.injectable";
import { afterQuitOfFrontEndInjectionToken } from "../runnable-tokens/phases";
const stopClusterManagerInjectable = getInjectable({ const stopClusterManagerInjectable = getInjectable({
id: "stop-cluster-manager", id: "stop-cluster-manager",
instantiate: (di) => ({ instantiate: (di) => ({
run: () => { run: () => {
const clusterManager = di.inject(clusterManagerInjectable); const clusters = di.inject(clustersInjectable);
clusterManager.stop(); for (const cluster of clusters.get()) {
di.inject(clusterConnectionInjectable, cluster).disconnect();
}
return undefined; return undefined;
}, },
}), }),
injectionToken: beforeQuitOfFrontEndInjectionToken, injectionToken: afterQuitOfFrontEndInjectionToken,
}); });
export default stopClusterManagerInjectable; export default stopClusterManagerInjectable;

View File

@ -3,33 +3,24 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import exitAppInjectable from "./electron-app/features/exit-app.injectable"; import quitAppInjectable from "./electron-app/features/exit-app.injectable";
import clusterManagerInjectable from "./cluster/manager.injectable";
import loggerInjectable from "../common/logger.injectable"; import loggerInjectable from "../common/logger.injectable";
import closeAllWindowsInjectable from "./start-main-application/lens-window/hide-all-windows/close-all-windows.injectable";
import emitAppEventInjectable from "../common/app-event-bus/emit-event.injectable"; import emitAppEventInjectable from "../common/app-event-bus/emit-event.injectable";
import stopAllExtensionsInjectable from "../features/extensions/stopping/main/stop-all.injectable";
const stopServicesAndExitAppInjectable = getInjectable({ const quitAppExplicitlyInjectable = getInjectable({
id: "stop-services-and-exit-app", id: "quit-app-explicitly",
instantiate: (di) => { instantiate: (di) => {
const exitApp = di.inject(exitAppInjectable); const quitApp = di.inject(quitAppInjectable);
const clusterManager = di.inject(clusterManagerInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const closeAllWindows = di.inject(closeAllWindowsInjectable);
const emitAppEvent = di.inject(emitAppEventInjectable); const emitAppEvent = di.inject(emitAppEventInjectable);
const stopAllExtensions = di.inject(stopAllExtensionsInjectable);
return async () => { return () => {
emitAppEvent({ name: "service", action: "close" }); emitAppEvent({ name: "service", action: "close" });
closeAllWindows();
clusterManager.stop();
await stopAllExtensions();
logger.info("SERVICE:QUIT"); logger.info("SERVICE:QUIT");
setTimeout(exitApp, 1000); quitApp();
}; };
}, },
}); });
export default stopServicesAndExitAppInjectable; export default quitAppExplicitlyInjectable;

View File

@ -14,8 +14,6 @@ const stopSyncingThemeFromOperatingSystemInjectable = getInjectable({
const syncTheme = di.inject(syncThemeFromOperatingSystemInjectable); const syncTheme = di.inject(syncThemeFromOperatingSystemInjectable);
syncTheme.stop(); syncTheme.stop();
return undefined;
}, },
}), }),

View File

@ -15,8 +15,6 @@ const stopTrayInjectable = getInjectable({
const electronTray = di.inject(electronTrayInjectable); const electronTray = di.inject(electronTrayInjectable);
electronTray.stop(); electronTray.stop();
return undefined;
}, },
runAfter: stopReactiveTrayMenuItemsInjectable, runAfter: stopReactiveTrayMenuItemsInjectable,
}), }),

View File

@ -14,8 +14,6 @@ const stopReactiveTrayMenuIconInjectable = getInjectable({
const reactiveTrayMenuIcon = di.inject(reactiveTrayMenuIconInjectable); const reactiveTrayMenuIcon = di.inject(reactiveTrayMenuIconInjectable);
reactiveTrayMenuIcon.stop(); reactiveTrayMenuIcon.stop();
return undefined;
}, },
}), }),

View File

@ -14,8 +14,6 @@ const stopReactiveTrayMenuItemsInjectable = getInjectable({
const reactiveTrayMenuItems = di.inject(reactiveTrayMenuItemsInjectable); const reactiveTrayMenuItems = di.inject(reactiveTrayMenuItemsInjectable);
reactiveTrayMenuItems.stop(); reactiveTrayMenuItems.stop();
return undefined;
}, },
}), }),

View File

@ -5,37 +5,20 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token"; import { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
import { computed } from "mobx"; import { computed } from "mobx";
import stopServicesAndExitAppInjectable from "../../../stop-services-and-exit-app.injectable"; import quitAppExplicitlyInjectable from "../../../stop-services-and-exit-app.injectable";
import { withErrorSuppression } from "../../../../common/utils/with-error-suppression/with-error-suppression";
import { pipeline } from "@ogre-tools/fp";
import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable";
const quitAppTrayItemInjectable = getInjectable({ const quitAppTrayItemInjectable = getInjectable({
id: "quit-app-tray-item", id: "quit-app-tray-item",
instantiate: (di) => { instantiate: (di) => ({
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable); id: "quit-app",
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable); parentId: null,
orderNumber: 150,
return { label: computed(() => "Quit App"),
id: "quit-app", enabled: computed(() => true),
parentId: null, visible: computed(() => true),
orderNumber: 150, click: di.inject(quitAppExplicitlyInjectable),
label: computed(() => "Quit App"), }),
enabled: computed(() => true),
visible: computed(() => true),
click: pipeline(
stopServicesAndExitApp,
withErrorLoggingFor(() => "[TRAY]: Quitting application failed."),
// TODO: Find out how to improve typing so that instead of
// x => withErrorSuppression(x) there could only be withErrorSuppression
(x) => withErrorSuppression(x),
),
};
},
injectionToken: trayMenuItemInjectionToken, injectionToken: trayMenuItemInjectionToken,
}); });

View File

@ -44,7 +44,7 @@ class DynamicBarrier {
} }
} }
const executeRunnableWith = <Param>(param: Param) => { const executeRunnableWith = <Param>(tokenId: string, param: Param) => {
const barrier = new DynamicBarrier(); const barrier = new DynamicBarrier();
return async (runnable: RunnableWithId<Param>): Promise<void> => { return async (runnable: RunnableWithId<Param>): Promise<void> => {
@ -52,7 +52,9 @@ const executeRunnableWith = <Param>(param: Param) => {
await barrier.blockOn(parentRunnable.id); await barrier.blockOn(parentRunnable.id);
} }
console.log(`^^^ "${tokenId}" @ "${runnable.id}"`)
await runnable.run(param); await runnable.run(param);
console.log(`--- "${tokenId}" @ "${runnable.id}"`)
barrier.setFinished(runnable.id); barrier.setFinished(runnable.id);
}; };
}; };
@ -61,7 +63,7 @@ export function runManyFor(di: DiContainerForInjection): RunMany {
const convertToWithId = convertToWithIdWith(di); const convertToWithId = convertToWithIdWith(di);
return <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => async (param: Param) => { return <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => async (param: Param) => {
const executeRunnable = executeRunnableWith(param); const executeRunnable = executeRunnableWith(injectionToken.id, param);
const allRunnables = di.injectManyWithMeta(injectionToken).map(x => convertToWithId(x)); const allRunnables = di.injectManyWithMeta(injectionToken).map(x => convertToWithId(x));
verifyRunnablesAreDAG(injectionToken.id, allRunnables); verifyRunnablesAreDAG(injectionToken.id, allRunnables);

View File

@ -12,6 +12,13 @@ export interface Runnable<T = void> {
id?: never; id?: never;
run: Run<T>; run: Run<T>;
readonly runAfter?: SingleOrMany<Injectable<Runnable<T>, Runnable<T>, void>>; readonly runAfter?: SingleOrMany<Injectable<Runnable<T>, Runnable<T>, void>>;
/**
* Allows for specifying an black box ordering so that some runnables can be run at the end of the phase in
* a sorted order but after all other ones
*
* NOTE: Must be exclusively set to `runAfter`
*/
readonly afterOrderNumber?: number;
} }
export interface RunnableWithId<T> { export interface RunnableWithId<T> {
@ -20,6 +27,12 @@ export interface RunnableWithId<T> {
readonly runAfter: RunnableWithId<T>[]; readonly runAfter: RunnableWithId<T>[];
} }
export interface AfterRunnableWithId<T> {
run: Run<T>;
readonly id: string;
readonly runAfter: number;
}
export interface RunnableSync<T = void> { export interface RunnableSync<T = void> {
id?: never; id?: never;
run: RunSync<T>; run: RunSync<T>;