From a19f094a0d1622bfbae88b41a69f311b6da5cc1d Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 9 Mar 2022 17:51:51 -0500 Subject: [PATCH] Consolidate downloading binaries into one script (#4942) * Consolidate downloading binaries into one script - Upgrade lens-k8s-proxy to 0.1.5 which allows us to remove the trailing slash in KubeAuthProxy - Consolidate nromalizing arch and os strings - Remove LensBinary as HelmCli is not just a raw object since it is always bundled - Introduce OnceCell and use it for the binary paths Signed-off-by: Sebastian Malton * Fully remove helmCli, move associated variable to vars Signed-off-by: Sebastian Malton * Fix helm downloading on windows Signed-off-by: Sebastian Malton * Don't download binaries for unsupported windows Signed-off-by: Sebastian Malton * Arch specific paths Signed-off-by: Sebastian Malton * Fix downloading helm on windows Signed-off-by: Sebastian Malton * rename once-cell file to lazy-initialized Signed-off-by: Sebastian Malton --- Makefile | 2 +- build/download_binaries.ts | 235 ++++++++++++++++++ build/download_helm.ts | 19 -- build/download_k8s_proxy.ts | 98 -------- build/download_kubectl.ts | 125 ---------- package.json | 39 ++- ...rectory-for-bundled-binaries.injectable.ts | 12 +- src/common/utils/lazy-initialized.ts | 35 +++ src/common/vars.ts | 49 ++++ src/main/helm/exec.ts | 23 ++ src/main/helm/helm-chart-manager.ts | 13 +- src/main/helm/helm-cli.ts | 49 ---- src/main/helm/helm-release-manager.ts | 18 +- src/main/helm/helm-repo-manager.ts | 21 +- .../create-kube-auth-proxy.injectable.ts | 16 +- src/main/kube-auth-proxy/kube-auth-proxy.ts | 8 +- src/main/kubectl/kubectl.ts | 50 +--- src/main/lens-binary.ts | 202 --------------- .../local-shell-session.ts | 11 +- .../+preferences/kubectl-binaries.tsx | 7 +- webpack.main.ts | 2 +- webpack.renderer.ts | 2 +- yarn.lock | 77 +++++- 23 files changed, 472 insertions(+), 641 deletions(-) create mode 100644 build/download_binaries.ts delete mode 100644 build/download_helm.ts delete mode 100644 build/download_k8s_proxy.ts delete mode 100644 build/download_kubectl.ts create mode 100644 src/common/utils/lazy-initialized.ts create mode 100644 src/main/helm/exec.ts delete mode 100644 src/main/helm/helm-cli.ts delete mode 100644 src/main/lens-binary.ts diff --git a/Makefile b/Makefile index 85de564e4b..1435ab5004 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ node_modules: yarn.lock yarn check --verify-tree --integrity binaries/client: node_modules - yarn download-bins + yarn download:binaries .PHONY: compile-dev compile-dev: node_modules diff --git a/build/download_binaries.ts b/build/download_binaries.ts new file mode 100644 index 0000000000..edae0e4535 --- /dev/null +++ b/build/download_binaries.ts @@ -0,0 +1,235 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import packageInfo from "../package.json"; +import { type WriteStream } from "fs"; +import { FileHandle, open } from "fs/promises"; +import { constants, ensureDir, unlink } from "fs-extra"; +import path from "path"; +import fetch from "node-fetch"; +import { promisify } from "util"; +import { pipeline as _pipeline, Transform, Writable } from "stream"; +import { MultiBar, SingleBar } from "cli-progress"; +import AbortController from "abort-controller"; +import { extract } from "tar-stream"; +import gunzip from "gunzip-maybe"; +import { getBinaryName, normalizedPlatform } from "../src/common/vars"; + +const pipeline = promisify(_pipeline); + +interface BinaryDownloaderArgs { + readonly version: string; + readonly platform: SupportedPlatform; + readonly downloadArch: string; + readonly fileArch: string; + readonly binaryName: string; + readonly baseDir: string; +} + +abstract class BinaryDownloader { + protected abstract readonly url: string; + protected readonly bar: SingleBar; + protected readonly target: string; + + protected getTransformStreams(file: Writable): (NodeJS.ReadWriteStream | NodeJS.WritableStream)[] { + return [file]; + } + + constructor(public readonly args: BinaryDownloaderArgs, multiBar: MultiBar) { + this.bar = multiBar.create(1, 0, args); + this.target = path.join(args.baseDir, args.platform, args.fileArch, args.binaryName); + } + + async ensureBinary(): Promise { + const controller = new AbortController(); + const stream = await fetch(this.url, { + timeout: 15 * 60 * 1000, // 15min + signal: controller.signal, + }); + const total = Number(stream.headers.get("content-length")); + const bar = this.bar; + let fileHandle: FileHandle; + + if (isNaN(total)) { + throw new Error("no content-length header was present"); + } + + bar.setTotal(total); + + await ensureDir(path.dirname(this.target), 0o755); + + try { + /** + * This is necessary because for some reason `createWriteStream({ flags: "wx" })` + * was throwing someplace else and not here + */ + fileHandle = await open(this.target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL); + + await pipeline( + stream.body, + new Transform({ + transform(chunk, encoding, callback) { + bar.increment(chunk.length); + this.push(chunk); + callback(); + }, + }), + ...this.getTransformStreams(new Writable({ + write(chunk, encoding, cb) { + fileHandle.write(chunk) + .then(() => cb()) + .catch(cb); + }, + })), + ); + await fileHandle.chmod(0o755); + await fileHandle.close(); + } catch (error) { + await fileHandle?.close(); + + if (error.code === "EEXIST") { + bar.increment(total); // mark as finished + controller.abort(); // stop trying to download + } else { + await unlink(this.target); + throw error; + } + } + } +} + +class LensK8sProxyDownloader extends BinaryDownloader { + protected readonly url: string; + + constructor(args: Omit, bar: MultiBar) { + const binaryName = getBinaryName("lens-k8s-proxy", { forPlatform: args.platform }); + + super({ ...args, binaryName }, bar); + this.url = `https://github.com/lensapp/lens-k8s-proxy/releases/download/v${args.version}/lens-k8s-proxy-${args.platform}-${args.downloadArch}`; + } +} + +class KubectlDownloader extends BinaryDownloader { + protected readonly url: string; + + constructor(args: Omit, bar: MultiBar) { + const binaryName = getBinaryName("kubectl", { forPlatform: args.platform }); + + super({ ...args, binaryName }, bar); + this.url = `https://storage.googleapis.com/kubernetes-release/release/v${args.version}/bin/${args.platform}/${args.downloadArch}/${binaryName}`; + } +} + +class HelmDownloader extends BinaryDownloader { + protected readonly url: string; + + constructor(args: Omit, bar: MultiBar) { + const binaryName = getBinaryName("helm", { forPlatform: args.platform }); + + super({ ...args, binaryName }, bar); + this.url = `https://get.helm.sh/helm-v${args.version}-${args.platform}-${args.downloadArch}.tar.gz`; + } + + protected getTransformStreams(file: WriteStream) { + const extracting = extract({ + allowUnknownFormat: false, + }); + + extracting.on("entry", (headers, stream, next) => { + if (headers.name.endsWith(this.args.binaryName)) { + stream + .pipe(file) + .once("finish", () => next()) + .once("error", next); + } else { + stream.resume(); + next(); + } + }); + + return [gunzip(3), extracting]; + } +} + +type SupportedPlatform = "darwin" | "linux" | "windows"; + +async function main() { + const multiBar = new MultiBar({ + align: "left", + clearOnComplete: false, + hideCursor: true, + autopadding: true, + noTTYOutput: true, + format: "[{bar}] {percentage}% | {downloadArch} {binaryName}", + }); + const baseDir = path.join(__dirname, "..", "binaries", "client"); + const downloaders: BinaryDownloader[] = [ + new LensK8sProxyDownloader({ + version: packageInfo.config.k8sProxyVersion, + platform: normalizedPlatform, + downloadArch: "amd64", + fileArch: "x64", + baseDir, + }, multiBar), + new KubectlDownloader({ + version: packageInfo.config.bundledKubectlVersion, + platform: normalizedPlatform, + downloadArch: "amd64", + fileArch: "x64", + baseDir, + }, multiBar), + new HelmDownloader({ + version: packageInfo.config.bundledHelmVersion, + platform: normalizedPlatform, + downloadArch: "amd64", + fileArch: "x64", + baseDir, + }, multiBar), + ]; + + if (normalizedPlatform === "darwin") { + downloaders.push( + new LensK8sProxyDownloader({ + version: packageInfo.config.k8sProxyVersion, + platform: normalizedPlatform, + downloadArch: "arm64", + fileArch: "arm64", + baseDir, + }, multiBar), + new KubectlDownloader({ + version: packageInfo.config.bundledKubectlVersion, + platform: normalizedPlatform, + downloadArch: "arm64", + fileArch: "arm64", + baseDir, + }, multiBar), + new HelmDownloader({ + version: packageInfo.config.bundledHelmVersion, + platform: normalizedPlatform, + downloadArch: "arm64", + fileArch: "arm64", + baseDir, + }, multiBar), + ); + } + + const settledResults = await Promise.allSettled(downloaders.map(downloader => ( + downloader.ensureBinary() + .catch(error => { + throw new Error(`Failed to download ${downloader.args.binaryName} for ${downloader.args.platform}/${downloader.args.downloadArch}: ${error}`); + }) + ))); + + multiBar.stop(); + const errorResult = settledResults.find(res => res.status === "rejected") as PromiseRejectedResult | undefined; + + if (errorResult) { + console.error("234", String(errorResult.reason)); + process.exit(1); + } + + process.exit(0); +} + +main().catch(error => console.error("from main", error)); diff --git a/build/download_helm.ts b/build/download_helm.ts deleted file mode 100644 index 3fc931327d..0000000000 --- a/build/download_helm.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import packageInfo from "../package.json"; -import { isWindows } from "../src/common/vars"; -import { HelmCli } from "../src/main/helm/helm-cli"; -import * as path from "path"; - -const helmVersion = packageInfo.config.bundledHelmVersion; - -if (!isWindows) { - Promise.all([ - new HelmCli(path.join(process.cwd(), "binaries", "client", "x64"), helmVersion).ensureBinary(), - new HelmCli(path.join(process.cwd(), "binaries", "client", "arm64"), helmVersion).ensureBinary(), - ]); -} else { - new HelmCli(path.join(process.cwd(), "binaries", "client", "x64"), helmVersion).ensureBinary(); -} diff --git a/build/download_k8s_proxy.ts b/build/download_k8s_proxy.ts deleted file mode 100644 index 891f4f573c..0000000000 --- a/build/download_k8s_proxy.ts +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import packageInfo from "../package.json"; -import fs from "fs"; -import request from "request"; -import { ensureDir, pathExists } from "fs-extra"; -import path from "path"; -import { noop } from "lodash"; -import { isLinux, isMac } from "../src/common/vars"; - -class K8sProxyDownloader { - public version: string; - protected url: string; - protected path: string; - protected dirname: string; - - constructor(version: string, platform: string, arch: string, target: string) { - this.version = version; - this.url = `https://github.com/lensapp/lens-k8s-proxy/releases/download/v${this.version}/lens-k8s-proxy-${platform}-${arch}`; - this.dirname = path.dirname(target); - this.path = target; - } - - public async checkBinary() { - const exists = await pathExists(this.path); - - if (exists) { - return true; - } - - return false; - } - - public async download() { - if (await this.checkBinary()) { - return console.log("Already exists"); - } - - await ensureDir(path.dirname(this.path), 0o755); - - const file = fs.createWriteStream(this.path); - - console.log(`Downloading lens-k8s-proxy ${this.version} from ${this.url} to ${this.path}`); - const requestOpts: request.UriOptions & request.CoreOptions = { - uri: this.url, - gzip: true, - followAllRedirects: true, - }; - const stream = request(requestOpts); - - stream.on("complete", () => { - console.log("lens-k8s-proxy binary download finished"); - file.end(noop); - }); - - stream.on("error", (error) => { - console.log(error); - fs.unlink(this.path, noop); - throw error; - }); - - return new Promise((resolve, reject) => { - file.on("close", () => { - console.log("lens-k8s-proxy binary download closed"); - fs.chmod(this.path, 0o755, (err) => { - if (err) reject(err); - }); - resolve(); - }); - stream.pipe(file); - }); - } -} -const downloadVersion = packageInfo.config.k8sProxyVersion; -const baseDir = path.join(__dirname, "..", "binaries", "client"); - -const downloads = []; - -if (isMac) { - downloads.push({ platform: "darwin", arch: "amd64", target: path.join(baseDir, "darwin", "x64", "lens-k8s-proxy") }); - downloads.push({ platform: "darwin", arch: "arm64", target: path.join(baseDir, "darwin", "arm64", "lens-k8s-proxy") }); -} else if (isLinux) { - downloads.push({ platform: "linux", arch: "amd64", target: path.join(baseDir, "linux", "x64", "lens-k8s-proxy") }); - downloads.push({ platform: "linux", arch: "arm64", target: path.join(baseDir, "linux", "arm64", "lens-k8s-proxy") }); -} else { - downloads.push({ platform: "windows", arch: "amd64", target: path.join(baseDir, "windows", "x64", "lens-k8s-proxy.exe") }); - downloads.push({ platform: "windows", arch: "386", target: path.join(baseDir, "windows", "ia32", "lens-k8s-proxy.exe") }); -} - -downloads.forEach((dlOpts) => { - console.log(dlOpts); - const downloader = new K8sProxyDownloader(downloadVersion, dlOpts.platform, dlOpts.arch, dlOpts.target); - - console.log(`Downloading: ${JSON.stringify(dlOpts)}`); - downloader.download().then(() => downloader.checkBinary().then(() => console.log("Download complete"))); -}); diff --git a/build/download_kubectl.ts b/build/download_kubectl.ts deleted file mode 100644 index 8e85ac0c98..0000000000 --- a/build/download_kubectl.ts +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import packageInfo from "../package.json"; -import fs from "fs"; -import request from "request"; -import md5File from "md5-file"; -import requestPromise from "request-promise-native"; -import { ensureDir, pathExists } from "fs-extra"; -import path from "path"; -import { noop } from "lodash"; -import { isLinux, isMac } from "../src/common/vars"; - -class KubectlDownloader { - public kubectlVersion: string; - protected url: string; - protected path: string; - protected dirname: string; - - constructor(clusterVersion: string, platform: string, arch: string, target: string) { - this.kubectlVersion = clusterVersion; - const binaryName = platform === "windows" ? "kubectl.exe" : "kubectl"; - - this.url = `https://storage.googleapis.com/kubernetes-release/release/v${this.kubectlVersion}/bin/${platform}/${arch}/${binaryName}`; - this.dirname = path.dirname(target); - this.path = target; - } - - protected async urlEtag() { - const response = await requestPromise({ - method: "HEAD", - uri: this.url, - resolveWithFullResponse: true, - }).catch(console.error); - - if (response.headers["etag"]) { - return response.headers["etag"].replace(/"/g, ""); - } - - return ""; - } - - public async checkBinary() { - const exists = await pathExists(this.path); - - if (exists) { - const hash = md5File.sync(this.path); - const etag = await this.urlEtag(); - - if (hash == etag) { - console.log("Kubectl md5sum matches the remote etag"); - - return true; - } - - console.log(`Kubectl md5sum ${hash} does not match the remote etag ${etag}, unlinking and downloading again`); - await fs.promises.unlink(this.path); - } - - return false; - } - - public async downloadKubectl() { - if (await this.checkBinary()) { - return console.log("Already exists and is valid"); - } - - await ensureDir(path.dirname(this.path), 0o755); - - const file = fs.createWriteStream(this.path); - - console.log(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); - const requestOpts: request.UriOptions & request.CoreOptions = { - uri: this.url, - gzip: true, - }; - const stream = request(requestOpts); - - stream.on("complete", () => { - console.log("kubectl binary download finished"); - file.end(noop); - }); - - stream.on("error", (error) => { - console.log(error); - fs.unlink(this.path, noop); - throw error; - }); - - return new Promise((resolve, reject) => { - file.on("close", () => { - console.log("kubectl binary download closed"); - fs.chmod(this.path, 0o755, (err) => { - if (err) reject(err); - }); - resolve(); - }); - stream.pipe(file); - }); - } -} -const downloadVersion = packageInfo.config.bundledKubectlVersion; -const baseDir = path.join(__dirname, "..", "binaries", "client"); - -const downloads = []; - -if (isMac) { - downloads.push({ platform: "darwin", arch: "amd64", target: path.join(baseDir, "darwin", "x64", "kubectl") }); - downloads.push({ platform: "darwin", arch: "arm64", target: path.join(baseDir, "darwin", "arm64", "kubectl") }); -} else if (isLinux) { - downloads.push({ platform: "linux", arch: "amd64", target: path.join(baseDir, "linux", "x64", "kubectl") }); - downloads.push({ platform: "linux", arch: "arm64", target: path.join(baseDir, "linux", "arm64", "kubectl") }); -} else { - downloads.push({ platform: "windows", arch: "amd64", target: path.join(baseDir, "windows", "x64", "kubectl.exe") }); - downloads.push({ platform: "windows", arch: "386", target: path.join(baseDir, "windows", "ia32", "kubectl.exe") }); -} - -downloads.forEach((dlOpts) => { - console.log(dlOpts); - const downloader = new KubectlDownloader(downloadVersion, dlOpts.platform, dlOpts.arch, dlOpts.target); - - console.log(`Downloading: ${JSON.stringify(dlOpts)}`); - downloader.downloadKubectl().then(() => downloader.checkBinary().then(() => console.log("Download complete"))); -}); diff --git a/package.json b/package.json index acf1262f03..bf4467785e 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,7 @@ "integration": "jest --runInBand --detectOpenHandles --forceExit integration", "dist": "yarn run compile && electron-builder --publish onTag", "dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null", - "download-bins": "concurrently yarn:download:*", - "download:kubectl": "yarn run ts-node build/download_kubectl.ts", - "download:helm": "yarn run ts-node build/download_helm.ts", - "download:k8s-proxy": "yarn run ts-node build/download_k8s_proxy.ts", + "download:binaries": "yarn run ts-node build/download_binaries.ts", "build:tray-icons": "yarn run ts-node build/build_tray_icon.ts", "build:theme-vars": "yarn run ts-node build/build_theme_vars.ts", "lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .", @@ -135,8 +132,8 @@ "to": "./${arch}/lens-k8s-proxy" }, { - "from": "binaries/client/${arch}/helm3/helm3", - "to": "./helm3/helm3" + "from": "binaries/client/linux/${arch}/helm", + "to": "./${arch}/helm" } ] }, @@ -160,8 +157,8 @@ "to": "./${arch}/lens-k8s-proxy" }, { - "from": "binaries/client/${arch}/helm3/helm3", - "to": "./helm3/helm3" + "from": "binaries/client/darwin/${arch}/helm", + "to": "./${arch}/helm" } ] }, @@ -171,24 +168,16 @@ ], "extraResources": [ { - "from": "binaries/client/windows/x64/kubectl.exe", - "to": "./x64/kubectl.exe" + "from": "binaries/client/windows/${arch}/kubectl.exe", + "to": "./${arch}/kubectl.exe" }, { - "from": "binaries/client/windows/ia32/kubectl.exe", - "to": "./ia32/kubectl.exe" + "from": "binaries/client/windows/${arch}/lens-k8s-proxy.exe", + "to": "./${arch}/lens-k8s-proxy.exe" }, { - "from": "binaries/client/windows/x64/lens-k8s-proxy", - "to": "./x64/lens-k8s-proxy.exe" - }, - { - "from": "binaries/client/windows/ia32/lens-k8s-proxy", - "to": "./ia32/lens-k8s-proxy.exe" - }, - { - "from": "binaries/client/x64/helm3/helm3.exe", - "to": "./helm3/helm3.exe" + "from": "binaries/client/windows/${arch}/helm.exe", + "to": "./${arch}/helm.exe" } ] }, @@ -292,12 +281,14 @@ "@testing-library/user-event": "^13.5.0", "@types/byline": "^4.2.33", "@types/chart.js": "^2.9.34", + "@types/cli-progress": "^3.9.2", "@types/color": "^3.0.2", "@types/crypto-js": "^3.1.47", "@types/dompurify": "^2.3.1", "@types/electron-devtools-installer": "^2.2.0", "@types/fs-extra": "^9.0.13", "@types/glob-to-regexp": "^0.4.1", + "@types/gunzip-maybe": "^1.4.0", "@types/hoist-non-react-statics": "^3.3.1", "@types/html-webpack-plugin": "^3.2.6", "@types/http-proxy": "^1.17.7", @@ -330,6 +321,7 @@ "@types/sharp": "^0.29.4", "@types/spdy": "^3.4.5", "@types/tar": "^4.0.5", + "@types/tar-stream": "^2.2.2", "@types/tcp-port-used": "^1.0.0", "@types/tempy": "^0.3.0", "@types/triple-beam": "^1.3.2", @@ -344,6 +336,7 @@ "ansi_up": "^5.1.0", "chart.js": "^2.9.4", "circular-dependency-plugin": "^5.2.2", + "cli-progress": "^3.10.0", "color": "^3.2.1", "concurrently": "^7.0.0", "css-loader": "^6.5.1", @@ -362,6 +355,7 @@ "eslint-plugin-unused-imports": "^2.0.0", "flex.box": "^3.4.4", "fork-ts-checker-webpack-plugin": "^6.5.0", + "gunzip-maybe": "^1.4.2", "hoist-non-react-statics": "^3.3.2", "html-webpack-plugin": "^5.5.0", "ignore-loader": "^0.1.2", @@ -392,6 +386,7 @@ "sharp": "^0.29.3", "style-loader": "^3.3.1", "tailwindcss": "^3.0.7", + "tar-stream": "^2.2.0", "ts-jest": "26.5.6", "ts-loader": "^9.2.6", "ts-node": "^10.4.0", diff --git a/src/common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable.ts b/src/common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable.ts index f1f279e0b8..7ac1a21a1e 100644 --- a/src/common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable.ts +++ b/src/common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable.ts @@ -3,19 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import path from "path"; -import isDevelopmentInjectable from "../../vars/is-development.injectable"; -import contextDirInjectable from "../../vars/context-dir.injectable"; +import { baseBinariesDir } from "../../vars"; const directoryForBundledBinariesInjectable = getInjectable({ id: "directory-for-bundled-binaries", - instantiate: (di) => { - if (di.inject(isDevelopmentInjectable)) { - return path.join(di.inject(contextDirInjectable), "binaries"); - } - - return process.resourcesPath; - }, + instantiate: () => baseBinariesDir.get(), lifecycle: lifecycleEnum.singleton, }); diff --git a/src/common/utils/lazy-initialized.ts b/src/common/utils/lazy-initialized.ts new file mode 100644 index 0000000000..866999e208 --- /dev/null +++ b/src/common/utils/lazy-initialized.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +/** + * A OnceCell is an object that wraps some function that produces a value. + * + * It then only calls the function on the first call to `get()` and returns the + * same instance/value on every subsequent call. + */ +export interface LazyInitialized { + get(): T; +} + +/** + * A function to make a `OnceCell` + */ +export function lazyInitialized(builder: () => T): LazyInitialized { + let value: T | undefined; + let called = false; + + return { + get() { + if (called) { + return value; + } + + value = builder(); + called = true; + + return value; + }, + }; +} diff --git a/src/common/vars.ts b/src/common/vars.ts index 149f8319f7..1b9b9a92d1 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -8,6 +8,7 @@ import path from "path"; import { SemVer } from "semver"; import packageInfo from "../../package.json"; import { defineGlobal } from "./utils/defineGlobal"; +import { lazyInitialized } from "./utils/lazy-initialized"; export const isMac = process.platform === "darwin"; export const isWindows = process.platform === "win32"; @@ -29,6 +30,54 @@ export const defaultTheme = "lens-dark" as string; export const defaultFontSize = 12; export const defaultTerminalFontFamily = "RobotoMono"; export const defaultEditorFontFamily = "RobotoMono"; +export const normalizedPlatform = (() => { + switch (process.platform) { + case "darwin": + return "darwin"; + case "linux": + return "linux"; + case "win32": + return "windows"; + default: + throw new Error(`platform=${process.platform} is unsupported`); + } +})(); +export const normalizedArch = (() => { + switch (process.arch) { + case "arm64": + return "arm64"; + case "x64": + case "amd64": + return "x64"; + case "386": + case "x32": + case "ia32": + return "ia32"; + default: + throw new Error(`arch=${process.arch} is unsupported`); + } +})(); + +export function getBinaryName(name: string, { forPlatform = normalizedPlatform } = {}): string { + if (forPlatform === "windows") { + return `${name}.exe`; + } + + return name; +} + +const resourcesDir = lazyInitialized(() => ( + isProduction + ? process.resourcesPath + : path.join(process.cwd(), "binaries", "client", normalizedPlatform) +)); + +export const baseBinariesDir = lazyInitialized(() => path.join(resourcesDir.get(), normalizedArch)); +export const kubeAuthProxyBinaryName = getBinaryName("lens-k8s-proxy"); +export const helmBinaryName = getBinaryName("helm"); +export const helmBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), helmBinaryName)); +export const kubectlBinaryName = getBinaryName("kubectl"); +export const kubectlBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), kubectlBinaryName)); // Webpack build paths export const contextDir = process.cwd(); diff --git a/src/main/helm/exec.ts b/src/main/helm/exec.ts new file mode 100644 index 0000000000..8d8d0d4340 --- /dev/null +++ b/src/main/helm/exec.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { promiseExecFile } from "../../common/utils/promise-exec"; +import type { BaseEncodingOptions } from "fs"; +import type { ExecFileOptions } from "child_process"; +import { helmBinaryPath } from "../../common/vars"; + +/** + * ExecFile the bundled helm CLI + * @returns STDOUT + */ +export async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise { + try { + const { stdout } = await promiseExecFile(helmBinaryPath.get(), args, options); + + return stdout; + } catch (error) { + throw error?.stderr || error; + } +} diff --git a/src/main/helm/helm-chart-manager.ts b/src/main/helm/helm-chart-manager.ts index 5630848f85..5c15bfc6b8 100644 --- a/src/main/helm/helm-chart-manager.ts +++ b/src/main/helm/helm-chart-manager.ts @@ -8,10 +8,9 @@ import v8 from "v8"; import * as yaml from "js-yaml"; import type { HelmRepo } from "./helm-repo-manager"; import logger from "../logger"; -import { promiseExecFile } from "../../common/utils/promise-exec"; -import { helmCli } from "./helm-cli"; import type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api"; import { iter, sortCharts } from "../../common/utils"; +import { execHelm } from "./exec"; interface ChartCacheEntry { data: Buffer; @@ -49,21 +48,13 @@ export class HelmChartManager { } private async executeCommand(args: string[], name: string, version?: string) { - const helm = await helmCli.binaryPath(); - args.push(`${this.repo.name}/${name}`); if (version) { args.push("--version", version); } - try { - const { stdout } = await promiseExecFile(helm, args); - - return stdout; - } catch (error) { - throw error.stderr || error; - } + return execHelm(args); } public async getReadme(name: string, version?: string) { diff --git a/src/main/helm/helm-cli.ts b/src/main/helm/helm-cli.ts deleted file mode 100644 index 295631f691..0000000000 --- a/src/main/helm/helm-cli.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import packageInfo from "../../../package.json"; -import path from "path"; -import { LensBinary, LensBinaryOpts } from "../lens-binary"; -import { isProduction } from "../../common/vars"; - -export class HelmCli extends LensBinary { - - public constructor(baseDir: string, version: string) { - const opts: LensBinaryOpts = { - version, - baseDir, - originalBinaryName: "helm", - newBinaryName: "helm3", - }; - - super(opts); - } - - protected getTarName(): string | null { - return `${this.binaryName}-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`; - } - - protected getUrl() { - return `https://get.helm.sh/helm-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`; - } - - protected getBinaryPath() { - return path.join(this.dirname, this.binaryName); - } - - protected getOriginalBinaryPath() { - return path.join(this.dirname, `${this.platformName}-${this.arch}`, this.originalBinaryName); - } -} - -const helmVersion = packageInfo.config.bundledHelmVersion; -let baseDir = process.resourcesPath; - -if (!isProduction) { - baseDir = path.join(process.cwd(), "binaries", "client", process.arch); -} - -export const helmCli = new HelmCli(baseDir, helmVersion); - diff --git a/src/main/helm/helm-release-manager.ts b/src/main/helm/helm-release-manager.ts index 72704eb020..e3de7ab3f7 100644 --- a/src/main/helm/helm-release-manager.ts +++ b/src/main/helm/helm-release-manager.ts @@ -6,23 +6,9 @@ import tempy from "tempy"; import fse from "fs-extra"; import * as yaml from "js-yaml"; -import { promiseExecFile } from "../../common/utils/promise-exec"; -import { helmCli } from "./helm-cli"; import { toCamelCase } from "../../common/utils/camelCase"; -import type { BaseEncodingOptions } from "fs"; -import { execFile, ExecFileOptions } from "child_process"; - -async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise { - const helmCliPath = await helmCli.binaryPath(); - - try { - const { stdout } = await promiseExecFile(helmCliPath, args, options); - - return stdout; - } catch (error) { - throw error?.stderr || error; - } -} +import { execFile } from "child_process"; +import { execHelm } from "./exec"; export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise[]> { const args = [ diff --git a/src/main/helm/helm-repo-manager.ts b/src/main/helm/helm-repo-manager.ts index 3674654a29..7f174cfa8e 100644 --- a/src/main/helm/helm-repo-manager.ts +++ b/src/main/helm/helm-repo-manager.ts @@ -4,14 +4,12 @@ */ import yaml from "js-yaml"; -import { BaseEncodingOptions, readFile } from "fs-extra"; -import { promiseExecFile } from "../../common/utils/promise-exec"; -import { helmCli } from "./helm-cli"; +import { readFile } from "fs-extra"; import { Singleton } from "../../common/utils/singleton"; import { customRequestPromise } from "../../common/request"; import orderBy from "lodash/orderBy"; import logger from "../logger"; -import type { ExecFileOptions } from "child_process"; +import { execHelm } from "./exec"; export type HelmEnv = Record & { HELM_REPOSITORY_CACHE?: string; @@ -34,18 +32,6 @@ export interface HelmRepo { password?: string; } -async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise { - const helmCliPath = await helmCli.binaryPath(); - - try { - const { stdout } = await promiseExecFile(helmCliPath, args, options); - - return stdout; - } catch (error) { - throw error?.stderr || error; - } -} - export class HelmRepoManager extends Singleton { protected repos: HelmRepo[]; protected helmEnv: HelmEnv; @@ -63,9 +49,6 @@ export class HelmRepoManager extends Singleton { } private async ensureInitialized() { - helmCli.setLogger(logger); - await helmCli.ensureBinary(); - this.helmEnv ??= await this.parseHelmEnv(); const repos = await this.list(); diff --git a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts index 411cfddc0c..3e3327dd46 100644 --- a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts +++ b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts @@ -3,24 +3,24 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { KubeAuthProxy } from "./kube-auth-proxy"; +import { KubeAuthProxy, KubeAuthProxyDependencies } from "./kube-auth-proxy"; import type { Cluster } from "../../common/cluster/cluster"; import path from "path"; -import { isDevelopment, isWindows } from "../../common/vars"; +import { getBinaryName } from "../../common/vars"; import directoryForBundledBinariesInjectable from "../../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable"; const createKubeAuthProxyInjectable = getInjectable({ id: "create-kube-auth-proxy", instantiate: (di) => { - const binaryName = isWindows ? "lens-k8s-proxy.exe" : "lens-k8s-proxy"; - const proxyPath = isDevelopment ? path.join("client", process.platform, process.arch) : process.arch; - const dependencies = { - proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), proxyPath, binaryName), + const binaryName = getBinaryName("lens-k8s-proxy"); + const dependencies: KubeAuthProxyDependencies = { + proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), binaryName), }; - return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => - new KubeAuthProxy(dependencies, cluster, environmentVariables); + return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => ( + new KubeAuthProxy(dependencies, cluster, environmentVariables) + ); }, }); diff --git a/src/main/kube-auth-proxy/kube-auth-proxy.ts b/src/main/kube-auth-proxy/kube-auth-proxy.ts index addc11b53b..13c1bc1a82 100644 --- a/src/main/kube-auth-proxy/kube-auth-proxy.ts +++ b/src/main/kube-auth-proxy/kube-auth-proxy.ts @@ -13,7 +13,7 @@ import { makeObservable, observable, when } from "mobx"; const startingServeRegex = /starting to serve on (?
.+)/i; -interface Dependencies { +export interface KubeAuthProxyDependencies { proxyBinPath: string; } @@ -28,7 +28,7 @@ export class KubeAuthProxy { protected proxyProcess?: ChildProcess; @observable protected ready = false; - constructor(private dependencies: Dependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) { + constructor(private dependencies: KubeAuthProxyDependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) { makeObservable(this); } @@ -42,8 +42,8 @@ export class KubeAuthProxy { } const proxyBin = this.dependencies.proxyBinPath; - - this.proxyProcess = spawn(proxyBin, [], { + + this.proxyProcess = spawn(proxyBin, [], { env: { ...this.env, KUBECONFIG: this.cluster.kubeConfigPath, diff --git a/src/main/kubectl/kubectl.ts b/src/main/kubectl/kubectl.ts index 5b083eeb91..690b9970b9 100644 --- a/src/main/kubectl/kubectl.ts +++ b/src/main/kubectl/kubectl.ts @@ -9,9 +9,8 @@ import { promiseExecFile } from "../../common/utils/promise-exec"; import logger from "../logger"; import { ensureDir, pathExists } from "fs-extra"; import * as lockFile from "proper-lockfile"; -import { helmCli } from "../helm/helm-cli"; import { getBundledKubectlVersion } from "../../common/utils/app-version"; -import { isDevelopment, isWindows, isTestEnv } from "../../common/vars"; +import { normalizedPlatform, normalizedArch, kubectlBinaryName, kubectlBinaryPath, baseBinariesDir } from "../../common/vars"; import { SemVer } from "semver"; import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers"; import got from "got/dist/source"; @@ -39,27 +38,8 @@ const kubectlMap: Map = new Map([ ["1.22", "1.22.6"], ["1.23", bundledVersion], ]); -let bundledPath: string; const initScriptVersionString = "# lens-initscript v3"; -export function bundledKubectlPath(): string { - if (bundledPath) { return bundledPath; } - - if (isDevelopment || isTestEnv) { - const platformName = isWindows ? "windows" : process.platform; - - bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl"); - } else { - bundledPath = path.join(process.resourcesPath, process.arch, "kubectl"); - } - - if (isWindows) { - bundledPath = `${bundledPath}.exe`; - } - - return bundledPath; -} - interface Dependencies { directoryForKubectlBinaries: string; @@ -102,27 +82,13 @@ export class Kubectl { logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`); } - let arch = null; - - if (process.arch == "x64") { - arch = "amd64"; - } else if (process.arch == "x86" || process.arch == "ia32") { - arch = "386"; - } else { - arch = process.arch; - } - - const platformName = isWindows ? "windows" : process.platform; - const binaryName = isWindows ? "kubectl.exe" : "kubectl"; - - this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`; - + this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${normalizedPlatform}/${normalizedArch}/${kubectlBinaryName}`; this.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion)); - this.path = path.join(this.dirname, binaryName); + this.path = path.join(this.dirname, kubectlBinaryName); } public getBundledPath() { - return bundledKubectlPath(); + return kubectlBinaryPath.get(); } public getPathFromPreferences() { @@ -316,7 +282,7 @@ export class Kubectl { ? this.dirname : path.dirname(this.getPathFromPreferences()); - const helmPath = helmCli.getBinaryDir(); + const binariesDir = baseBinariesDir.get(); const bashScriptPath = path.join(this.dirname, ".bash_set_path"); const bashScript = [ @@ -330,7 +296,7 @@ export class Kubectl { "elif test -f \"$HOME/.profile\"; then", " . \"$HOME/.profile\"", "fi", - `export PATH="${helmPath}:${kubectlPath}:$PATH"`, + `export PATH="${binariesDir}:${kubectlPath}:$PATH"`, 'export KUBECONFIG="$tempkubeconfig"', `NO_PROXY=",\${NO_PROXY:-localhost},"`, `NO_PROXY="\${NO_PROXY//,localhost,/,}"`, @@ -356,12 +322,12 @@ export class Kubectl { // voodoo to replace any previous occurrences of kubectl path in the PATH `kubectlpath="${kubectlPath}"`, - `helmpath="${helmPath}"`, + `binariesDir="${binariesDir}"`, "p=\":$kubectlpath:\"", "d=\":$PATH:\"", `d=\${d//$p/:}`, `d=\${d/#:/}`, - `export PATH="$helmpath:$kubectlpath:\${d/%:/}"`, + `export PATH="$binariesDir:$kubectlpath:\${d/%:/}"`, "export KUBECONFIG=\"$tempkubeconfig\"", `NO_PROXY=",\${NO_PROXY:-localhost},"`, `NO_PROXY="\${NO_PROXY//,localhost,/,}"`, diff --git a/src/main/lens-binary.ts b/src/main/lens-binary.ts deleted file mode 100644 index 63841585fd..0000000000 --- a/src/main/lens-binary.ts +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import fs from "fs"; -import request from "request"; -import { ensureDir, pathExists } from "fs-extra"; -import * as tar from "tar"; -import { isWindows } from "../common/vars"; -import type winston from "winston"; - -export interface LensBinaryOpts { - version: string; - baseDir: string; - originalBinaryName: string; - newBinaryName?: string; - requestOpts?: request.Options; -} - -export class LensBinary { - - public binaryVersion: string; - protected directory: string; - protected url: string; - protected path: string; - protected tarPath: string; - protected dirname: string; - protected binaryName: string; - protected platformName: string; - protected arch: string; - protected originalBinaryName: string; - protected requestOpts: request.Options; - protected logger: Console | winston.Logger; - - constructor(opts: LensBinaryOpts) { - const baseDir = opts.baseDir; - - this.originalBinaryName = opts.originalBinaryName; - this.binaryName = opts.newBinaryName || opts.originalBinaryName; - this.binaryVersion = opts.version; - this.requestOpts = opts.requestOpts; - this.logger = console; - let arch = null; - - if (process.env.BINARY_ARCH) { - arch = process.env.BINARY_ARCH; - } else if (process.arch == "x64") { - arch = "amd64"; - } else if (process.arch == "x86" || process.arch == "ia32") { - arch = "386"; - } else { - arch = process.arch; - } - this.arch = arch; - this.platformName = isWindows ? "windows" : process.platform; - this.dirname = path.normalize(path.join(baseDir, this.binaryName)); - - if (isWindows) { - this.binaryName = `${this.binaryName}.exe`; - this.originalBinaryName = `${this.originalBinaryName}.exe`; - } - const tarName = this.getTarName(); - - if (tarName) { - this.tarPath = path.join(this.dirname, tarName); - } - } - - public setLogger(logger: Console | winston.Logger) { - this.logger = logger; - } - - protected binaryDir() { - throw new Error("binaryDir not implemented"); - } - - public async binaryPath() { - await this.ensureBinary(); - - return this.getBinaryPath(); - } - - protected getTarName(): string | null { - return null; - } - - protected getUrl() { - return ""; - } - - protected getBinaryPath() { - return ""; - } - - protected getOriginalBinaryPath() { - return ""; - } - - public getBinaryDir() { - return path.dirname(this.getBinaryPath()); - } - - public async binDir() { - try { - await this.ensureBinary(); - - return this.dirname; - } catch (err) { - this.logger.error(err); - - return ""; - } - } - - protected async checkBinary() { - const exists = await pathExists(this.getBinaryPath()); - - return exists; - } - - public async ensureBinary() { - const isValid = await this.checkBinary(); - - if (!isValid) { - await this.downloadBinary().catch((error) => { - this.logger.error(error); - }); - if (this.tarPath) await this.untarBinary(); - if (this.originalBinaryName != this.binaryName) await this.renameBinary(); - this.logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`); - } - } - - protected async untarBinary() { - return new Promise(resolve => { - this.logger.debug(`Extracting ${this.originalBinaryName} binary`); - tar.x({ - file: this.tarPath, - cwd: this.dirname, - }).then((() => { - resolve(); - })); - }); - } - - protected async renameBinary() { - return new Promise((resolve, reject) => { - this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`); - fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => { - if (err) { - reject(err); - } - else { - resolve(); - } - }); - }); - } - - protected async downloadBinary() { - const binaryPath = this.tarPath || this.getBinaryPath(); - - await ensureDir(this.getBinaryDir(), 0o755); - - const file = fs.createWriteStream(binaryPath); - const url = this.getUrl(); - - this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`); - const requestOpts: request.UriOptions & request.CoreOptions = { - uri: url, - gzip: true, - ...this.requestOpts, - }; - const stream = request(requestOpts); - - stream.on("complete", () => { - this.logger.info(`Download of ${this.originalBinaryName} finished`); - file.end(); - }); - - stream.on("error", (error) => { - this.logger.error(error); - fs.unlink(binaryPath, () => { - // do nothing - }); - throw(error); - }); - - return new Promise((resolve, reject) => { - file.on("close", () => { - this.logger.debug(`${this.originalBinaryName} binary download closed`); - if (!this.tarPath) fs.chmod(binaryPath, 0o755, (err) => { - if (err) reject(err); - }); - resolve(); - }); - stream.pipe(file); - }); - } -} diff --git a/src/main/shell-session/local-shell-session/local-shell-session.ts b/src/main/shell-session/local-shell-session/local-shell-session.ts index 301600bd71..314f8700ec 100644 --- a/src/main/shell-session/local-shell-session/local-shell-session.ts +++ b/src/main/shell-session/local-shell-session/local-shell-session.ts @@ -5,12 +5,12 @@ import type WebSocket from "ws"; import path from "path"; -import { helmCli } from "../../helm/helm-cli"; import { UserStore } from "../../../common/user-store"; import type { Cluster } from "../../../common/cluster/cluster"; import type { ClusterId } from "../../../common/cluster-types"; import { ShellSession } from "../shell-session"; import type { Kubectl } from "../../kubectl/kubectl"; +import { baseBinariesDir } from "../../../common/vars"; export class LocalShellSession extends ShellSession { ShellType = "shell"; @@ -18,9 +18,9 @@ export class LocalShellSession extends ShellSession { constructor(protected shellEnvModify: (clusterId: ClusterId, env: Record) => Record, kubectl: Kubectl, websocket: WebSocket, cluster: Cluster, terminalId: string) { super(kubectl, websocket, cluster, terminalId); } - + protected getPathEntries(): string[] { - return [helmCli.getBinaryDir()]; + return [baseBinariesDir.get()]; } protected get cwd(): string | undefined { @@ -40,17 +40,16 @@ export class LocalShellSession extends ShellSession { } protected async getShellArgs(shell: string): Promise { - const helmpath = helmCli.getBinaryDir(); const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath(); const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences); switch(path.basename(shell)) { case "powershell.exe": - return ["-NoExit", "-command", `& {$Env:PATH="${helmpath};${kubectlPathDir};$Env:PATH"}`]; + return ["-NoExit", "-command", `& {$Env:PATH="${baseBinariesDir.get()};${kubectlPathDir};$Env:PATH"}`]; case "bash": return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")]; case "fish": - return ["--login", "--init-command", `export PATH="${helmpath}:${kubectlPathDir}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`]; + return ["--login", "--init-command", `export PATH="${baseBinariesDir.get()}:${kubectlPathDir}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`]; case "zsh": return ["--login"]; default: diff --git a/src/renderer/components/+preferences/kubectl-binaries.tsx b/src/renderer/components/+preferences/kubectl-binaries.tsx index bd956b0e16..efcf1f807b 100644 --- a/src/renderer/components/+preferences/kubectl-binaries.tsx +++ b/src/renderer/components/+preferences/kubectl-binaries.tsx @@ -8,13 +8,12 @@ import { observer } from "mobx-react"; import { Input, InputValidators } from "../input"; import { SubTitle } from "../layout/sub-title"; import { UserStore } from "../../../common/user-store"; -import { bundledKubectlPath } from "../../../main/kubectl/kubectl"; import { SelectOption, Select } from "../select"; import { Switch } from "../switch"; import { packageMirrors } from "../../../common/user-store/preferences-helpers"; -import directoryForBinariesInjectable - from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable"; +import directoryForBinariesInjectable from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; +import { kubectlBinaryPath } from "../../../common/vars"; interface Dependencies { defaultPathForKubectlBinaries: string; @@ -80,7 +79,7 @@ const NonInjectedKubectlBinaries: React.FC = observer(({ defaultPa { - console.info("WEBPACK:main", vars); + console.info("WEBPACK:main", { ...vars }); const { mainDir, buildDir, isDevelopment } = vars; return { diff --git a/webpack.renderer.ts b/webpack.renderer.ts index c7865aa7aa..7b21f9214e 100755 --- a/webpack.renderer.ts +++ b/webpack.renderer.ts @@ -16,7 +16,7 @@ import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin"; export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration { if (showVars) { - console.info("WEBPACK:renderer", vars); + console.info("WEBPACK:renderer", { ...vars }); } const assetsFolderName = "assets"; diff --git a/yarn.lock b/yarn.lock index 3cb00503a9..9c51fff5d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1361,6 +1361,13 @@ dependencies: "@types/node" "*" +"@types/cli-progress@^3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.9.2.tgz#6ca355f96268af39bee9f9307f0ac96145639c26" + integrity sha512-VO5/X5Ij+oVgEVjg5u0IXVe3JQSKJX+Ev8C5x+0hPy0AuWyW+bF8tbajR7cPFnDGhs7pidztcac+ccrDtk5teA== + dependencies: + "@types/node" "*" + "@types/color-convert@*": version "1.9.0" resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d" @@ -1491,6 +1498,13 @@ dependencies: "@types/node" "*" +"@types/gunzip-maybe@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@types/gunzip-maybe/-/gunzip-maybe-1.4.0.tgz#9410fd15ff68eca8907b7b9198e63e2a7c14d511" + integrity sha512-dFP9GrYAR9KhsjTkWJ8q8Gsfql75YIKcg9DuQOj/IrlPzR7W+1zX+cclw1McV82UXAQ+Lpufvgk3e9bC8+HzgA== + dependencies: + "@types/node" "*" + "@types/history@*", "@types/history@^4.7.8": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" @@ -1956,6 +1970,13 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== +"@types/tar-stream@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-2.2.2.tgz#be9d0be9404166e4b114151f93e8442e6ab6fb1d" + integrity sha512-1AX+Yt3icFuU6kxwmPakaiGrJUwG44MpuiqPg4dSolRFk6jmvs4b3IbUol9wKDLIgU76gevn3EwE8y/DkSJCZQ== + dependencies: + "@types/node" "*" + "@types/tar@^4.0.3", "@types/tar@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.5.tgz#5f953f183e36a15c6ce3f336568f6051b7b183f3" @@ -3158,6 +3179,13 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + integrity sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0= + dependencies: + pako "~0.2.0" + browserslist@^4.14.5: version "4.19.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" @@ -3608,6 +3636,13 @@ cli-columns@^3.1.2: string-width "^2.0.0" strip-ansi "^3.0.1" +cli-progress@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.10.0.tgz#63fd9d6343c598c93542fdfa3563a8b59887d78a" + integrity sha512-kLORQrhYCAtUPLZxqsAt2YJGOvRdt34+O6jl5cQGb7iF3dM55FQZlTR+rQyIK9JUcO9bBMwZsTlND+3dmFU2Cw== + dependencies: + string-width "^4.2.0" + cli-table3@^0.5.0, cli-table3@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -4748,7 +4783,7 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexify@^3.4.2, duplexify@^3.6.0: +duplexify@^3.4.2, duplexify@^3.5.0, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== @@ -6515,6 +6550,18 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +gunzip-maybe@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz#b913564ae3be0eda6f3de36464837a9cd94b98ac" + integrity sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw== + dependencies: + browserify-zlib "^0.1.4" + is-deflate "^1.0.0" + is-gzip "^1.0.0" + peek-stream "^1.1.0" + pumpify "^1.3.3" + through2 "^2.0.3" + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -7236,6 +7283,11 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== +is-deflate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" + integrity sha1-yGKQHDwWH7CdrHzcfnhPgOmPLxQ= + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -7315,6 +7367,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-gzip@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" + integrity sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM= + is-in-browser@^1.0.2, is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" @@ -10252,6 +10309,11 @@ pacote@^9.1.0, pacote@^9.5.12, pacote@^9.5.3: unique-filename "^1.1.1" which "^1.3.1" +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= + pako@~1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -10390,6 +10452,15 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +peek-stream@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" + integrity sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA== + dependencies: + buffer-from "^1.0.0" + duplexify "^3.5.0" + through2 "^2.0.3" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -12670,7 +12741,7 @@ tar-fs@^2.0.0, tar-fs@^2.1.1: pump "^3.0.0" tar-stream "^2.1.4" -tar-stream@^2.1.4: +tar-stream@^2.1.4, tar-stream@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== @@ -12808,7 +12879,7 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== -through2@^2.0.0: +through2@^2.0.0, through2@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==