From 3300a99a787663cb05d867be6a6b9ad1c11ce559 Mon Sep 17 00:00:00 2001 From: Panu Horsmalahti Date: Mon, 14 Dec 2020 09:23:59 +0200 Subject: [PATCH] Display error dialog if extensions couldn't be loaded (#1752) * Display error dialog if extensions couldn't be loaded * Reject npm install on failure using the process exit code Signed-off-by: Panu Horsmalahti --- src/extensions/extension-discovery.ts | 20 ++++++++++++++++---- src/extensions/extension-installer.ts | 20 +++++++++++++++----- src/main/index.ts | 26 +++++++++++++++----------- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/extensions/extension-discovery.ts b/src/extensions/extension-discovery.ts index 2b26d79a9c..73bddd7481 100644 --- a/src/extensions/extension-discovery.ts +++ b/src/extensions/extension-discovery.ts @@ -266,18 +266,30 @@ export class ExtensionDiscovery { logger.info(`${logModule} loading extensions from ${extensionInstaller.extensionPackagesRoot}`); - if (fs.existsSync(path.join(extensionInstaller.extensionPackagesRoot, "package-lock.json"))) { - await fs.remove(path.join(extensionInstaller.extensionPackagesRoot, "package-lock.json")); - } + // fs.remove won't throw if path is missing + await fs.remove(path.join(extensionInstaller.extensionPackagesRoot, "package-lock.json")); + try { + // Verify write access to static/extensions, which is needed for symlinking await fs.access(this.inTreeFolderPath, fs.constants.W_OK); + + // Set bundled folder path to static/extensions this.bundledFolderPath = this.inTreeFolderPath; } catch { - // we need to copy in-tree extensions so that we can symlink them properly on "npm install" + // If there is error accessing static/extensions, we need to copy in-tree extensions so that we can symlink them properly on "npm install". + // The error can happen if there is read-only rights to static/extensions, which would fail symlinking. + + // Remove e.g. /Users//Library/Application Support/LensDev/extensions await fs.remove(this.inTreeTargetPath); + + // Create folder e.g. /Users//Library/Application Support/LensDev/extensions await fs.ensureDir(this.inTreeTargetPath); + + // Copy static/extensions to e.g. /Users//Library/Application Support/LensDev/extensions await fs.copy(this.inTreeFolderPath, this.inTreeTargetPath); + + // Set bundled folder path to e.g. /Users//Library/Application Support/LensDev/extensions this.bundledFolderPath = this.inTreeTargetPath; } diff --git a/src/extensions/extension-installer.ts b/src/extensions/extension-installer.ts index 2143c62287..75b30d0b9a 100644 --- a/src/extensions/extension-installer.ts +++ b/src/extensions/extension-installer.ts @@ -33,16 +33,26 @@ export class ExtensionInstaller { installDependencies(): Promise { return new Promise((resolve, reject) => { logger.info(`${logModule} installing dependencies at ${extensionPackagesRoot()}`); - const child = child_process.fork(this.npmPath, ["install", "--silent", "--no-audit", "--only=prod", "--prefer-offline", "--no-package-lock"], { + const child = child_process.fork(this.npmPath, ["install", "--no-audit", "--only=prod", "--prefer-offline", "--no-package-lock"], { cwd: extensionPackagesRoot(), silent: true }); + let stderr = ""; - child.on("close", () => { - resolve(); + child.stderr.on("data", data => { + stderr += String(data); }); - child.on("error", (err) => { - reject(err); + + child.on("close", (code) => { + if (code !== 0) { + reject(new Error(stderr)); + } else { + resolve(); + } + }); + + child.on("error", error => { + reject(error); }); }); } diff --git a/src/main/index.ts b/src/main/index.ts index 1c6b91c2c4..6f682efb6b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -93,8 +93,8 @@ app.on("ready", async () => { // eslint-disable-next-line unused-imports/no-unused-vars-ts proxyServer = LensProxy.create(proxyPort, clusterManager); } catch (error) { - logger.error(`Could not start proxy (127.0.0:${proxyPort}): ${error.message}`); - dialog.showErrorBox("Lens Error", `Could not start proxy (127.0.0:${proxyPort}): ${error.message || "unknown error"}`); + logger.error(`Could not start proxy (127.0.0:${proxyPort}): ${error?.message}`); + dialog.showErrorBox("Lens Error", `Could not start proxy (127.0.0:${proxyPort}): ${error?.message || "unknown error"}`); app.exit(); } @@ -104,17 +104,21 @@ app.on("ready", async () => { windowManager = WindowManager.getInstance(proxyPort); // call after windowManager to see splash earlier - const extensions = await extensionDiscovery.load(); + try { + const extensions = await extensionDiscovery.load(); - // Subscribe to extensions that are copied or deleted to/from the extensions folder - extensionDiscovery.events.on("add", (extension: InstalledExtension) => { - extensionLoader.addExtension(extension); - }); - extensionDiscovery.events.on("remove", (lensExtensionId: LensExtensionId) => { - extensionLoader.removeExtension(lensExtensionId); - }); + // Subscribe to extensions that are copied or deleted to/from the extensions folder + extensionDiscovery.events.on("add", (extension: InstalledExtension) => { + extensionLoader.addExtension(extension); + }); + extensionDiscovery.events.on("remove", (lensExtensionId: LensExtensionId) => { + extensionLoader.removeExtension(lensExtensionId); + }); - extensionLoader.initExtensions(extensions); + extensionLoader.initExtensions(extensions); + } catch (error) { + dialog.showErrorBox("Lens Error", `Could not load extensions${error?.message ? `: ${error.message}` : ""}`); + } setTimeout(() => { appEventBus.emit({ name: "service", action: "start" });