refactor/fix integration tests
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
1
.github/workflows/bump-master-version.yaml
vendored
@ -20,7 +20,6 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn lerna bootstrap
|
||||
- name: Bump version to first alpha of next minor version
|
||||
id: bump
|
||||
run: |
|
||||
|
||||
8
.github/workflows/main.yml
vendored
@ -35,11 +35,11 @@ jobs:
|
||||
- name: Generate Extensions API Reference using typedocs
|
||||
run: |
|
||||
yarn install
|
||||
yarn typedocs-extensions-api
|
||||
yarn run build:docs
|
||||
|
||||
- name: Verify that the markdown is valid
|
||||
run: |
|
||||
yarn run verify-docs
|
||||
yarn run mkdocs:verify
|
||||
|
||||
build:
|
||||
name: Deploy docs
|
||||
@ -77,8 +77,8 @@ jobs:
|
||||
|
||||
- name: Generate Extensions API Reference using typedocs
|
||||
run: |
|
||||
yarn install
|
||||
yarn typedocs-extensions-api
|
||||
yarn install --frozen-lockfile
|
||||
yarn build:docs
|
||||
|
||||
- name: mkdocs deploy master
|
||||
if: contains(github.ref, 'refs/heads/master')
|
||||
|
||||
1
.github/workflows/publish-master-npm.yml
vendored
@ -28,7 +28,6 @@ jobs:
|
||||
- name: Generate NPM packages
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn lerna bootstrap
|
||||
yarn run build
|
||||
|
||||
- name: Publish NPM package
|
||||
|
||||
1
.github/workflows/publish-release-npm.yml
vendored
@ -33,7 +33,6 @@ jobs:
|
||||
- name: Generate NPM packages
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn lerna bootstrap
|
||||
yarn run build
|
||||
|
||||
- name: Publish NPM packages
|
||||
|
||||
6
.github/workflows/test.yml
vendored
@ -51,7 +51,7 @@ jobs:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_on: error
|
||||
command: yarn install --frozen-lockfile && yarn lerna bootstrap
|
||||
command: yarn install --frozen-lockfile
|
||||
|
||||
- run: make test
|
||||
name: Run tests
|
||||
@ -72,14 +72,14 @@ jobs:
|
||||
name: Run Linux integration tests
|
||||
if: ${{ runner.os == 'Linux' && matrix.type == 'smoke' }}
|
||||
|
||||
- run: make integration
|
||||
- run: yarn run test:integration
|
||||
name: Run macOS integration tests
|
||||
shell: bash
|
||||
env:
|
||||
ELECTRON_BUILDER_EXTRA_ARGS: "--x64 --arm64"
|
||||
if: ${{ runner.os == 'macOS' && matrix.type == 'smoke' }}
|
||||
|
||||
- run: make integration
|
||||
- run: yarn run test:integration
|
||||
name: Run Windows integration tests
|
||||
shell: bash
|
||||
env:
|
||||
|
||||
2
.yarnrc
Normal file
@ -0,0 +1,2 @@
|
||||
--install.check-files true
|
||||
--install.network-timeout 100000
|
||||
20
Makefile
@ -5,15 +5,9 @@ CMD_ARGS = $(filter-out $@,$(MAKECMDGOALS))
|
||||
|
||||
ELECTRON_BUILDER_EXTRA_ARGS ?=
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
DETECTED_OS := Windows
|
||||
else
|
||||
DETECTED_OS := $(shell uname)
|
||||
endif
|
||||
|
||||
node_modules: yarn.lock
|
||||
yarn install --check-files --frozen-lockfile --network-timeout=100000
|
||||
yarn lerna bootstrap
|
||||
.PHONY: bootstrap
|
||||
bootstrap:
|
||||
yarn install
|
||||
|
||||
.PHONY: lint
|
||||
lint: node_modules
|
||||
@ -29,13 +23,7 @@ integration: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
yarn run build
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
# https://github.com/ukoloff/win-ca#clear-pem-folder-on-publish
|
||||
rm -rf packages/core/node_modules/win-ca/pem
|
||||
endif
|
||||
yarn lerna run build:app --publish onTag $(ELECTRON_BUILDER_EXTRA_ARGS)
|
||||
|
||||
yarn lerna run build:app
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
||||
32
nx.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"tasksRunnerOptions": {
|
||||
"default": {
|
||||
"runner": "nx/tasks-runners/default",
|
||||
"options": {
|
||||
"cacheableOperations": [
|
||||
"build"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"targetDefaults": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"{workspaceRoot}/dist"
|
||||
]
|
||||
},
|
||||
"build:app": {
|
||||
"dependsOn": [
|
||||
"build"
|
||||
]
|
||||
},
|
||||
"test:integration": {
|
||||
"dependsOn": [
|
||||
"build:app"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@
|
||||
"adr:update-readme": "adr update",
|
||||
"adr:list": "adr list",
|
||||
"build": "lerna run --stream build",
|
||||
"build:app": "lerna run --stream build:app",
|
||||
"build:docs": "lerna run --stream build:docs",
|
||||
"clean": "lerna run clean --stream",
|
||||
"clean:node_modules": "lerna clean -y && rm -rf node_modules",
|
||||
@ -17,7 +18,8 @@
|
||||
"test:integration": "lerna run --stream test:integration",
|
||||
"bump-version": "lerna version --no-git-tag-version --no-push",
|
||||
"precreate-release-pr": "lerna run build --no-progress --scope @k8slens/release-tool",
|
||||
"create-release-pr": "node ./packages/release-tool/dist/index.mjs"
|
||||
"create-release-pr": "node ./packages/release-tool/dist/index.mjs",
|
||||
"postinstall": "lerna bootstrap"
|
||||
},
|
||||
"devDependencies": {
|
||||
"adr": "^1.4.3",
|
||||
|
||||
@ -49,18 +49,9 @@
|
||||
"build": "env NODE_ENV=production yarn run webpack --config webpack/library-bundle.ts",
|
||||
"clean": "rm -rf dist webpack/build/ static/build",
|
||||
"compile:node-fetch": "yarn run webpack --config webpack/node-fetch.ts",
|
||||
"dev": "env NODE_ENV=development yarn run webpack --config webpack/library-bundle.ts --watch",
|
||||
"prepare": "yarn run compile:node-fetch",
|
||||
"build:linux": "yarn run compile && electron-builder --linux --dir",
|
||||
"build:mac": "yarn run compile && electron-builder --mac --dir",
|
||||
"build:win": "yarn run compile && electron-builder --win --dir",
|
||||
"integration": "jest --runInBand --detectOpenHandles --forceExit integration",
|
||||
"test:unit": "func() { jest ${1} --watch --testPathIgnorePatterns integration; }; func",
|
||||
"test:integration": "func() { jest ${1:-xyz} --watch --runInBand --detectOpenHandles --forceExit --modulePaths=[\"<rootDir>/integration/\"]; }; func",
|
||||
"dist": "yarn run compile && electron-builder --publish onTag",
|
||||
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
|
||||
"download:binaries": "yarn run ts-node build/download_binaries.ts",
|
||||
"build:tray-icons": "yarn run ts-node build/generate-tray-icons.ts",
|
||||
"build:theme-vars": "yarn run ts-node build/build_theme_vars.ts",
|
||||
"test:unit": "func() { jest ${1} --testPathIgnorePatterns integration; }; func",
|
||||
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
|
||||
"lint:fix": "yarn run lint --fix"
|
||||
},
|
||||
|
||||
280
packages/open-lens/build/download_binaries.ts
Normal file
@ -0,0 +1,280 @@
|
||||
/**
|
||||
* 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 { FileHandle } from "fs/promises";
|
||||
import { open } from "fs/promises";
|
||||
import type { WriteStream } from "fs-extra";
|
||||
import { constants, ensureDir, unlink } from "fs-extra";
|
||||
import path from "path";
|
||||
import type * as FetchModule from "node-fetch";
|
||||
import { promisify } from "util";
|
||||
import { pipeline as _pipeline, Transform, Writable } from "stream";
|
||||
import type { SingleBar } from "cli-progress";
|
||||
import { MultiBar } from "cli-progress";
|
||||
import { extract } from "tar-stream";
|
||||
import gunzip from "gunzip-maybe";
|
||||
import { isErrnoException, setTimeoutFor } from "../../core/src/common/utils";
|
||||
import AbortController from "abort-controller";
|
||||
|
||||
type Response = FetchModule.Response;
|
||||
type RequestInfo = FetchModule.RequestInfo;
|
||||
type RequestInit = FetchModule.RequestInit;
|
||||
|
||||
const pipeline = promisify(_pipeline);
|
||||
|
||||
const getBinaryName = (binaryName: string, { forPlatform }: { forPlatform : string }) => {
|
||||
if (forPlatform === "windows") {
|
||||
return `${binaryName}.exe`;
|
||||
}
|
||||
|
||||
return binaryName;
|
||||
};
|
||||
|
||||
interface BinaryDownloaderArgs {
|
||||
readonly version: string;
|
||||
readonly platform: SupportedPlatform;
|
||||
readonly downloadArch: string;
|
||||
readonly fileArch: string;
|
||||
readonly binaryName: string;
|
||||
readonly baseDir: string;
|
||||
}
|
||||
|
||||
interface BinaryDownloaderDependencies {
|
||||
fetch: (url: RequestInfo, init?: RequestInit) => Promise<Response>;
|
||||
}
|
||||
|
||||
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(protected readonly dependencies: BinaryDownloaderDependencies, 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> {
|
||||
if (process.env.LENS_SKIP_DOWNLOAD_BINARIES === "true") {
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
|
||||
setTimeoutFor(controller, 15 * 60 * 1000);
|
||||
|
||||
const stream = await this.dependencies.fetch(this.url, {
|
||||
signal: controller.signal,
|
||||
});
|
||||
const total = Number(stream.headers.get("content-length"));
|
||||
const bar = this.bar;
|
||||
let fileHandle: FileHandle | undefined = undefined;
|
||||
|
||||
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
|
||||
*/
|
||||
const handle = fileHandle = await open(this.target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL);
|
||||
|
||||
if (!stream.body) {
|
||||
throw new Error("no body on stream");
|
||||
}
|
||||
|
||||
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) {
|
||||
handle.write(chunk)
|
||||
.then(() => cb())
|
||||
.catch(cb);
|
||||
},
|
||||
})),
|
||||
);
|
||||
await fileHandle.chmod(0o755);
|
||||
await fileHandle.close();
|
||||
} catch (error) {
|
||||
await fileHandle?.close();
|
||||
|
||||
if (isErrnoException(error) && 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(deps: BinaryDownloaderDependencies, args: Omit<BinaryDownloaderArgs, "binaryName">, bar: MultiBar) {
|
||||
const binaryName = getBinaryName("lens-k8s-proxy", { forPlatform: args.platform });
|
||||
|
||||
super(deps, { ...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(deps: BinaryDownloaderDependencies, args: Omit<BinaryDownloaderArgs, "binaryName">, bar: MultiBar) {
|
||||
const binaryName = getBinaryName("kubectl", { forPlatform: args.platform });
|
||||
|
||||
super(deps, { ...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(deps: BinaryDownloaderDependencies, args: Omit<BinaryDownloaderArgs, "binaryName">, bar: MultiBar) {
|
||||
const binaryName = getBinaryName("helm", { forPlatform: args.platform });
|
||||
|
||||
super(deps, { ...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";
|
||||
|
||||
const importFetchModule = new Function('return import("node-fetch")') as () => Promise<typeof FetchModule>;
|
||||
|
||||
async function main() {
|
||||
const deps: BinaryDownloaderDependencies = {
|
||||
fetch: (await importFetchModule()).default,
|
||||
};
|
||||
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`);
|
||||
}
|
||||
})();
|
||||
const multiBar = new MultiBar({
|
||||
align: "left",
|
||||
clearOnComplete: false,
|
||||
hideCursor: true,
|
||||
autopadding: true,
|
||||
noTTYOutput: true,
|
||||
format: "[{bar}] {percentage}% | {downloadArch} {binaryName}",
|
||||
});
|
||||
const baseDir = path.join(process.cwd(), "binaries", "client");
|
||||
const downloaders: BinaryDownloader[] = [
|
||||
new LensK8sProxyDownloader(deps, {
|
||||
version: packageInfo.config.k8sProxyVersion,
|
||||
platform: normalizedPlatform,
|
||||
downloadArch: "amd64",
|
||||
fileArch: "x64",
|
||||
baseDir,
|
||||
}, multiBar),
|
||||
new KubectlDownloader(deps, {
|
||||
version: packageInfo.config.bundledKubectlVersion,
|
||||
platform: normalizedPlatform,
|
||||
downloadArch: "amd64",
|
||||
fileArch: "x64",
|
||||
baseDir,
|
||||
}, multiBar),
|
||||
new HelmDownloader(deps, {
|
||||
version: packageInfo.config.bundledHelmVersion,
|
||||
platform: normalizedPlatform,
|
||||
downloadArch: "amd64",
|
||||
fileArch: "x64",
|
||||
baseDir,
|
||||
}, multiBar),
|
||||
];
|
||||
|
||||
if (normalizedPlatform !== "windows") {
|
||||
downloaders.push(
|
||||
new LensK8sProxyDownloader(deps, {
|
||||
version: packageInfo.config.k8sProxyVersion,
|
||||
platform: normalizedPlatform,
|
||||
downloadArch: "arm64",
|
||||
fileArch: "arm64",
|
||||
baseDir,
|
||||
}, multiBar),
|
||||
new KubectlDownloader(deps, {
|
||||
version: packageInfo.config.bundledKubectlVersion,
|
||||
platform: normalizedPlatform,
|
||||
downloadArch: "arm64",
|
||||
fileArch: "arm64",
|
||||
baseDir,
|
||||
}, multiBar),
|
||||
new HelmDownloader(deps, {
|
||||
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));
|
||||
12
packages/open-lens/build/entitlements.mac.plist
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
139
packages/open-lens/build/generate-tray-icons.ts
Normal file
@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { ensureDir, readFile } from "fs-extra";
|
||||
import { JSDOM } from "jsdom";
|
||||
import path from "path";
|
||||
import sharp from "sharp";
|
||||
|
||||
const size = Number(process.env.OUTPUT_SIZE || "16");
|
||||
const outputFolder = process.env.OUTPUT_DIR || "./static/build/tray";
|
||||
const inputFile = process.env.INPUT_SVG_PATH || path.resolve(__dirname, "../src/renderer/components/icon/logo-lens.svg");
|
||||
const noticeFile = process.env.NOTICE_SVG_PATH || path.resolve(__dirname, "../src/renderer/components/icon/notice.svg");
|
||||
const spinnerFile = process.env.SPINNER_SVG_PATH || path.resolve(__dirname, "../src/renderer/components/icon/arrow-spinner.svg");
|
||||
|
||||
async function ensureOutputFoler() {
|
||||
await ensureDir(outputFolder);
|
||||
}
|
||||
|
||||
function getSvgStyling(colouring: "dark" | "light"): string {
|
||||
return `
|
||||
<style>
|
||||
ellipse {
|
||||
stroke: ${colouring === "dark" ? "white" : "black"} !important;
|
||||
}
|
||||
path, rect {
|
||||
fill: ${colouring === "dark" ? "white" : "black"} !important;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
type TargetSystems = "macos" | "windows-or-linux";
|
||||
|
||||
async function getBaseIconImage(system: TargetSystems) {
|
||||
const svgData = await readFile(inputFile, { encoding: "utf-8" });
|
||||
const dom = new JSDOM(`<body>${svgData}</body>`);
|
||||
const root = dom.window.document.body.getElementsByTagName("svg")[0];
|
||||
|
||||
root.innerHTML += getSvgStyling(system === "macos" ? "light" : "dark");
|
||||
|
||||
return Buffer.from(root.outerHTML);
|
||||
}
|
||||
|
||||
async function generateImage(image: Buffer, size: number, namePrefix: string) {
|
||||
sharp(image)
|
||||
.resize({ width: size, height: size })
|
||||
.png()
|
||||
.toFile(path.join(outputFolder, `${namePrefix}.png`));
|
||||
}
|
||||
|
||||
async function generateImages(image: Buffer, size: number, name: string) {
|
||||
await Promise.all([
|
||||
generateImage(image, size, name),
|
||||
generateImage(image, size*2, `${name}@2x`),
|
||||
generateImage(image, size*3, `${name}@3x`),
|
||||
generateImage(image, size*4, `${name}@4x`),
|
||||
]);
|
||||
}
|
||||
|
||||
async function generateImageWithSvg(baseImage: Buffer, system: TargetSystems, filePath: string) {
|
||||
const svgFile = await getIconImage(system, filePath);
|
||||
|
||||
const circleBuffer = await sharp(Buffer.from(`
|
||||
<svg viewBox="0 0 64 64">
|
||||
<circle cx="32" cy="32" r="32" fill="black" />
|
||||
</svg>
|
||||
`))
|
||||
.toBuffer();
|
||||
|
||||
return sharp(baseImage)
|
||||
.resize({ width: 128, height: 128 })
|
||||
.composite([
|
||||
{
|
||||
input: circleBuffer,
|
||||
top: 64,
|
||||
left: 64,
|
||||
blend: "dest-out",
|
||||
},
|
||||
{
|
||||
input: (
|
||||
await sharp(svgFile)
|
||||
.resize({
|
||||
width: 60,
|
||||
height: 60,
|
||||
})
|
||||
.toBuffer()
|
||||
),
|
||||
top: 66,
|
||||
left: 66,
|
||||
},
|
||||
])
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
async function getIconImage(system: TargetSystems, filePath: string) {
|
||||
const svgData = await readFile(filePath, { encoding: "utf-8" });
|
||||
const root = new JSDOM(svgData).window.document.getElementsByTagName("svg")[0];
|
||||
|
||||
root.innerHTML += getSvgStyling(system === "macos" ? "light" : "dark");
|
||||
|
||||
return Buffer.from(root.outerHTML);
|
||||
}
|
||||
|
||||
async function generateTrayIcons() {
|
||||
try {
|
||||
console.log("Generating tray icon pngs");
|
||||
await ensureOutputFoler();
|
||||
|
||||
const baseIconTemplateImage = await getBaseIconImage("macos");
|
||||
const baseIconImage = await getBaseIconImage("windows-or-linux");
|
||||
|
||||
const updateAvailableTemplateImage = await generateImageWithSvg(baseIconTemplateImage, "macos", noticeFile);
|
||||
const updateAvailableImage = await generateImageWithSvg(baseIconImage, "windows-or-linux", noticeFile);
|
||||
|
||||
const checkingForUpdatesTemplateImage = await generateImageWithSvg(baseIconTemplateImage, "macos", spinnerFile);
|
||||
const checkingForUpdatesImage = await generateImageWithSvg(baseIconImage, "windows-or-linux", spinnerFile);
|
||||
|
||||
await Promise.all([
|
||||
// Templates are for macOS only
|
||||
generateImages(baseIconTemplateImage, size, "trayIconTemplate"),
|
||||
generateImages(updateAvailableTemplateImage, size, "trayIconUpdateAvailableTemplate"),
|
||||
generateImages(updateAvailableTemplateImage, size, "trayIconUpdateAvailableTemplate"),
|
||||
generateImages(checkingForUpdatesTemplateImage, size, "trayIconCheckingForUpdatesTemplate"),
|
||||
|
||||
// Non-templates are for windows and linux
|
||||
generateImages(baseIconImage, size, "trayIcon"),
|
||||
generateImages(updateAvailableImage, size, "trayIconUpdateAvailable"),
|
||||
generateImages(checkingForUpdatesImage, size, "trayIconCheckingForUpdates"),
|
||||
]);
|
||||
|
||||
console.log("Generated all images");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
generateTrayIcons();
|
||||
BIN
packages/open-lens/build/icon.ico
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
packages/open-lens/build/icon.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
packages/open-lens/build/icons/512x512.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
packages/open-lens/build/icons/512x512@2x.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
15
packages/open-lens/build/installer.nsh
Normal file
@ -0,0 +1,15 @@
|
||||
!macro customInit
|
||||
; Make sure all old extensions are removed
|
||||
RMDir /r "$INSTDIR\resources\extensions"
|
||||
|
||||
; Workaround for installer handing when the app directory is removed manually
|
||||
${ifNot} ${FileExists} "$INSTDIR"
|
||||
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\{${UNINSTALL_APP_KEY}}"
|
||||
${EndIf}
|
||||
|
||||
; Workaround for the old-format uninstall registry key (some people report it causes hangups, too)
|
||||
ReadRegStr $0 HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}" "QuietUninstallString"
|
||||
StrCmp $0 "" proceed 0
|
||||
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}"
|
||||
proceed:
|
||||
!macroend
|
||||
27
packages/open-lens/build/notarize.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
const { notarize } = require("electron-notarize");
|
||||
|
||||
exports.default = async function notarizing(context) {
|
||||
const { electronPlatformName, appOutDir } = context;
|
||||
|
||||
if (electronPlatformName !== "darwin") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.APPLEID || !process.env.APPLEIDPASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const appName = context.packager.appInfo.productFilename;
|
||||
|
||||
return await notarize({
|
||||
appBundleId: process.env.APPBUNDLEID || "io.kontena.lens-app",
|
||||
appPath: `${appOutDir}/${appName}.app`,
|
||||
appleId: process.env.APPLEID,
|
||||
appleIdPassword: process.env.APPLEIDPASS,
|
||||
ascProvider:process.env.ASCPROVIDER,
|
||||
});
|
||||
};
|
||||
BIN
packages/open-lens/build/tray/trayIcon.png
Normal file
|
After Width: | Height: | Size: 392 B |
BIN
packages/open-lens/build/tray/trayIcon@2x.png
Normal file
|
After Width: | Height: | Size: 724 B |
BIN
packages/open-lens/build/tray/trayIcon@3x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
packages/open-lens/build/tray/trayIcon@4x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
packages/open-lens/build/tray/trayIconCheckingForUpdates.png
Normal file
|
After Width: | Height: | Size: 504 B |
BIN
packages/open-lens/build/tray/trayIconCheckingForUpdates@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
packages/open-lens/build/tray/trayIconCheckingForUpdates@3x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
packages/open-lens/build/tray/trayIconCheckingForUpdates@4x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 442 B |
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
BIN
packages/open-lens/build/tray/trayIconTemplate.png
Normal file
|
After Width: | Height: | Size: 397 B |
BIN
packages/open-lens/build/tray/trayIconTemplate@2x.png
Normal file
|
After Width: | Height: | Size: 717 B |
BIN
packages/open-lens/build/tray/trayIconTemplate@3x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
packages/open-lens/build/tray/trayIconTemplate@4x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
packages/open-lens/build/tray/trayIconUpdateAvailable.png
Normal file
|
After Width: | Height: | Size: 518 B |
BIN
packages/open-lens/build/tray/trayIconUpdateAvailable@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
packages/open-lens/build/tray/trayIconUpdateAvailable@3x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
packages/open-lens/build/tray/trayIconUpdateAvailable@4x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 466 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
2
packages/open-lens/build/webpack/node-fetch.bundle.js
Normal file
@ -0,0 +1 @@
|
||||
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
@ -10,12 +10,12 @@ import * as uuid from "uuid";
|
||||
import type { ElectronApplication, Frame, Page } from "playwright";
|
||||
import { _electron as electron } from "playwright";
|
||||
import { noop } from "lodash";
|
||||
import { disposer } from "../../src/common/utils";
|
||||
import { disposer } from "../../../core/src/common/utils";
|
||||
|
||||
export const appPaths: Partial<Record<NodeJS.Platform, string>> = {
|
||||
"win32": "./dist/win-unpacked/OpenLens.exe",
|
||||
"linux": "./dist/linux-unpacked/open-lens",
|
||||
"darwin": "./dist/mac/OpenLens.app/Contents/MacOS/OpenLens",
|
||||
"darwin": `./dist/mac${ process.arch === "arm64" ? "-arm64" : "" }/OpenLens.app/Contents/MacOS/OpenLens`,
|
||||
};
|
||||
|
||||
async function getMainWindow(app: ElectronApplication, timeout = 50_000): Promise<Page> {
|
||||
6
packages/open-lens/integration/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": [
|
||||
"./**/*",
|
||||
]
|
||||
}
|
||||
@ -20,7 +20,10 @@
|
||||
"scripts": {
|
||||
"clean": "rm -rf binaries/ dist/ static/build",
|
||||
"build": "npm run compile",
|
||||
"build:app": "electron-builder --publish onTag",
|
||||
"build:app": "electron-builder --publish onTag $ELECTRON_BUILDER_EXTRA_ARGS",
|
||||
"prebuild:app": "run-script-os",
|
||||
"prebuild:app:default": "exit 0",
|
||||
"prebuild:app:win32": "rm -rf node_modules/win-ca/pem",
|
||||
"build:dir": "npm run compile && electron-builder --dir",
|
||||
"compile": "env NODE_ENV=production webpack --config webpack/webpack.ts --progress",
|
||||
"postcompile": "npm run build:tray-icons && npm run download:binaries",
|
||||
@ -29,8 +32,9 @@
|
||||
"dev-run": "nodemon --watch ./static/build/main.js --exec \"electron --remote-debugging-port=9223 --inspect .\"",
|
||||
"dev:main": "env NODE_ENV=development webpack --config webpack/main.ts --progress --watch",
|
||||
"dev:renderer": "env NODE_ENV=development ts-node ./webpack/dev-server.ts",
|
||||
"build:tray-icons": "ts-node ../core/build/generate-tray-icons.ts",
|
||||
"download:binaries": "ts-node ../core/build/download_binaries.ts"
|
||||
"test:integration": "func() { jest ${1:-xyz} --runInBand --detectOpenHandles --forceExit --modulePaths=[\"<rootDir>/integration/\"]; }; func",
|
||||
"build:tray-icons": "ts-node build/generate-tray-icons.ts",
|
||||
"download:binaries": "ts-node build/download_binaries.ts"
|
||||
},
|
||||
"config": {
|
||||
"k8sProxyVersion": "0.3.0",
|
||||
@ -50,26 +54,21 @@
|
||||
"^.+\\.(t|j)sx?$": [
|
||||
"@swc/jest"
|
||||
]
|
||||
},
|
||||
"testEnvironment": "jsdom",
|
||||
"resolver": "<rootDir>/src/jest-28-resolver.js",
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss)$": "identity-obj-proxy",
|
||||
"\\.(svg|png|jpg|eot|woff2?|ttf)$": "<rootDir>/__mocks__/assetMock.ts"
|
||||
},
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/dist",
|
||||
"<rootDir>/packages"
|
||||
],
|
||||
"setupFiles": [
|
||||
"<rootDir>/src/jest.setup.ts",
|
||||
"jest-canvas-mock"
|
||||
],
|
||||
"globalSetup": "<rootDir>/src/jest.timezone.ts",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/src/jest-after-env.setup.ts"
|
||||
],
|
||||
"runtime": "@side/jest-runtime"
|
||||
}
|
||||
},
|
||||
"nx": {
|
||||
"targets": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"{workspaceRoot}/dist/",
|
||||
"{workspaceRoot}/binaries/",
|
||||
"{workspaceRoot}/static/build/"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"generateUpdatesFilesForAllChannels": true,
|
||||
@ -78,7 +77,7 @@
|
||||
"!node_modules/@k8slens/open-lens/node_modules/**/*",
|
||||
"!node_modules/@k8slens/open-lens/src"
|
||||
],
|
||||
"afterSign": "../core/build/notarize.js",
|
||||
"afterSign": "build/notarize.js",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "templates/",
|
||||
@ -118,8 +117,8 @@
|
||||
"mac": {
|
||||
"hardenedRuntime": true,
|
||||
"gatekeeperAssess": false,
|
||||
"entitlements": "../core/build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "../core/build/entitlements.mac.plist",
|
||||
"entitlements": "build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "core/build/entitlements.mac.plist",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "binaries/client/darwin/${arch}/kubectl",
|
||||
@ -155,7 +154,7 @@
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"include": "../core/build/installer.nsh",
|
||||
"include": "build/installer.nsh",
|
||||
"oneClick": false,
|
||||
"allowElevation": true,
|
||||
"createStartMenuShortcut": true,
|
||||
@ -180,6 +179,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@swc/core": "^1.3.26",
|
||||
"@swc/jest": "^0.2.24",
|
||||
"@types/byline": "^4.2.33",
|
||||
"@types/chart.js": "^2.9.36",
|
||||
"@types/color": "^3.0.3",
|
||||
@ -212,6 +213,8 @@
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.14",
|
||||
"gunzip-maybe": "^1.4.2",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"jest": "^28.1.3",
|
||||
"jest-environment-jsdom": "^28.1.3",
|
||||
"jsdom": "^20.0.3",
|
||||
"jsonfile": "^6.1.0",
|
||||
"mini-css-extract-plugin": "^2.7.1",
|
||||
@ -219,9 +222,11 @@
|
||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"playwright": "^1.29.2",
|
||||
"react-refresh": "^0.14.0",
|
||||
"react-refresh-typescript": "^2.0.7",
|
||||
"react-select": "^5.7.0",
|
||||
"run-script-os": "^1.1.6",
|
||||
"sharp": "^0.31.2",
|
||||
"style-loader": "^3.3.1",
|
||||
"tailwindcss": "^3.2.4",
|
||||
|
||||