diff --git a/src/extensions/extension-installer.ts b/src/extensions/extension-installer.ts index 04b78bbe1a..82a26f8af8 100644 --- a/src/extensions/extension-installer.ts +++ b/src/extensions/extension-installer.ts @@ -43,9 +43,9 @@ export class ExtensionInstaller { mode: 0o600 }); - logger.info(`${logModule} installing dependencies at ${extensionPackagesRoot()}`); + logger.info(`${logModule}: installing dependencies at ${this.extensionPackagesRoot}`); await this.npm(["install", "--no-audit", "--only=prod", "--prefer-offline", "--no-package-lock"]); - logger.info(`${logModule} dependencies installed at ${extensionPackagesRoot()}`); + logger.info(`${logModule}: dependencies installed at ${this.extensionPackagesRoot}`); } finally { this.installLock.release(); } @@ -59,9 +59,9 @@ export class ExtensionInstaller { await this.installLock.acquireAsync(); try { - logger.info(`${logModule} installing package from ${name} to ${extensionPackagesRoot()}`); + logger.info(`${logModule}: installing package from ${name} to ${this.extensionPackagesRoot}`); await this.npm(["install", "--no-audit", "--only=prod", "--prefer-offline", "--no-package-lock", "--no-save", name]); - logger.info(`${logModule} package ${name} installed to ${extensionPackagesRoot()}`); + logger.info(`${logModule}: package ${name} installed to ${this.extensionPackagesRoot}`); } finally { this.installLock.release(); } @@ -70,7 +70,7 @@ export class ExtensionInstaller { private npm(args: string[]): Promise { return new Promise((resolve, reject) => { const child = child_process.fork(this.npmPath, args, { - cwd: extensionPackagesRoot(), + cwd: this.extensionPackagesRoot, silent: true, env: {} }); diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index 98697d252c..7f78d3bdaf 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -13,6 +13,7 @@ import type { LensMainExtension } from "./lens-main-extension"; import type { LensRendererExtension } from "./lens-renderer-extension"; import * as registries from "./registries"; import fs from "fs"; +import { delay } from "../common/utils"; // lazy load so that we get correct userData export function extensionPackagesRoot() { @@ -87,7 +88,7 @@ export class ExtensionLoader { this.extensions.set(extension.id, extension); } - removeInstance(lensExtensionId: LensExtensionId) { + async removeInstance(lensExtensionId: LensExtensionId) { logger.info(`${logModule} deleting extension instance ${lensExtensionId}`); const instance = this.instances.get(lensExtensionId); @@ -96,13 +97,12 @@ export class ExtensionLoader { } try { - instance.disable(); + await Promise.race([instance.disable(), delay(15 * 1000)]); // allow at most 15s to disable the instace this.events.emit("remove", instance); this.instances.delete(lensExtensionId); } catch (error) { logger.error(`${logModule}: deactivation extension error`, { lensExtensionId, error }); } - } removeExtension(lensExtensionId: LensExtensionId) { diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts index aaa6f60ac5..31d97e9acd 100644 --- a/src/extensions/lens-extension.ts +++ b/src/extensions/lens-extension.ts @@ -57,7 +57,7 @@ export class LensExtension { async enable() { if (this.isEnabled) return; this.isEnabled = true; - this.onActivate(); + await this.onActivate(); logger.info(`[EXTENSION]: enabled ${this.name}@${this.version}`); } @@ -65,7 +65,7 @@ export class LensExtension { async disable() { if (!this.isEnabled) return; this.isEnabled = false; - this.onDeactivate(); + await this.onDeactivate(); logger.info(`[EXTENSION]: disabled ${this.name}@${this.version}`); } @@ -101,11 +101,11 @@ export class LensExtension { }; } - protected onActivate() { + protected onActivate(): Promise | void { // mock } - protected onDeactivate() { + protected onDeactivate(): Promise | void { // mock } } diff --git a/src/main/helm/helm-chart-manager.ts b/src/main/helm/helm-chart-manager.ts index 69619a56d4..8aa08d7830 100644 --- a/src/main/helm/helm-chart-manager.ts +++ b/src/main/helm/helm-chart-manager.ts @@ -12,11 +12,9 @@ type CachedYaml = { export class HelmChartManager { protected cache: any = {}; - protected repo: HelmRepo; - constructor(repo: HelmRepo){ + constructor(protected repo: HelmRepo){ this.cache = HelmRepoManager.cache; - this.repo = repo; } public async chart(name: string) { diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index 67f0837034..8425bd934a 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -46,6 +46,10 @@ function parseLensExtensionManifest(buf: Buffer): LensExtensionManifest { return raw; } +function notifyOnAction(displayName: string, action: string): void { + Notifications.ok(

Extension {displayName} successfully {action}!

); +} + @observer export class Extensions extends React.Component { private static supportedFormats = ["tar", "tgz"]; @@ -95,9 +99,7 @@ export class Extensions extends React.Component { disposeOnUnmount(this, reaction(() => this.extensions, () => { this.removedUninstalling.forEach(({ id, displayName }) => { - Notifications.ok( -

Extension {displayName} successfully uninstalled!

- ); + notifyOnAction(displayName, "uninstalled"); this.extensionStateStore.extensionState.delete(id); }); @@ -108,9 +110,7 @@ export class Extensions extends React.Component { throw new Error("Extension not found"); } - Notifications.ok( -

Extension {displayName} successfully installed!

- ); + notifyOnAction(displayName, "installed"); this.extensionStateStore.extensionState.delete(id); this.installPath = ""; @@ -205,39 +205,36 @@ export class Extensions extends React.Component { ); }; - async preloadExtensions(requests: InstallRequest[], { showError = true } = {}) { - const preloadedRequests = requests.filter(request => request.data); + async preloadExtensions(requests: InstallRequest[], { showError = true } = {}): Promise { + const res = await Promise.all( + requests.map(async ({ data, fileName, filePath }) => { + if (data) { + return { data, fileName, filePath }; + } - await Promise.all( - requests - .filter(request => !request.data && request.filePath) - .map(async request => { + if (filePath) { try { - const data = await fse.readFile(request.filePath); - - request.data = data; - preloadedRequests.push(request); - - return request; + return { data: await fse.readFile(filePath), fileName, filePath }; } catch(error) { if (showError) { - Notifications.error(`Error while reading "${request.filePath}": ${String(error)}`); + Notifications.error(`Error while reading "${filePath}": ${String(error)}`); } } - }) + } + }) ); - return preloadedRequests as InstallRequestPreloaded[]; + return res.filter(Boolean); } async validatePackage(filePath: string): Promise { const tarFiles = await listTarEntries(filePath); // tarball from npm contains single root folder "package/*" - const firstFile = tarFiles[0]; + const [firstFile] = tarFiles; if (!firstFile) { - throw new Error(`invalid extension bundle, ${manifestFilename} not found`); + throw new Error(`invalid extension bundle, ${manifestFilename} not found`); } const rootFolder = path.normalize(firstFile).split(path.sep)[0]; @@ -255,7 +252,7 @@ export class Extensions extends React.Component { }); if (!manifest.lens && !manifest.renderer) { - throw new Error(`${manifestFilename} must specify "main" and/or "renderer" fields`); + throw new Error(`${manifestFilename} must specify at least one of: ["main", "renderer"]`); } return manifest; @@ -267,11 +264,9 @@ export class Extensions extends React.Component { // copy files to temp await fse.ensureDir(this.getExtensionPackageTemp()); - for (const request of requests) { - const tempFile = this.getExtensionPackageTemp(request.fileName); - - await fse.writeFile(tempFile, request.data); - } + await Promise.all( + requests.map(request => fse.writeFile(this.getExtensionPackageTemp(request.fileName), request.data)) + ); // validate packages await Promise.all(