mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
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 <sebastian@malton.name> * Fully remove helmCli, move associated variable to vars Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix helm downloading on windows Signed-off-by: Sebastian Malton <sebastian@malton.name> * Don't download binaries for unsupported windows Signed-off-by: Sebastian Malton <sebastian@malton.name> * Arch specific paths Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix downloading helm on windows Signed-off-by: Sebastian Malton <sebastian@malton.name> * rename once-cell file to lazy-initialized Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
dd5dfb393d
commit
a19f094a0d
2
Makefile
2
Makefile
@ -21,7 +21,7 @@ node_modules: yarn.lock
|
|||||||
yarn check --verify-tree --integrity
|
yarn check --verify-tree --integrity
|
||||||
|
|
||||||
binaries/client: node_modules
|
binaries/client: node_modules
|
||||||
yarn download-bins
|
yarn download:binaries
|
||||||
|
|
||||||
.PHONY: compile-dev
|
.PHONY: compile-dev
|
||||||
compile-dev: node_modules
|
compile-dev: node_modules
|
||||||
|
|||||||
235
build/download_binaries.ts
Normal file
235
build/download_binaries.ts
Normal file
@ -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<void> {
|
||||||
|
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<BinaryDownloaderArgs, "binaryName">, 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<BinaryDownloaderArgs, "binaryName">, 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<BinaryDownloaderArgs, "binaryName">, 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));
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
@ -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<void>((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")));
|
|
||||||
});
|
|
||||||
@ -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<void>((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")));
|
|
||||||
});
|
|
||||||
39
package.json
39
package.json
@ -30,10 +30,7 @@
|
|||||||
"integration": "jest --runInBand --detectOpenHandles --forceExit integration",
|
"integration": "jest --runInBand --detectOpenHandles --forceExit integration",
|
||||||
"dist": "yarn run compile && electron-builder --publish onTag",
|
"dist": "yarn run compile && electron-builder --publish onTag",
|
||||||
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
|
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
|
||||||
"download-bins": "concurrently yarn:download:*",
|
"download:binaries": "yarn run ts-node build/download_binaries.ts",
|
||||||
"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",
|
|
||||||
"build:tray-icons": "yarn run ts-node build/build_tray_icon.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",
|
"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 .",
|
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
|
||||||
@ -135,8 +132,8 @@
|
|||||||
"to": "./${arch}/lens-k8s-proxy"
|
"to": "./${arch}/lens-k8s-proxy"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "binaries/client/${arch}/helm3/helm3",
|
"from": "binaries/client/linux/${arch}/helm",
|
||||||
"to": "./helm3/helm3"
|
"to": "./${arch}/helm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -160,8 +157,8 @@
|
|||||||
"to": "./${arch}/lens-k8s-proxy"
|
"to": "./${arch}/lens-k8s-proxy"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "binaries/client/${arch}/helm3/helm3",
|
"from": "binaries/client/darwin/${arch}/helm",
|
||||||
"to": "./helm3/helm3"
|
"to": "./${arch}/helm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -171,24 +168,16 @@
|
|||||||
],
|
],
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "binaries/client/windows/x64/kubectl.exe",
|
"from": "binaries/client/windows/${arch}/kubectl.exe",
|
||||||
"to": "./x64/kubectl.exe"
|
"to": "./${arch}/kubectl.exe"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "binaries/client/windows/ia32/kubectl.exe",
|
"from": "binaries/client/windows/${arch}/lens-k8s-proxy.exe",
|
||||||
"to": "./ia32/kubectl.exe"
|
"to": "./${arch}/lens-k8s-proxy.exe"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "binaries/client/windows/x64/lens-k8s-proxy",
|
"from": "binaries/client/windows/${arch}/helm.exe",
|
||||||
"to": "./x64/lens-k8s-proxy.exe"
|
"to": "./${arch}/helm.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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -292,12 +281,14 @@
|
|||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/byline": "^4.2.33",
|
"@types/byline": "^4.2.33",
|
||||||
"@types/chart.js": "^2.9.34",
|
"@types/chart.js": "^2.9.34",
|
||||||
|
"@types/cli-progress": "^3.9.2",
|
||||||
"@types/color": "^3.0.2",
|
"@types/color": "^3.0.2",
|
||||||
"@types/crypto-js": "^3.1.47",
|
"@types/crypto-js": "^3.1.47",
|
||||||
"@types/dompurify": "^2.3.1",
|
"@types/dompurify": "^2.3.1",
|
||||||
"@types/electron-devtools-installer": "^2.2.0",
|
"@types/electron-devtools-installer": "^2.2.0",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/glob-to-regexp": "^0.4.1",
|
"@types/glob-to-regexp": "^0.4.1",
|
||||||
|
"@types/gunzip-maybe": "^1.4.0",
|
||||||
"@types/hoist-non-react-statics": "^3.3.1",
|
"@types/hoist-non-react-statics": "^3.3.1",
|
||||||
"@types/html-webpack-plugin": "^3.2.6",
|
"@types/html-webpack-plugin": "^3.2.6",
|
||||||
"@types/http-proxy": "^1.17.7",
|
"@types/http-proxy": "^1.17.7",
|
||||||
@ -330,6 +321,7 @@
|
|||||||
"@types/sharp": "^0.29.4",
|
"@types/sharp": "^0.29.4",
|
||||||
"@types/spdy": "^3.4.5",
|
"@types/spdy": "^3.4.5",
|
||||||
"@types/tar": "^4.0.5",
|
"@types/tar": "^4.0.5",
|
||||||
|
"@types/tar-stream": "^2.2.2",
|
||||||
"@types/tcp-port-used": "^1.0.0",
|
"@types/tcp-port-used": "^1.0.0",
|
||||||
"@types/tempy": "^0.3.0",
|
"@types/tempy": "^0.3.0",
|
||||||
"@types/triple-beam": "^1.3.2",
|
"@types/triple-beam": "^1.3.2",
|
||||||
@ -344,6 +336,7 @@
|
|||||||
"ansi_up": "^5.1.0",
|
"ansi_up": "^5.1.0",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
"circular-dependency-plugin": "^5.2.2",
|
"circular-dependency-plugin": "^5.2.2",
|
||||||
|
"cli-progress": "^3.10.0",
|
||||||
"color": "^3.2.1",
|
"color": "^3.2.1",
|
||||||
"concurrently": "^7.0.0",
|
"concurrently": "^7.0.0",
|
||||||
"css-loader": "^6.5.1",
|
"css-loader": "^6.5.1",
|
||||||
@ -362,6 +355,7 @@
|
|||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
"flex.box": "^3.4.4",
|
"flex.box": "^3.4.4",
|
||||||
"fork-ts-checker-webpack-plugin": "^6.5.0",
|
"fork-ts-checker-webpack-plugin": "^6.5.0",
|
||||||
|
"gunzip-maybe": "^1.4.2",
|
||||||
"hoist-non-react-statics": "^3.3.2",
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"ignore-loader": "^0.1.2",
|
"ignore-loader": "^0.1.2",
|
||||||
@ -392,6 +386,7 @@
|
|||||||
"sharp": "^0.29.3",
|
"sharp": "^0.29.3",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"tailwindcss": "^3.0.7",
|
"tailwindcss": "^3.0.7",
|
||||||
|
"tar-stream": "^2.2.0",
|
||||||
"ts-jest": "26.5.6",
|
"ts-jest": "26.5.6",
|
||||||
"ts-loader": "^9.2.6",
|
"ts-loader": "^9.2.6",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
|
|||||||
@ -3,19 +3,11 @@
|
|||||||
* 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, lifecycleEnum } from "@ogre-tools/injectable";
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
import path from "path";
|
import { baseBinariesDir } from "../../vars";
|
||||||
import isDevelopmentInjectable from "../../vars/is-development.injectable";
|
|
||||||
import contextDirInjectable from "../../vars/context-dir.injectable";
|
|
||||||
|
|
||||||
const directoryForBundledBinariesInjectable = getInjectable({
|
const directoryForBundledBinariesInjectable = getInjectable({
|
||||||
id: "directory-for-bundled-binaries",
|
id: "directory-for-bundled-binaries",
|
||||||
instantiate: (di) => {
|
instantiate: () => baseBinariesDir.get(),
|
||||||
if (di.inject(isDevelopmentInjectable)) {
|
|
||||||
return path.join(di.inject(contextDirInjectable), "binaries");
|
|
||||||
}
|
|
||||||
|
|
||||||
return process.resourcesPath;
|
|
||||||
},
|
|
||||||
lifecycle: lifecycleEnum.singleton,
|
lifecycle: lifecycleEnum.singleton,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
35
src/common/utils/lazy-initialized.ts
Normal file
35
src/common/utils/lazy-initialized.ts
Normal file
@ -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<T> {
|
||||||
|
get(): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to make a `OnceCell<T>`
|
||||||
|
*/
|
||||||
|
export function lazyInitialized<T>(builder: () => T): LazyInitialized<T> {
|
||||||
|
let value: T | undefined;
|
||||||
|
let called = false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
get() {
|
||||||
|
if (called) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = builder();
|
||||||
|
called = true;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ import path from "path";
|
|||||||
import { SemVer } from "semver";
|
import { SemVer } from "semver";
|
||||||
import packageInfo from "../../package.json";
|
import packageInfo from "../../package.json";
|
||||||
import { defineGlobal } from "./utils/defineGlobal";
|
import { defineGlobal } from "./utils/defineGlobal";
|
||||||
|
import { lazyInitialized } from "./utils/lazy-initialized";
|
||||||
|
|
||||||
export const isMac = process.platform === "darwin";
|
export const isMac = process.platform === "darwin";
|
||||||
export const isWindows = process.platform === "win32";
|
export const isWindows = process.platform === "win32";
|
||||||
@ -29,6 +30,54 @@ export const defaultTheme = "lens-dark" as string;
|
|||||||
export const defaultFontSize = 12;
|
export const defaultFontSize = 12;
|
||||||
export const defaultTerminalFontFamily = "RobotoMono";
|
export const defaultTerminalFontFamily = "RobotoMono";
|
||||||
export const defaultEditorFontFamily = "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
|
// Webpack build paths
|
||||||
export const contextDir = process.cwd();
|
export const contextDir = process.cwd();
|
||||||
|
|||||||
23
src/main/helm/exec.ts
Normal file
23
src/main/helm/exec.ts
Normal file
@ -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<string> {
|
||||||
|
try {
|
||||||
|
const { stdout } = await promiseExecFile(helmBinaryPath.get(), args, options);
|
||||||
|
|
||||||
|
return stdout;
|
||||||
|
} catch (error) {
|
||||||
|
throw error?.stderr || error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,10 +8,9 @@ import v8 from "v8";
|
|||||||
import * as yaml from "js-yaml";
|
import * as yaml from "js-yaml";
|
||||||
import type { HelmRepo } from "./helm-repo-manager";
|
import type { HelmRepo } from "./helm-repo-manager";
|
||||||
import logger from "../logger";
|
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 type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api";
|
||||||
import { iter, sortCharts } from "../../common/utils";
|
import { iter, sortCharts } from "../../common/utils";
|
||||||
|
import { execHelm } from "./exec";
|
||||||
|
|
||||||
interface ChartCacheEntry {
|
interface ChartCacheEntry {
|
||||||
data: Buffer;
|
data: Buffer;
|
||||||
@ -49,21 +48,13 @@ export class HelmChartManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async executeCommand(args: string[], name: string, version?: string) {
|
private async executeCommand(args: string[], name: string, version?: string) {
|
||||||
const helm = await helmCli.binaryPath();
|
|
||||||
|
|
||||||
args.push(`${this.repo.name}/${name}`);
|
args.push(`${this.repo.name}/${name}`);
|
||||||
|
|
||||||
if (version) {
|
if (version) {
|
||||||
args.push("--version", version);
|
args.push("--version", version);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return execHelm(args);
|
||||||
const { stdout } = await promiseExecFile(helm, args);
|
|
||||||
|
|
||||||
return stdout;
|
|
||||||
} catch (error) {
|
|
||||||
throw error.stderr || error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getReadme(name: string, version?: string) {
|
public async getReadme(name: string, version?: string) {
|
||||||
|
|||||||
@ -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);
|
|
||||||
|
|
||||||
@ -6,23 +6,9 @@
|
|||||||
import tempy from "tempy";
|
import tempy from "tempy";
|
||||||
import fse from "fs-extra";
|
import fse from "fs-extra";
|
||||||
import * as yaml from "js-yaml";
|
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 { toCamelCase } from "../../common/utils/camelCase";
|
||||||
import type { BaseEncodingOptions } from "fs";
|
import { execFile } from "child_process";
|
||||||
import { execFile, ExecFileOptions } from "child_process";
|
import { execHelm } from "./exec";
|
||||||
|
|
||||||
async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
|
|
||||||
const helmCliPath = await helmCli.binaryPath();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { stdout } = await promiseExecFile(helmCliPath, args, options);
|
|
||||||
|
|
||||||
return stdout;
|
|
||||||
} catch (error) {
|
|
||||||
throw error?.stderr || error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise<Record<string, any>[]> {
|
export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise<Record<string, any>[]> {
|
||||||
const args = [
|
const args = [
|
||||||
|
|||||||
@ -4,14 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
import { BaseEncodingOptions, readFile } from "fs-extra";
|
import { readFile } from "fs-extra";
|
||||||
import { promiseExecFile } from "../../common/utils/promise-exec";
|
|
||||||
import { helmCli } from "./helm-cli";
|
|
||||||
import { Singleton } from "../../common/utils/singleton";
|
import { Singleton } from "../../common/utils/singleton";
|
||||||
import { customRequestPromise } from "../../common/request";
|
import { customRequestPromise } from "../../common/request";
|
||||||
import orderBy from "lodash/orderBy";
|
import orderBy from "lodash/orderBy";
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import type { ExecFileOptions } from "child_process";
|
import { execHelm } from "./exec";
|
||||||
|
|
||||||
export type HelmEnv = Record<string, string> & {
|
export type HelmEnv = Record<string, string> & {
|
||||||
HELM_REPOSITORY_CACHE?: string;
|
HELM_REPOSITORY_CACHE?: string;
|
||||||
@ -34,18 +32,6 @@ export interface HelmRepo {
|
|||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
|
|
||||||
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 {
|
export class HelmRepoManager extends Singleton {
|
||||||
protected repos: HelmRepo[];
|
protected repos: HelmRepo[];
|
||||||
protected helmEnv: HelmEnv;
|
protected helmEnv: HelmEnv;
|
||||||
@ -63,9 +49,6 @@ export class HelmRepoManager extends Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async ensureInitialized() {
|
private async ensureInitialized() {
|
||||||
helmCli.setLogger(logger);
|
|
||||||
await helmCli.ensureBinary();
|
|
||||||
|
|
||||||
this.helmEnv ??= await this.parseHelmEnv();
|
this.helmEnv ??= await this.parseHelmEnv();
|
||||||
|
|
||||||
const repos = await this.list();
|
const repos = await this.list();
|
||||||
|
|||||||
@ -3,24 +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 { KubeAuthProxy } from "./kube-auth-proxy";
|
import { KubeAuthProxy, KubeAuthProxyDependencies } from "./kube-auth-proxy";
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import path from "path";
|
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";
|
import directoryForBundledBinariesInjectable from "../../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
|
||||||
|
|
||||||
const createKubeAuthProxyInjectable = getInjectable({
|
const createKubeAuthProxyInjectable = getInjectable({
|
||||||
id: "create-kube-auth-proxy",
|
id: "create-kube-auth-proxy",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const binaryName = isWindows ? "lens-k8s-proxy.exe" : "lens-k8s-proxy";
|
const binaryName = getBinaryName("lens-k8s-proxy");
|
||||||
const proxyPath = isDevelopment ? path.join("client", process.platform, process.arch) : process.arch;
|
const dependencies: KubeAuthProxyDependencies = {
|
||||||
const dependencies = {
|
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), binaryName),
|
||||||
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), proxyPath, binaryName),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) =>
|
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => (
|
||||||
new KubeAuthProxy(dependencies, cluster, environmentVariables);
|
new KubeAuthProxy(dependencies, cluster, environmentVariables)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { makeObservable, observable, when } from "mobx";
|
|||||||
|
|
||||||
const startingServeRegex = /starting to serve on (?<address>.+)/i;
|
const startingServeRegex = /starting to serve on (?<address>.+)/i;
|
||||||
|
|
||||||
interface Dependencies {
|
export interface KubeAuthProxyDependencies {
|
||||||
proxyBinPath: string;
|
proxyBinPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ export class KubeAuthProxy {
|
|||||||
protected proxyProcess?: ChildProcess;
|
protected proxyProcess?: ChildProcess;
|
||||||
@observable protected ready = false;
|
@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);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,9 +9,8 @@ import { promiseExecFile } from "../../common/utils/promise-exec";
|
|||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import { ensureDir, pathExists } from "fs-extra";
|
import { ensureDir, pathExists } from "fs-extra";
|
||||||
import * as lockFile from "proper-lockfile";
|
import * as lockFile from "proper-lockfile";
|
||||||
import { helmCli } from "../helm/helm-cli";
|
|
||||||
import { getBundledKubectlVersion } from "../../common/utils/app-version";
|
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 { SemVer } from "semver";
|
||||||
import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers";
|
import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers";
|
||||||
import got from "got/dist/source";
|
import got from "got/dist/source";
|
||||||
@ -39,27 +38,8 @@ const kubectlMap: Map<string, string> = new Map([
|
|||||||
["1.22", "1.22.6"],
|
["1.22", "1.22.6"],
|
||||||
["1.23", bundledVersion],
|
["1.23", bundledVersion],
|
||||||
]);
|
]);
|
||||||
let bundledPath: string;
|
|
||||||
const initScriptVersionString = "# lens-initscript v3";
|
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 {
|
interface Dependencies {
|
||||||
directoryForKubectlBinaries: string;
|
directoryForKubectlBinaries: string;
|
||||||
|
|
||||||
@ -102,27 +82,13 @@ export class Kubectl {
|
|||||||
logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`);
|
logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let arch = null;
|
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${normalizedPlatform}/${normalizedArch}/${kubectlBinaryName}`;
|
||||||
|
|
||||||
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.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion));
|
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() {
|
public getBundledPath() {
|
||||||
return bundledKubectlPath();
|
return kubectlBinaryPath.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPathFromPreferences() {
|
public getPathFromPreferences() {
|
||||||
@ -316,7 +282,7 @@ export class Kubectl {
|
|||||||
? this.dirname
|
? this.dirname
|
||||||
: path.dirname(this.getPathFromPreferences());
|
: path.dirname(this.getPathFromPreferences());
|
||||||
|
|
||||||
const helmPath = helmCli.getBinaryDir();
|
const binariesDir = baseBinariesDir.get();
|
||||||
|
|
||||||
const bashScriptPath = path.join(this.dirname, ".bash_set_path");
|
const bashScriptPath = path.join(this.dirname, ".bash_set_path");
|
||||||
const bashScript = [
|
const bashScript = [
|
||||||
@ -330,7 +296,7 @@ export class Kubectl {
|
|||||||
"elif test -f \"$HOME/.profile\"; then",
|
"elif test -f \"$HOME/.profile\"; then",
|
||||||
" . \"$HOME/.profile\"",
|
" . \"$HOME/.profile\"",
|
||||||
"fi",
|
"fi",
|
||||||
`export PATH="${helmPath}:${kubectlPath}:$PATH"`,
|
`export PATH="${binariesDir}:${kubectlPath}:$PATH"`,
|
||||||
'export KUBECONFIG="$tempkubeconfig"',
|
'export KUBECONFIG="$tempkubeconfig"',
|
||||||
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
|
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
|
||||||
`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
|
// voodoo to replace any previous occurrences of kubectl path in the PATH
|
||||||
`kubectlpath="${kubectlPath}"`,
|
`kubectlpath="${kubectlPath}"`,
|
||||||
`helmpath="${helmPath}"`,
|
`binariesDir="${binariesDir}"`,
|
||||||
"p=\":$kubectlpath:\"",
|
"p=\":$kubectlpath:\"",
|
||||||
"d=\":$PATH:\"",
|
"d=\":$PATH:\"",
|
||||||
`d=\${d//$p/:}`,
|
`d=\${d//$p/:}`,
|
||||||
`d=\${d/#:/}`,
|
`d=\${d/#:/}`,
|
||||||
`export PATH="$helmpath:$kubectlpath:\${d/%:/}"`,
|
`export PATH="$binariesDir:$kubectlpath:\${d/%:/}"`,
|
||||||
"export KUBECONFIG=\"$tempkubeconfig\"",
|
"export KUBECONFIG=\"$tempkubeconfig\"",
|
||||||
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
|
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
|
||||||
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`,
|
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`,
|
||||||
|
|||||||
@ -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<void>(resolve => {
|
|
||||||
this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
|
|
||||||
tar.x({
|
|
||||||
file: this.tarPath,
|
|
||||||
cwd: this.dirname,
|
|
||||||
}).then((() => {
|
|
||||||
resolve();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async renameBinary() {
|
|
||||||
return new Promise<void>((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<void>((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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
import type WebSocket from "ws";
|
import type WebSocket from "ws";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { helmCli } from "../../helm/helm-cli";
|
|
||||||
import { UserStore } from "../../../common/user-store";
|
import { UserStore } from "../../../common/user-store";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import type { ClusterId } from "../../../common/cluster-types";
|
import type { ClusterId } from "../../../common/cluster-types";
|
||||||
import { ShellSession } from "../shell-session";
|
import { ShellSession } from "../shell-session";
|
||||||
import type { Kubectl } from "../../kubectl/kubectl";
|
import type { Kubectl } from "../../kubectl/kubectl";
|
||||||
|
import { baseBinariesDir } from "../../../common/vars";
|
||||||
|
|
||||||
export class LocalShellSession extends ShellSession {
|
export class LocalShellSession extends ShellSession {
|
||||||
ShellType = "shell";
|
ShellType = "shell";
|
||||||
@ -20,7 +20,7 @@ export class LocalShellSession extends ShellSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected getPathEntries(): string[] {
|
protected getPathEntries(): string[] {
|
||||||
return [helmCli.getBinaryDir()];
|
return [baseBinariesDir.get()];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get cwd(): string | undefined {
|
protected get cwd(): string | undefined {
|
||||||
@ -40,17 +40,16 @@ export class LocalShellSession extends ShellSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getShellArgs(shell: string): Promise<string[]> {
|
protected async getShellArgs(shell: string): Promise<string[]> {
|
||||||
const helmpath = helmCli.getBinaryDir();
|
|
||||||
const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath();
|
const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath();
|
||||||
const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
|
const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
|
||||||
|
|
||||||
switch(path.basename(shell)) {
|
switch(path.basename(shell)) {
|
||||||
case "powershell.exe":
|
case "powershell.exe":
|
||||||
return ["-NoExit", "-command", `& {$Env:PATH="${helmpath};${kubectlPathDir};$Env:PATH"}`];
|
return ["-NoExit", "-command", `& {$Env:PATH="${baseBinariesDir.get()};${kubectlPathDir};$Env:PATH"}`];
|
||||||
case "bash":
|
case "bash":
|
||||||
return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")];
|
return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")];
|
||||||
case "fish":
|
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":
|
case "zsh":
|
||||||
return ["--login"];
|
return ["--login"];
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -8,13 +8,12 @@ import { observer } from "mobx-react";
|
|||||||
import { Input, InputValidators } from "../input";
|
import { Input, InputValidators } from "../input";
|
||||||
import { SubTitle } from "../layout/sub-title";
|
import { SubTitle } from "../layout/sub-title";
|
||||||
import { UserStore } from "../../../common/user-store";
|
import { UserStore } from "../../../common/user-store";
|
||||||
import { bundledKubectlPath } from "../../../main/kubectl/kubectl";
|
|
||||||
import { SelectOption, Select } from "../select";
|
import { SelectOption, Select } from "../select";
|
||||||
import { Switch } from "../switch";
|
import { Switch } from "../switch";
|
||||||
import { packageMirrors } from "../../../common/user-store/preferences-helpers";
|
import { packageMirrors } from "../../../common/user-store/preferences-helpers";
|
||||||
import directoryForBinariesInjectable
|
import directoryForBinariesInjectable from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable";
|
||||||
from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable";
|
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import { kubectlBinaryPath } from "../../../common/vars";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
defaultPathForKubectlBinaries: string;
|
defaultPathForKubectlBinaries: string;
|
||||||
@ -80,7 +79,7 @@ const NonInjectedKubectlBinaries: React.FC<Dependencies> = observer(({ defaultPa
|
|||||||
<SubTitle title="Path to kubectl binary" />
|
<SubTitle title="Path to kubectl binary" />
|
||||||
<Input
|
<Input
|
||||||
theme="round-black"
|
theme="round-black"
|
||||||
placeholder={bundledKubectlPath()}
|
placeholder={kubectlBinaryPath.get()}
|
||||||
value={binariesPath}
|
value={binariesPath}
|
||||||
validators={pathValidator}
|
validators={pathValidator}
|
||||||
onChange={setBinariesPath}
|
onChange={setBinariesPath}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { iconsAndImagesWebpackRules } from "./webpack.renderer";
|
|||||||
const configs: { (): webpack.Configuration }[] = [];
|
const configs: { (): webpack.Configuration }[] = [];
|
||||||
|
|
||||||
configs.push((): webpack.Configuration => {
|
configs.push((): webpack.Configuration => {
|
||||||
console.info("WEBPACK:main", vars);
|
console.info("WEBPACK:main", { ...vars });
|
||||||
const { mainDir, buildDir, isDevelopment } = vars;
|
const { mainDir, buildDir, isDevelopment } = vars;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin";
|
|||||||
|
|
||||||
export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration {
|
export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration {
|
||||||
if (showVars) {
|
if (showVars) {
|
||||||
console.info("WEBPACK:renderer", vars);
|
console.info("WEBPACK:renderer", { ...vars });
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetsFolderName = "assets";
|
const assetsFolderName = "assets";
|
||||||
|
|||||||
77
yarn.lock
77
yarn.lock
@ -1361,6 +1361,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@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@*":
|
"@types/color-convert@*":
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d"
|
resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d"
|
||||||
@ -1491,6 +1498,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@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":
|
"@types/history@*", "@types/history@^4.7.8":
|
||||||
version "4.7.8"
|
version "4.7.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
|
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"
|
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
|
||||||
integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
|
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":
|
"@types/tar@^4.0.3", "@types/tar@^4.0.5":
|
||||||
version "4.0.5"
|
version "4.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.5.tgz#5f953f183e36a15c6ce3f336568f6051b7b183f3"
|
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"
|
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
|
||||||
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
|
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:
|
browserslist@^4.14.5:
|
||||||
version "4.19.1"
|
version "4.19.1"
|
||||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3"
|
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"
|
string-width "^2.0.0"
|
||||||
strip-ansi "^3.0.1"
|
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:
|
cli-table3@^0.5.0, cli-table3@^0.5.1:
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202"
|
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"
|
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||||
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
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"
|
version "3.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
|
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
|
||||||
integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
|
integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
|
||||||
@ -6515,6 +6550,18 @@ growly@^1.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||||
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
|
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:
|
handle-thing@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
|
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"
|
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
||||||
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
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:
|
is-descriptor@^0.1.0:
|
||||||
version "0.1.6"
|
version "0.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
|
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:
|
dependencies:
|
||||||
is-extglob "^2.1.1"
|
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:
|
is-in-browser@^1.0.2, is-in-browser@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
|
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"
|
unique-filename "^1.1.1"
|
||||||
which "^1.3.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:
|
pako@~1.0.2:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
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"
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
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:
|
pend@~1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
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"
|
pump "^3.0.0"
|
||||||
tar-stream "^2.1.4"
|
tar-stream "^2.1.4"
|
||||||
|
|
||||||
tar-stream@^2.1.4:
|
tar-stream@^2.1.4, tar-stream@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||||
@ -12808,7 +12879,7 @@ throat@^5.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"
|
resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"
|
||||||
integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==
|
integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==
|
||||||
|
|
||||||
through2@^2.0.0:
|
through2@^2.0.0, through2@^2.0.3:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
||||||
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user