diff --git a/Makefile b/Makefile index 86cdf24e95..068116b3ba 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,10 @@ compile-dev: yarn compile:renderer --cache dev: - test -f out/main.js || make init - yarn dev # run electron and watch files +ifeq ("$(wildcard static/build/main.js)","") + make init +endif + yarn dev lint: yarn lint @@ -53,6 +55,12 @@ else endif clean: +ifeq "$(DETECTED_OS)" "Windows" + if exist binaries\client del /s /q binaries\client\*.* + if exist dist del /s /q dist\*.* + if exist static\build del /s /q static\build\*.* +else rm -rf binaries/client/* rm -rf dist/* - rm -rf out/* + rm -rf static/build/* +endif \ No newline at end of file diff --git a/integration/specs/app_spec.ts b/integration/specs/app_spec.ts index f3cb922740..9c9a7361fb 100644 --- a/integration/specs/app_spec.ts +++ b/integration/specs/app_spec.ts @@ -26,11 +26,8 @@ describe("app start", () => { const waitForMinikubeDashboard = async (app: Application) => { await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started") let windowCount = await app.client.getWindowCount() - // wait for webview to appear on window count - while (windowCount == 1) { - windowCount = await app.client.getWindowCount() - } - await app.client.windowByIndex(windowCount - 1) + await app.client.waitForExist(`iframe[name="minikube"]`) + await app.client.frame("minikube") await app.client.waitUntilTextExists("span.link-text", "Cluster") } @@ -39,10 +36,10 @@ describe("app start", () => { await app.start() await app.client.waitUntilWindowLoaded() let windowCount = await app.client.getWindowCount() - while (windowCount > 1) { + while (windowCount > 1) { // Wait for splash screen to be closed windowCount = await app.client.getWindowCount() } - await app.client.windowByIndex(windowCount - 1) + await app.client.windowByIndex(0) await app.client.waitUntilWindowLoaded() }, 20000) diff --git a/locales/en/messages.po b/locales/en/messages.po index 9ccefa8003..8ae622d54c 100644 --- a/locales/en/messages.po +++ b/locales/en/messages.po @@ -83,7 +83,7 @@ msgid "Account Name" msgstr "Account Name" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:51 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:46 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:50 msgid "Active" msgstr "Active" @@ -181,7 +181,7 @@ msgstr "Affinities" #: src/renderer/components/+user-management-roles/roles.tsx:35 #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:38 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:38 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:48 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:52 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:50 #: src/renderer/components/+workloads-deployments/deployments.tsx:63 #: src/renderer/components/+workloads-jobs/jobs.tsx:41 @@ -707,7 +707,7 @@ msgstr "Created at" msgid "Credentials Ref" msgstr "Credentials Ref" -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:40 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:44 msgid "Cron Jobs" msgstr "Cron Jobs" @@ -1190,6 +1190,10 @@ msgstr "Item list is empty" msgid "JSON Path" msgstr "JSON Path" +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:91 +msgid "Job name" +msgstr "Job name" + #: src/renderer/components/+workloads/workloads.tsx:69 #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62 #: src/renderer/components/+workloads-jobs/jobs.tsx:36 @@ -1254,7 +1258,7 @@ msgid "Last Failure Time" msgstr "Last Failure Time" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:57 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:47 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:51 msgid "Last schedule" msgstr "Last schedule" @@ -1484,7 +1488,7 @@ msgstr "Mounts" #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:35 #: src/renderer/components/+user-management-service-accounts/service-accounts-secret.tsx:29 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:36 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:41 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:45 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:45 #: src/renderer/components/+workloads-deployments/deployments.tsx:58 #: src/renderer/components/+workloads-jobs/jobs.tsx:37 @@ -1534,7 +1538,7 @@ msgstr "Names" #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:37 #: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:79 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:37 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:43 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:47 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:46 #: src/renderer/components/+workloads-deployments/deployments.tsx:59 #: src/renderer/components/+workloads-jobs/jobs.tsx:38 @@ -2264,7 +2268,7 @@ msgid "Scale Deployment <0>{deploymentName}" msgstr "Scale Deployment <0>{deploymentName}" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:46 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:44 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:48 msgid "Schedule" msgstr "Schedule" @@ -2588,7 +2592,7 @@ msgid "Supplemental Groups" msgstr "Supplemental Groups" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:54 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:45 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:49 msgid "Suspend" msgstr "Suspend" @@ -2657,6 +2661,16 @@ msgstr "Tolerations" msgid "Transmit" msgstr "Transmit" +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80 +msgid "Trigger" +msgstr "Trigger" + +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103 +msgid "Trigger CronJob <0>{cronjobName}" +msgstr "Trigger CronJob <0>{cronjobName}" + #: src/renderer/components/+cluster/cluster-issues.tsx:102 #: src/renderer/components/+config-secrets/secret-details.tsx:74 #: src/renderer/components/+config-secrets/secrets.tsx:45 @@ -2897,7 +2911,7 @@ msgid "listKind" msgstr "listKind" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:48 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:57 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:61 msgid "never" msgstr "never" diff --git a/locales/fi/messages.po b/locales/fi/messages.po index 21760f5441..f64a50f272 100644 --- a/locales/fi/messages.po +++ b/locales/fi/messages.po @@ -83,7 +83,7 @@ msgid "Account Name" msgstr "" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:51 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:46 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:50 msgid "Active" msgstr "" @@ -181,7 +181,7 @@ msgstr "" #: src/renderer/components/+user-management-roles/roles.tsx:35 #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:38 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:38 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:48 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:52 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:50 #: src/renderer/components/+workloads-deployments/deployments.tsx:63 #: src/renderer/components/+workloads-jobs/jobs.tsx:41 @@ -703,7 +703,7 @@ msgstr "" msgid "Credentials Ref" msgstr "" -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:40 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:44 msgid "Cron Jobs" msgstr "" @@ -1181,6 +1181,10 @@ msgstr "" msgid "JSON Path" msgstr "" +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:91 +msgid "Job name" +msgstr "" + #: src/renderer/components/+workloads/workloads.tsx:69 #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62 #: src/renderer/components/+workloads-jobs/jobs.tsx:36 @@ -1245,7 +1249,7 @@ msgid "Last Failure Time" msgstr "" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:57 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:47 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:51 msgid "Last schedule" msgstr "" @@ -1475,7 +1479,7 @@ msgstr "" #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:35 #: src/renderer/components/+user-management-service-accounts/service-accounts-secret.tsx:29 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:36 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:41 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:45 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:45 #: src/renderer/components/+workloads-deployments/deployments.tsx:58 #: src/renderer/components/+workloads-jobs/jobs.tsx:37 @@ -1525,7 +1529,7 @@ msgstr "" #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:37 #: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:79 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:37 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:43 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:47 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:46 #: src/renderer/components/+workloads-deployments/deployments.tsx:59 #: src/renderer/components/+workloads-jobs/jobs.tsx:38 @@ -2247,7 +2251,7 @@ msgid "Scale Deployment <0>{deploymentName}" msgstr "" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:46 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:44 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:48 msgid "Schedule" msgstr "" @@ -2571,7 +2575,7 @@ msgid "Supplemental Groups" msgstr "" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:54 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:45 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:49 msgid "Suspend" msgstr "" @@ -2640,6 +2644,16 @@ msgstr "" msgid "Transmit" msgstr "" +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80 +msgid "Trigger" +msgstr "" + +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103 +msgid "Trigger CronJob <0>{cronjobName}" +msgstr "" + #: src/renderer/components/+cluster/cluster-issues.tsx:102 #: src/renderer/components/+config-secrets/secret-details.tsx:74 #: src/renderer/components/+config-secrets/secrets.tsx:45 @@ -2880,7 +2894,7 @@ msgid "listKind" msgstr "" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:48 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:57 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:61 msgid "never" msgstr "" diff --git a/locales/ru/messages.po b/locales/ru/messages.po index e1ae65a244..e3a021d837 100644 --- a/locales/ru/messages.po +++ b/locales/ru/messages.po @@ -84,7 +84,7 @@ msgid "Account Name" msgstr "Название аккаунта" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:51 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:46 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:50 msgid "Active" msgstr "Активный" @@ -182,7 +182,7 @@ msgstr "Аффинитеты" #: src/renderer/components/+user-management-roles/roles.tsx:35 #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:38 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:38 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:48 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:52 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:50 #: src/renderer/components/+workloads-deployments/deployments.tsx:63 #: src/renderer/components/+workloads-jobs/jobs.tsx:41 @@ -708,7 +708,7 @@ msgstr "Создано" msgid "Credentials Ref" msgstr "Credentials Ref" -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:40 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:44 msgid "Cron Jobs" msgstr "" @@ -1191,6 +1191,10 @@ msgstr "Список пуст" msgid "JSON Path" msgstr "" +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:91 +msgid "Job name" +msgstr "" + #: src/renderer/components/+workloads/workloads.tsx:69 #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62 #: src/renderer/components/+workloads-jobs/jobs.tsx:36 @@ -1255,7 +1259,7 @@ msgid "Last Failure Time" msgstr "Время последнего сбоя" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:57 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:47 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:51 msgid "Last schedule" msgstr "Последний запуск" @@ -1485,7 +1489,7 @@ msgstr "Установки" #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:35 #: src/renderer/components/+user-management-service-accounts/service-accounts-secret.tsx:29 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:36 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:41 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:45 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:45 #: src/renderer/components/+workloads-deployments/deployments.tsx:58 #: src/renderer/components/+workloads-jobs/jobs.tsx:37 @@ -1535,7 +1539,7 @@ msgstr "" #: src/renderer/components/+user-management-roles-bindings/role-bindings.tsx:37 #: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:79 #: src/renderer/components/+user-management-service-accounts/service-accounts.tsx:37 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:43 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:47 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:46 #: src/renderer/components/+workloads-deployments/deployments.tsx:59 #: src/renderer/components/+workloads-jobs/jobs.tsx:38 @@ -2265,7 +2269,7 @@ msgid "Scale Deployment <0>{deploymentName}" msgstr "Масштабировать Deployment <0>{deploymentName}" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:46 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:44 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:48 msgid "Schedule" msgstr "Расписание" @@ -2589,7 +2593,7 @@ msgid "Supplemental Groups" msgstr "" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:54 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:45 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:49 msgid "Suspend" msgstr "Заморозка" @@ -2658,6 +2662,16 @@ msgstr "Толерантности" msgid "Transmit" msgstr "Транзит" +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80 +msgid "Trigger" +msgstr "" + +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103 +msgid "Trigger CronJob <0>{cronjobName}" +msgstr "" + #: src/renderer/components/+cluster/cluster-issues.tsx:102 #: src/renderer/components/+config-secrets/secret-details.tsx:74 #: src/renderer/components/+config-secrets/secrets.tsx:45 @@ -2898,7 +2912,7 @@ msgid "listKind" msgstr "" #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:48 -#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:57 +#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:61 msgid "never" msgstr "" diff --git a/package.json b/package.json index d4581123b0..54e4038c77 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ }, "scripts": { "dev": "concurrently -k \"yarn dev-run -C\" \"yarn dev:main\" \"yarn dev:renderer\"", - "dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\" $@", - "dev:main": "env DEBUG=true yarn compile:main --watch $@", - "dev:renderer": "env DEBUG=true yarn compile:renderer --watch $@", + "dev-run": "cross-env DEBUG=true nodemon --watch static/build/main.js --exec \"electron --inspect .\"", + "dev:main": "cross-env DEBUG=true yarn compile:main --watch", + "dev:renderer": "cross-env DEBUG=true yarn compile:renderer --watch", "compile": "env NODE_ENV=production concurrently yarn:compile:*", "compile:main": "webpack --config webpack.main.ts", "compile:renderer": "webpack --config webpack.renderer.ts", @@ -175,6 +175,7 @@ "crypto-js": "^4.0.0", "electron-updater": "^4.3.1", "electron-window-state": "^5.0.3", + "file-type": "^14.7.1", "filenamify": "^4.1.0", "fs-extra": "^9.0.1", "handlebars": "^4.7.6", @@ -184,6 +185,7 @@ "jsonpath": "^1.0.2", "lodash": "^4.17.15", "mac-ca": "^1.0.4", + "make-synchronous": "^0.1.1", "marked": "^1.1.0", "md5-file": "^5.0.0", "mobx": "^5.15.5", @@ -264,6 +266,7 @@ "circular-dependency-plugin": "^5.2.0", "color": "^3.1.2", "concurrently": "^5.2.0", + "cross-env": "^7.0.2", "css-element-queries": "^1.2.3", "css-loader": "^3.5.3", "dompurify": "^2.0.11", diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index 63dbfafe0e..fb47294102 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -1,6 +1,6 @@ import type { WorkspaceId } from "./workspace-store"; import path from "path"; -import { app, ipcRenderer, remote } from "electron"; +import { app, remote, ipcRenderer } from "electron"; import { unlink } from "fs-extra"; import { action, computed, observable, toJS } from "mobx"; import { BaseStore } from "./base-store"; diff --git a/src/common/cluster-store_test.ts b/src/common/cluster-store_test.ts index ed4dd3a369..fd27d0da43 100644 --- a/src/common/cluster-store_test.ts +++ b/src/common/cluster-store_test.ts @@ -5,6 +5,10 @@ import { Cluster } from "../main/cluster"; import { ClusterStore } from "./cluster-store"; import { workspaceStore } from "./workspace-store"; +const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png") + +console.log("") // fix bug + let clusterStore: ClusterStore; describe("empty config", () => { @@ -231,12 +235,13 @@ describe("pre 2.6.0 config with a cluster icon", () => { }, cluster1: { kubeConfig: "foo", - icon: "icon path", + icon: "icon_path", preferences: { terminalCWD: "/tmp" } }, - }) + }), + "icon_path": testDataIcon, } } mockFs(mockOpts); @@ -252,7 +257,7 @@ describe("pre 2.6.0 config with a cluster icon", () => { const storedClusterData = clusterStore.clustersList[0]; expect(storedClusterData.hasOwnProperty('icon')).toBe(false); expect(storedClusterData.preferences.hasOwnProperty('icon')).toBe(true); - expect(storedClusterData.preferences.icon).toBe("icon path"); + expect(storedClusterData.preferences.icon.startsWith("data:image/jpeg;base64,")).toBe(true); }) }) @@ -269,7 +274,6 @@ describe("for a pre 2.7.0-beta.0 config without a workspace", () => { }, cluster1: { kubeConfig: "foo", - icon: "icon path", preferences: { terminalCWD: "/tmp" } @@ -300,16 +304,20 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => { 'lens-cluster-store.json': JSON.stringify({ __internal__: { migrations: { - version: "2.7.0" + version: "3.5.0" } }, clusters: [ { id: 'cluster1', - kubeConfig: 'kubeconfig content' + kubeConfig: 'kubeconfig content', + preferences: { + icon: "store://icon_path", + } } ] - }) + }), + "icon_path": testDataIcon, } }; mockFs(mockOpts); @@ -325,4 +333,9 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => { const config = clusterStore.clustersList[0].kubeConfigPath; expect(fs.readFileSync(config, "utf8")).toBe("kubeconfig content"); }) + + it("migrates to modern format with icon not in file", async () => { + const { icon } = clusterStore.clustersList[0].preferences; + expect(icon.startsWith("data:image/jpeg;base64, ")).toBe(true); + }) }) \ No newline at end of file diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 6ba0fb1f48..5176846ba4 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -27,6 +27,7 @@ export interface ClusterState extends ClusterModel { online: boolean; disconnected: boolean; accessible: boolean; + ready: boolean; failureReason: string; nodes: number; eventCount: number; @@ -47,6 +48,7 @@ export class Cluster implements ClusterModel { protected eventDisposers: Function[] = []; whenInitialized = when(() => this.initialized); + whenReady = when(() => this.ready); @observable initialized = false; @observable contextName: string; @@ -56,6 +58,7 @@ export class Cluster implements ClusterModel { @observable kubeProxyUrl: string; // lens-proxy to kube-api url @observable online: boolean; @observable accessible: boolean; + @observable ready: boolean; @observable disconnected: boolean; @observable failureReason: string; @observable nodes = 0; @@ -149,6 +152,7 @@ export class Cluster implements ClusterModel { this.disconnected = true; this.online = false; this.accessible = false; + this.ready = false; this.pushState(); } @@ -172,6 +176,7 @@ export class Cluster implements ClusterModel { this.refreshEvents(), this.refreshAllowedResources(), ]); + this.ready = true } } @@ -370,6 +375,7 @@ export class Cluster implements ClusterModel { initialized: this.initialized, apiUrl: this.apiUrl, online: this.online, + ready: this.ready, disconnected: this.disconnected, accessible: this.accessible, failureReason: this.failureReason, diff --git a/src/migrations/cluster-store/2.7.0-beta.0.ts b/src/migrations/cluster-store/2.7.0-beta.0.ts index 22c4e6bba9..3e0ae9337f 100644 --- a/src/migrations/cluster-store/2.7.0-beta.0.ts +++ b/src/migrations/cluster-store/2.7.0-beta.0.ts @@ -6,7 +6,7 @@ export default migration({ run(store, log) { for (const value of store) { const clusterKey = value[0]; - if(clusterKey === "__internal__") continue + if (clusterKey === "__internal__") continue const cluster = value[1]; cluster.workspace = "default" store.set(clusterKey, cluster) diff --git a/src/migrations/cluster-store/3.6.0-beta.1.ts b/src/migrations/cluster-store/3.6.0-beta.1.ts index ae5edb2399..18ce07a638 100644 --- a/src/migrations/cluster-store/3.6.0-beta.1.ts +++ b/src/migrations/cluster-store/3.6.0-beta.1.ts @@ -1,32 +1,71 @@ // Move embedded kubeconfig into separate file and add reference to it to cluster settings +// convert file path cluster icons to their base64 encoded versions +import path from "path" +import { app, remote } from "electron" import { migration } from "../migration-wrapper"; -import { ensureDirSync } from "fs-extra" +import fse from "fs-extra" import { ClusterModel, ClusterStore } from "../../common/cluster-store"; import { loadConfig } from "../../common/kube-helpers"; +import makeSynchronous from "make-synchronous" + +const AsyncFunction = Object.getPrototypeOf(async function () { return }).constructor; +const getFileTypeFnString = `return require("file-type").fromBuffer(fileData)`; +const getFileType = new AsyncFunction("fileData", getFileTypeFnString); export default migration({ version: "3.6.0-beta.1", run(store, printLog) { - const migratedClusters: ClusterModel[] = [] - const storedClusters: ClusterModel[] = store.get("clusters"); + const userDataPath = (app || remote.app).getPath("userData") const kubeConfigBase = ClusterStore.getCustomKubeConfigPath(""); + const storedClusters: ClusterModel[] = store.get("clusters") || []; - if (!storedClusters) return; - ensureDirSync(kubeConfigBase); + if (!storedClusters.length) return; + fse.ensureDirSync(kubeConfigBase); printLog("Number of clusters to migrate: ", storedClusters.length) - for (const cluster of storedClusters) { - try { - // take the embedded kubeconfig and dump it into a file] - cluster.kubeConfigPath = ClusterStore.embedCustomKubeConfig(cluster.id, cluster.kubeConfig); - cluster.contextName = loadConfig(cluster.kubeConfigPath).getCurrentContext(); - delete cluster.kubeConfig; - migratedClusters.push(cluster) - } catch (error) { - printLog(`Failed to migrate Kubeconfig for cluster "${cluster.id}"`, error) - } - } + const migratedClusters = storedClusters + .map(cluster => { + /** + * migrate kubeconfig + */ + try { + // take the embedded kubeconfig and dump it into a file + cluster.kubeConfigPath = ClusterStore.embedCustomKubeConfig(cluster.id, cluster.kubeConfig); + cluster.contextName = loadConfig(cluster.kubeConfigPath).getCurrentContext(); + delete cluster.kubeConfig; + + } catch (error) { + printLog(`Failed to migrate Kubeconfig for cluster "${cluster.id}", removing cluster...`, error) + return undefined; + } + + /** + * migrate cluster icon + */ + try { + if (cluster.preferences?.icon) { + printLog(`migrating ${cluster.preferences.icon} for ${cluster.preferences.clusterName}`) + const iconPath = cluster.preferences.icon.replace("store://", "") + const fileData = fse.readFileSync(path.join(userDataPath, iconPath)); + const { mime = "" } = makeSynchronous(getFileType)(fileData); + + if (!mime) { + printLog(`mime type not detected for ${cluster.preferences.clusterName}'s icon: ${iconPath}`) + } + + cluster.preferences.icon = `data:${mime};base64, ${fileData.toString('base64')}`; + } else { + delete cluster.preferences?.icon; + } + } catch (error) { + printLog(`Failed to migrate cluster icon for cluster "${cluster.id}"`, error) + delete cluster.preferences.icon; + } + + return cluster; + }) + .filter(c => c); // "overwrite" the cluster configs if (migratedClusters.length > 0) { diff --git a/src/renderer/api/endpoints/cron-job.api.ts b/src/renderer/api/endpoints/cron-job.api.ts index e48a8636b8..887ec9833c 100644 --- a/src/renderer/api/endpoints/cron-job.api.ts +++ b/src/renderer/api/endpoints/cron-job.api.ts @@ -32,6 +32,12 @@ export class CronJob extends KubeObject { jobTemplate: { metadata: { creationTimestamp?: string; + labels?: { + [key: string]: string; + }; + annotations?: { + [key: string]: string; + }; }; spec: { template: { @@ -53,7 +59,7 @@ export class CronJob extends KubeObject { failedJobsHistoryLimit: number; } status: { - lastScheduleTime: string; + lastScheduleTime?: string; } getSuspendFlag() { @@ -61,6 +67,7 @@ export class CronJob extends KubeObject { } getLastScheduleTime() { + if (!this.status.lastScheduleTime) return "-" const diff = moment().diff(this.status.lastScheduleTime) return formatDuration(diff, true) } diff --git a/src/renderer/api/endpoints/job.api.ts b/src/renderer/api/endpoints/job.api.ts index d4657605f6..6c6c1967c1 100644 --- a/src/renderer/api/endpoints/job.api.ts +++ b/src/renderer/api/endpoints/job.api.ts @@ -13,7 +13,7 @@ export class Job extends WorkloadKubeObject { parallelism?: number; completions?: number; backoffLimit?: number; - selector: { + selector?: { matchLabels: { [name: string]: string; }; @@ -21,8 +21,11 @@ export class Job extends WorkloadKubeObject { template: { metadata: { creationTimestamp?: string; - labels: { - name: string; + labels?: { + [name: string]: string; + }; + annotations?: { + [name: string]: string; }; }; spec: { @@ -35,7 +38,7 @@ export class Job extends WorkloadKubeObject { nodeSelector?: { [selector: string]: string; }; - tolerations: { + tolerations?: { key: string; operator: string; effect: string; diff --git a/src/renderer/components/+cluster-settings/components/cluster-icon-setting.tsx b/src/renderer/components/+cluster-settings/components/cluster-icon-setting.tsx index 20b4e6d6d5..04b7ef1fc6 100644 --- a/src/renderer/components/+cluster-settings/components/cluster-icon-setting.tsx +++ b/src/renderer/components/+cluster-settings/components/cluster-icon-setting.tsx @@ -28,7 +28,7 @@ export class ClusterIconSetting extends React.Component { try { if (file) { const buf = Buffer.from(await file.arrayBuffer()); - cluster.preferences.icon = `data:image/jpeg;base64, ${buf.toString('base64')}`; + cluster.preferences.icon = `data:${file.type};base64, ${buf.toString('base64')}`; } else { // this has to be done as a seperate branch (and not always) because `cluster` // is observable and triggers an update loop. diff --git a/src/renderer/components/+landing-page/landing-page.scss b/src/renderer/components/+landing-page/landing-page.scss index 6cc726f5d9..0d05c418d8 100644 --- a/src/renderer/components/+landing-page/landing-page.scss +++ b/src/renderer/components/+landing-page/landing-page.scss @@ -1,8 +1,24 @@ .LandingPage { height: 100%; - background: #282b2f url(../../components/icon/crane.svg) no-repeat; - background-position: 0 35%; - background-size: 85%; - background-clip: content-box; text-align: center; + z-index: 0; + + &::after { + content: ""; + background: url(../../components/icon/crane.svg) no-repeat; + background-position: 0 35%; + background-size: 85%; + background-clip: content-box; + opacity: 1; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: absolute; + z-index: -1; + + .theme-light & { + opacity: 0.2; + } + } } \ No newline at end of file diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.scss b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.scss new file mode 100644 index 0000000000..ef52eebb08 --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.scss @@ -0,0 +1,18 @@ +.CronJobTriggerDialog { + .Wizard { + .header { + span { + color: #a0a0a0; + white-space: nowrap; + text-overflow: ellipsis; + } + } + + .WizardStep { + .step-content { + min-height: 90px; + overflow: hidden; + } + } + } +} \ No newline at end of file diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx new file mode 100644 index 0000000000..7f29c0478d --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx @@ -0,0 +1,127 @@ +import "./cronjob-trigger-dialog.scss"; + +import React, { Component } from "react"; +import { observable } from "mobx"; +import { observer } from "mobx-react"; +import { Trans } from "@lingui/macro"; +import { Dialog, DialogProps } from "../dialog"; +import { Wizard, WizardStep } from "../wizard"; +import { CronJob, cronJobApi, jobApi, Job } from "../../api/endpoints"; +import { Notifications } from "../notifications"; +import { cssNames } from "../../utils"; +import { Input } from "../input"; +import { systemName, maxLength } from "../input/input.validators"; + +interface Props extends Partial { +} + +@observer +export class CronJobTriggerDialog extends Component { + @observable static isOpen = false; + @observable static data: CronJob = null; + + @observable jobName = ""; + + @observable ready = false; + + static open(cronjob: CronJob) { + CronJobTriggerDialog.isOpen = true; + CronJobTriggerDialog.data = cronjob; + } + + static close() { + CronJobTriggerDialog.isOpen = false; + } + + get cronjob() { + return CronJobTriggerDialog.data; + } + + close = () => { + CronJobTriggerDialog.close(); + } + + onOpen = async () => { + const { cronjob } = this; + this.jobName = cronjob ? cronjob.getName() + "-manual-" + Math.random().toString(36).slice(2, 7) : ""; + this.jobName = this.jobName.slice(0, 63); + this.ready = true; + } + + onClose = () => { + this.ready = false; + } + + trigger = async () => { + const { cronjob } = this; + const { close } = this; + try { + const cronjobDefinition = await cronJobApi.get({ + name: cronjob.getName(), + namespace: cronjob.getNs() + }); + + await jobApi.create({ + name: this.jobName, + namespace: cronjob.getNs() + }, { + spec: cronjobDefinition.spec.jobTemplate.spec + }); + + close(); + } catch (err) { + Notifications.error(err); + } + } + + renderContents() { + return ( + <> +
+ Job name: +
+
+ this.jobName = v.toLowerCase()} + className="box grow" + /> +
+ + ) + } + + render() { + const { className, ...dialogProps } = this.props; + const cronjobName = this.cronjob ? this.cronjob.getName() : ""; + const header = ( +
+ Trigger CronJob {cronjobName} +
+ ); + return ( + + + Trigger} + disabledNext={!this.ready} + > + {this.renderContents()} + + + + ); + } +} \ No newline at end of file diff --git a/src/renderer/components/+workloads-cronjobs/cronjob.store.ts b/src/renderer/components/+workloads-cronjobs/cronjob.store.ts index 9e75c25121..e8fa3719ae 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob.store.ts +++ b/src/renderer/components/+workloads-cronjobs/cronjob.store.ts @@ -9,13 +9,13 @@ export class CronJobStore extends KubeObjectStore { api = cronJobApi getStatuses(cronJobs?: CronJob[]) { - const status = { failed: 0, running: 0 } + const status = { suspended: 0, scheduled: 0 } cronJobs.forEach(cronJob => { if (cronJob.spec.suspend) { - status.failed++ + status.suspended++ } else { - status.running++ + status.scheduled++ } }) return status diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index 4221302116..32a2dc50dc 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -3,8 +3,10 @@ import "./cronjobs.scss"; import React from "react"; import { observer } from "mobx-react"; import { RouteComponentProps } from "react-router"; -import { Trans } from "@lingui/macro"; +import { t, Trans } from "@lingui/macro"; import { CronJob, cronJobApi } from "../../api/endpoints/cron-job.api"; +import { MenuItem } from "../menu"; +import { Icon } from "../icon"; import { cronJobStore } from "./cronjob.store"; import { jobStore } from "../+workloads-jobs/job.store"; import { eventStore } from "../+events/event.store"; @@ -12,7 +14,9 @@ import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object- import { ICronJobsRouteParams } from "../+workloads"; import { KubeObjectListLayout } from "../kube-object"; import { KubeEventIcon } from "../+events/kube-event-icon"; +import { _i18n } from "../../i18n"; import { apiManager } from "../../api/api-manager"; +import { CronJobTriggerDialog } from "./cronjob-trigger-dialog"; enum sortBy { name = "name", @@ -79,8 +83,14 @@ export class CronJobs extends React.Component { } export function CronJobMenu(props: KubeObjectMenuProps) { + const { object, toolbar } = props; return ( - + + CronJobTriggerDialog.open(object)}> + + Trigger + + ) } diff --git a/src/renderer/components/+workloads-overview/overview-workload-status.scss b/src/renderer/components/+workloads-overview/overview-workload-status.scss index c06ed5b4c8..4d47570973 100644 --- a/src/renderer/components/+workloads-overview/overview-workload-status.scss +++ b/src/renderer/components/+workloads-overview/overview-workload-status.scss @@ -3,6 +3,8 @@ --workload-status-pending: #{$pod-status-pending-color}; --workload-status-evicted: #{$pod-status-evicted-color}; --workload-status-succeeded: #{$pod-status-succeeded-color}; + --workload-status-scheduled: #{$cronjob-scheduled}; + --workload-status-suspended: #{$cronjob-suspended}; --workload-status-failed: #{$pod-status-failed-color}; --workload-status-terminated: #{$pod-status-terminated-color}; --workload-status-unknown: #{$pod-status-unknown-color}; diff --git a/src/renderer/components/+workloads/workloads-mixins.scss b/src/renderer/components/+workloads/workloads-mixins.scss index 84bcca8219..8ba7f76665 100644 --- a/src/renderer/components/+workloads/workloads-mixins.scss +++ b/src/renderer/components/+workloads/workloads-mixins.scss @@ -26,6 +26,10 @@ $deployment-replicafailure: $colorError; $job-complete: $colorSuccess; $job-failed: $colorError; +// Cronjob +$cronjob-scheduled: $colorSuccess; +$cronjob-suspended: $colorTerminated; + // Pod Statuses $pod-status-color-list: ( running: $pod-status-running-color, @@ -48,6 +52,12 @@ $job-condition-color-list: ( failed: $job-failed, ); +// Cronjob Conditions +$cronjob-condition-color-list: ( + scheduled: $cronjob-scheduled, + suspended: $cronjob-suspended, +); + @mixin pod-status-bgs { @each $status, $color in $pod-status-color-list { &.#{$status} { diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index abdef9b7f4..23e71e2be9 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -25,6 +25,7 @@ import { KubeObjectDetails } from "./kube-object/kube-object-details"; import { AddRoleBindingDialog } from "./+user-management-roles-bindings"; import { PodLogsDialog } from "./+workloads-pods/pod-logs-dialog"; import { DeploymentScaleDialog } from "./+workloads-deployments/deployment-scale-dialog"; +import { CronJobTriggerDialog } from "./+workloads-cronjobs/cronjob-trigger-dialog"; import { CustomResources } from "./+custom-resources/custom-resources"; import { crdRoute } from "./+custom-resources"; import { isAllowedResource } from "../../common/rbac"; @@ -80,6 +81,7 @@ export class App extends React.Component { + diff --git a/src/renderer/components/chart/chart.tsx b/src/renderer/components/chart/chart.tsx index 92e3619031..a5fa4b9706 100644 --- a/src/renderer/components/chart/chart.tsx +++ b/src/renderer/components/chart/chart.tsx @@ -63,14 +63,14 @@ export class Chart extends React.Component { this.renderChart() } - componentDidUpdate(prevProps: ChartProps) { - const { data, showChart, redraw } = this.props + componentDidUpdate() { + const { showChart, redraw } = this.props if (redraw) { this.chart.destroy() this.renderChart() return } - if (!isEqual(prevProps.data, data) && showChart) { + if (showChart) { if (!this.chart) this.renderChart() else this.updateChart() } diff --git a/src/renderer/components/cluster-icon/cluster-icon.scss b/src/renderer/components/cluster-icon/cluster-icon.scss index b8a87a07de..540cecf9eb 100644 --- a/src/renderer/components/cluster-icon/cluster-icon.scss +++ b/src/renderer/components/cluster-icon/cluster-icon.scss @@ -30,7 +30,7 @@ position: absolute; right: 0; bottom: 0; - margin: -$padding * 1.5; + margin: -$padding; font-size: $font-size-small; background: $colorError; color: white; diff --git a/src/renderer/components/cluster-manager/bottom-bar.scss b/src/renderer/components/cluster-manager/bottom-bar.scss index 974585723a..359c123836 100644 --- a/src/renderer/components/cluster-manager/bottom-bar.scss +++ b/src/renderer/components/cluster-manager/bottom-bar.scss @@ -3,7 +3,7 @@ font-size: $font-size-small; background-color: #3d90ce; - padding: $spacing $padding; + padding: $padding / 4 $padding; color: white; #current-workspace { diff --git a/src/renderer/components/cluster-manager/bottom-bar.tsx b/src/renderer/components/cluster-manager/bottom-bar.tsx index 14809618f5..abbe81ec92 100644 --- a/src/renderer/components/cluster-manager/bottom-bar.tsx +++ b/src/renderer/components/cluster-manager/bottom-bar.tsx @@ -11,7 +11,7 @@ export class BottomBar extends React.Component { const { currentWorkspace } = workspaceStore; return (
-
+
{currentWorkspace.name}
diff --git a/src/renderer/components/cluster-manager/clusters-menu.scss b/src/renderer/components/cluster-manager/clusters-menu.scss index 0ee1f59d7e..5e5f08664d 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.scss +++ b/src/renderer/components/cluster-manager/clusters-menu.scss @@ -1,12 +1,11 @@ .ClustersMenu { - @include hidden-scrollbar; $spacing: $padding * 2; position: relative; text-align: center; - padding: $spacing; background: $clusterMenuBackground; border-right: 1px solid $clusterMenuBorderColor; + padding-bottom: $spacing; .is-mac & { padding-top: $spacing * 2; @@ -23,8 +22,7 @@ padding: $spacing; width: 320px; background: $bgc; - z-index: 100; - color: white; + color: $textColorAccent; filter: drop-shadow(0 0px 2px #ffffff33); pointer-events: none; @@ -38,11 +36,24 @@ border-right: $arrowSize solid $bgc; right: 100%; } + + .theme-light & { + filter: drop-shadow(0 0px 2px #777); + background: white; + + &:before { + border-right-color: white; + } + } + } + + .clusters { + @include hidden-scrollbar; + padding: 0 $spacing $spacing; } > .add-cluster { position: relative; - margin-top: $padding; min-width: 43px; .Icon { diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx index 19a80c6217..46a8613429 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.tsx +++ b/src/renderer/components/cluster-manager/clusters-menu.tsx @@ -99,7 +99,7 @@ export class ClustersMenu extends React.Component { const showStartupHint = this.showHint && isLanding && noClustersInScope; // fixme: broken, move to landing.tsx return (
this.showHint = false} > {showStartupHint && ( @@ -112,18 +112,20 @@ export class ClustersMenu extends React.Component {

)} - {clusters.map(cluster => { - return ( - this.showCluster(cluster.id)} - onContextMenu={() => this.showContextMenu(cluster)} - /> - ) - })} +
+ {clusters.map(cluster => { + return ( + this.showCluster(cluster.id)} + onContextMenu={() => this.showContextMenu(cluster)} + /> + ) + })} +
Add Cluster diff --git a/src/renderer/components/cluster-manager/lens-views.ts b/src/renderer/components/cluster-manager/lens-views.ts index 67a0ac68e2..25cb2d5f3a 100644 --- a/src/renderer/components/cluster-manager/lens-views.ts +++ b/src/renderer/components/cluster-manager/lens-views.ts @@ -21,10 +21,10 @@ export async function initView(clusterId: ClusterId) { } logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`) const cluster = clusterStore.getById(clusterId); - await cluster.whenInitialized; + await cluster.whenReady; const parentElem = document.getElementById("lens-views"); const iframe = document.createElement("iframe"); - iframe.name = cluster.preferences.clusterName; + iframe.name = cluster.contextName; iframe.setAttribute("src", `//${clusterId}.${location.host}`) iframe.addEventListener("load", async () => { logger.info(`[LENS-VIEW]: loaded from ${iframe.src}`) diff --git a/test-data/cluster-store-migration-icon.png b/test-data/cluster-store-migration-icon.png new file mode 100644 index 0000000000..623a0fea77 Binary files /dev/null and b/test-data/cluster-store-migration-icon.png differ diff --git a/yarn.lock b/yarn.lock index d8881d0f1d..9e75e68c2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1588,6 +1588,11 @@ dependencies: defer-to-connect "^1.0.1" +"@tokenizer/token@^0.1.0", "@tokenizer/token@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.1.1.tgz#f0d92c12f87079ddfd1b29f614758b9696bc29e3" + integrity sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" @@ -3997,6 +4002,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-env@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9" + integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw== + dependencies: + cross-spawn "^7.0.1" + cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -4016,7 +4028,7 @@ cross-spawn@^3.0.0: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -5189,6 +5201,16 @@ file-loader@^6.0.0: loader-utils "^2.0.0" schema-utils "^2.6.5" +file-type@^14.7.1: + version "14.7.1" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-14.7.1.tgz#f748732b3e70478bff530e1cf0ec2fe33608b1bb" + integrity sha512-sXAMgFk67fQLcetXustxfKX+PZgHIUFn96Xld9uH8aXPdX3xOp0/jg9OdouVTvQrf7mrn+wAa4jN/y9fUOOiRA== + dependencies: + readable-web-to-node-stream "^2.0.0" + strtok3 "^6.0.3" + token-types "^2.0.0" + typedarray-to-buffer "^3.1.5" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -6067,7 +6089,7 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== @@ -7625,6 +7647,14 @@ make-plural@^6.2.1: resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-6.2.1.tgz#2790af1d05fb2fc35a111ce759ffdb0aca1339a3" integrity sha512-AmkruwJ9EjvyTv6AM8MBMK3TAeOJvhgTv5YQXzF0EP2qawhpvMjDpHvsdOIIT0Vn+BB0+IogmYZ1z+Ulm/m0Fg== +make-synchronous@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/make-synchronous/-/make-synchronous-0.1.1.tgz#0169f6ec769c3cf8948d66790da262740c1209e7" + integrity sha512-Y4SxxqhaoyMDokJQ0AZz0E+bLhRkOSR7Z/IQoTKPdS6HYi3aobal2kMHoHHoqBadPWjf07P4K1FQLXOx3wf9Yw== + dependencies: + subsume "^3.0.0" + type-fest "^0.16.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -8936,6 +8966,11 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +peek-readable@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-3.1.0.tgz#250b08b7de09db8573d7fd8ea475215bbff14348" + integrity sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA== + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -9529,6 +9564,11 @@ readable-stream@^3.1.1, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-web-to-node-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-2.0.0.tgz#751e632f466552ac0d5c440cc01470352f93c4b7" + integrity sha512-+oZJurc4hXpaaqsN68GoZGQAQIA3qr09Or4fqEsargABnbe5Aau8hFn6ISVleT3cpY/0n/8drn7huyyEvTbghA== + readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -10648,6 +10688,15 @@ strip-outer@^1.0.1: dependencies: escape-string-regexp "^1.0.2" +strtok3@^6.0.3: + version "6.0.4" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.0.4.tgz#ede0d20fde5aa9fda56417c3558eaafccc724694" + integrity sha512-rqWMKwsbN9APU47bQTMEYTPcwdpKDtmf1jVhHzNW2cL1WqAxaM9iBb9t5P2fj+RV2YsErUWgQzHD5JwV0uCTEQ== + dependencies: + "@tokenizer/token" "^0.1.1" + "@types/debug" "^4.1.5" + peek-readable "^3.1.0" + style-loader@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" @@ -10656,6 +10705,14 @@ style-loader@^1.2.1: loader-utils "^2.0.0" schema-utils "^2.6.6" +subsume@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/subsume/-/subsume-3.0.0.tgz#22c92730f441ad72ee9af4bdad42dc4ff830cfaf" + integrity sha512-6n/UfV8UWKwJNO8OAOiKntwEMihuBeeoJfzpL542C+OuvT4iWG9SwjrXkOmsxjb4SteHUsos9SvrdqZ9+ICwTQ== + dependencies: + escape-string-regexp "^2.0.0" + unique-string "^2.0.0" + sumchecker@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" @@ -10977,6 +11034,14 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +token-types@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-2.0.0.tgz#b23618af744818299c6fbf125e0fdad98bab7e85" + integrity sha512-WWvu8sGK8/ZmGusekZJJ5NM6rRVTTDO7/bahz4NGiSDb/XsmdYBn6a1N/bymUHuWYTWeuLUg98wUzvE4jPdCZw== + dependencies: + "@tokenizer/token" "^0.1.0" + ieee754 "^1.1.13" + touch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" @@ -11150,6 +11215,11 @@ type-fest@^0.13.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"