From 688336647d04b77760a250c2fee84bfed36e78af Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Wed, 22 Feb 2023 18:58:05 +0200 Subject: [PATCH 01/21] Extract injection token for application information to separate NPM package (#7211) * Introduce dummy package for application Signed-off-by: Janne Savolainen * Introduce application information token in application Feature Signed-off-by: Janne Savolainen * Switch to using applicationInformationToken from application feature Signed-off-by: Janne Savolainen --------- Signed-off-by: Janne Savolainen --- package-lock.json | 19 +++++++- packages/core/package.json | 1 + .../welcome-route-config.injectable.ts | 4 +- packages/core/src/common/library.ts | 2 - .../vars/application-copyright.injectable.ts | 2 +- .../application-description.injectable.ts | 2 +- ...application-information-fake-injectable.ts | 30 +++++++++++++ .../application-information-injectable.ts | 20 --------- .../vars/application-information-token.ts | 15 ------- ...ormation.global-override-for-injectable.ts | 24 ---------- .../bundled-kubectl-version.injectable.ts | 4 +- .../content-security-policy.injectable.ts | 4 +- .../common/vars/product-name.injectable.ts | 2 +- .../common/vars/sentry-dsn-url.injectable.ts | 4 +- .../store-migration-version.injectable.ts | 2 +- .../publish-is-configured.injectable.ts | 4 +- .../store-migrations/snap.injectable.ts | 2 +- packages/core/src/main/getDi.ts | 12 +---- packages/core/src/main/getDiForUnitTesting.ts | 7 +-- .../create-electron-window.injectable.ts | 2 +- .../migrations/currentVersion.injectable.ts | 2 +- packages/core/src/renderer/getDi.tsx | 12 +---- .../core/src/renderer/getDiForUnitTesting.tsx | 7 +-- packages/open-lens/package.json | 1 + .../application-information.injectable.ts | 44 ++++++++++++++++--- .../technical-features/application/README.md | 18 ++++++++ .../technical-features/application/index.ts | 3 ++ .../application/jest.config.js | 2 + .../application/package.json | 34 ++++++++++++++ .../src/application-information-token.ts | 24 ++++++++++ .../application/tsconfig.json | 3 ++ .../application/webpack.config.js | 1 + 32 files changed, 201 insertions(+), 112 deletions(-) create mode 100644 packages/core/src/common/vars/application-information-fake-injectable.ts delete mode 100644 packages/core/src/common/vars/application-information-injectable.ts delete mode 100644 packages/core/src/common/vars/application-information-token.ts delete mode 100644 packages/core/src/common/vars/application-information.global-override-for-injectable.ts create mode 100644 packages/technical-features/application/README.md create mode 100644 packages/technical-features/application/index.ts create mode 100644 packages/technical-features/application/jest.config.js create mode 100644 packages/technical-features/application/package.json create mode 100644 packages/technical-features/application/src/application-information-token.ts create mode 100644 packages/technical-features/application/tsconfig.json create mode 100644 packages/technical-features/application/webpack.config.js diff --git a/package-lock.json b/package-lock.json index ea90dd7e37..f5e29555c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3281,6 +3281,10 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@k8slens/application": { + "resolved": "packages/technical-features/application", + "link": true + }, "node_modules/@k8slens/bump-version-for-cron": { "resolved": "packages/bump-version-for-cron", "link": true @@ -32254,7 +32258,7 @@ "dependencies": { "@astronautlabs/jsonpath": "^1.1.0", "@hapi/call": "^9.0.1", - "@hapi/subtext": "^7.0.4", + "@hapi/subtext": "^7.1.0", "@k8slens/node-fetch": "^6.4.0-beta.13", "@kubernetes/client-node": "^0.18.1", "@material-ui/styles": "^4.11.5", @@ -32461,6 +32465,7 @@ "node": ">=16 <17" }, "peerDependencies": { + "@k8slens/application": "^6.4.0-beta.13", "@types/byline": "^4.2.33", "@types/chart.js": "^2.9.36", "@types/color": "^3.0.3", @@ -34095,6 +34100,7 @@ } }, "packages/infrastructure/webpack": { + "name": "@k8slens/webpack", "version": "0.0.1", "license": "MIT", "dependencies": { @@ -34291,6 +34297,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@k8slens/application": "^6.4.0-beta.13", "@k8slens/core": "^6.4.0-beta.13", "@k8slens/ensure-binaries": "^6.4.0-beta.13", "@k8slens/generate-tray-icons": "^6.4.0-beta.13", @@ -34534,6 +34541,16 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", "dev": true + }, + "packages/technical-features/application": { + "name": "@k8slens/application", + "version": "6.4.0-beta.13", + "license": "MIT", + "peerDependencies": { + "@ogre-tools/fp": "^12.0.1", + "@ogre-tools/injectable": "^12.0.1", + "lodash": "^4.17.15" + } } } } diff --git a/packages/core/package.json b/packages/core/package.json index 4e9ae7c9bf..f6d58909aa 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -330,6 +330,7 @@ "xterm-addon-fit": "^0.5.0" }, "peerDependencies": { + "@k8slens/application": "^6.4.0-beta.13", "@types/byline": "^4.2.33", "@types/chart.js": "^2.9.36", "@types/color": "^3.0.3", diff --git a/packages/core/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts b/packages/core/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts index d7aadf2f91..9883c1476b 100644 --- a/packages/core/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts @@ -2,13 +2,13 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "../../../vars/application-information-token"; const welcomeRouteConfigInjectable = getInjectable({ id: "welcome-route-config", - instantiate: (di) => di.inject(applicationInformationToken).config.welcomeRoute, + instantiate: (di) => di.inject(applicationInformationToken).welcomeRoute, }); export default welcomeRouteConfigInjectable; diff --git a/packages/core/src/common/library.ts b/packages/core/src/common/library.ts index f4aee75513..bc625eb3dd 100644 --- a/packages/core/src/common/library.ts +++ b/packages/core/src/common/library.ts @@ -4,6 +4,4 @@ */ // @experimental -export { applicationInformationToken } from "./vars/application-information-token"; -export type { ApplicationInformation } from "./vars/application-information-token"; export { bundledExtensionInjectionToken } from "../extensions/extension-discovery/bundled-extension-token"; diff --git a/packages/core/src/common/vars/application-copyright.injectable.ts b/packages/core/src/common/vars/application-copyright.injectable.ts index 0630c98cb6..440a20b5d1 100644 --- a/packages/core/src/common/vars/application-copyright.injectable.ts +++ b/packages/core/src/common/vars/application-copyright.injectable.ts @@ -2,8 +2,8 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "./application-information-token"; const applicationCopyrightInjectable = getInjectable({ id: "application-copyright", diff --git a/packages/core/src/common/vars/application-description.injectable.ts b/packages/core/src/common/vars/application-description.injectable.ts index 8dabb8f629..2b562df7a1 100644 --- a/packages/core/src/common/vars/application-description.injectable.ts +++ b/packages/core/src/common/vars/application-description.injectable.ts @@ -2,8 +2,8 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "./application-information-token"; const applicationDescriptionInjectable = getInjectable({ id: "application-description", diff --git a/packages/core/src/common/vars/application-information-fake-injectable.ts b/packages/core/src/common/vars/application-information-fake-injectable.ts new file mode 100644 index 0000000000..c881c4c8ca --- /dev/null +++ b/packages/core/src/common/vars/application-information-fake-injectable.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectable } from "@ogre-tools/injectable"; +import { applicationInformationToken } from "@k8slens/application"; + +export const applicationInformationFakeInjectable = getInjectable({ + id: "application-information-fake", + + instantiate: () => ({ + name: "some-product-name", + productName: "some-product-name", + version: "6.0.0", + updatingIsEnabled: false, + k8sProxyVersion: "0.2.1", + bundledKubectlVersion: "1.23.3", + bundledHelmVersion: "3.7.2", + sentryDsn: "", + contentSecurityPolicy: + "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:", + + welcomeRoute: "/welcome", + copyright: "some-copyright-information", + description: "some-descriptive-text", + }), + + injectionToken: applicationInformationToken, +}); diff --git a/packages/core/src/common/vars/application-information-injectable.ts b/packages/core/src/common/vars/application-information-injectable.ts deleted file mode 100644 index 8530257a3a..0000000000 --- a/packages/core/src/common/vars/application-information-injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import packageJson from "../../../package.json"; -import { applicationInformationToken } from "../../common/vars/application-information-token"; - -const applicationInformationInjectable = getInjectable({ - id: "application-information", - injectionToken: applicationInformationToken, - instantiate: () => { - const { version, config, productName, build, copyright, description, name } = packageJson; - - return { version, config, productName, build, copyright, description, name }; - }, - causesSideEffects: true, -}); - -export default applicationInformationInjectable; diff --git a/packages/core/src/common/vars/application-information-token.ts b/packages/core/src/common/vars/application-information-token.ts deleted file mode 100644 index 67b187478a..0000000000 --- a/packages/core/src/common/vars/application-information-token.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type packageJson from "../../../package.json"; - -export type ApplicationInformation = Pick & { - build: Partial & { publish?: unknown[] }; -}; - -export const applicationInformationToken = getInjectionToken({ - id: "application-information-token", -}); diff --git a/packages/core/src/common/vars/application-information.global-override-for-injectable.ts b/packages/core/src/common/vars/application-information.global-override-for-injectable.ts deleted file mode 100644 index e0b886fa65..0000000000 --- a/packages/core/src/common/vars/application-information.global-override-for-injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import applicationInformationInjectable from "./application-information-injectable"; - -export default getGlobalOverride(applicationInformationInjectable, () => ({ - name: "some-product-name", - productName: "some-product-name", - version: "6.0.0", - build: {} as any, - config: { - k8sProxyVersion: "0.2.1", - bundledKubectlVersion: "1.23.3", - bundledHelmVersion: "3.7.2", - sentryDsn: "", - contentSecurityPolicy: "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:", - welcomeRoute: "/welcome", - }, - copyright: "some-copyright-information", - description: "some-descriptive-text", -})); diff --git a/packages/core/src/common/vars/bundled-kubectl-version.injectable.ts b/packages/core/src/common/vars/bundled-kubectl-version.injectable.ts index c15457a2c0..bf3228e44f 100644 --- a/packages/core/src/common/vars/bundled-kubectl-version.injectable.ts +++ b/packages/core/src/common/vars/bundled-kubectl-version.injectable.ts @@ -2,12 +2,12 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "./application-information-token"; const bundledKubectlVersionInjectable = getInjectable({ id: "bundled-kubectl-version", - instantiate: (di) => di.inject(applicationInformationToken).config.bundledKubectlVersion, + instantiate: (di) => di.inject(applicationInformationToken).bundledKubectlVersion, }); export default bundledKubectlVersionInjectable; diff --git a/packages/core/src/common/vars/content-security-policy.injectable.ts b/packages/core/src/common/vars/content-security-policy.injectable.ts index b516571d2f..56d70e3e97 100644 --- a/packages/core/src/common/vars/content-security-policy.injectable.ts +++ b/packages/core/src/common/vars/content-security-policy.injectable.ts @@ -2,12 +2,12 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "./application-information-token"; const contentSecurityPolicyInjectable = getInjectable({ id: "content-security-policy", - instantiate: (di) => di.inject(applicationInformationToken).config.contentSecurityPolicy, + instantiate: (di) => di.inject(applicationInformationToken).contentSecurityPolicy, }); export default contentSecurityPolicyInjectable; diff --git a/packages/core/src/common/vars/product-name.injectable.ts b/packages/core/src/common/vars/product-name.injectable.ts index aa156e427f..6437af3c6c 100644 --- a/packages/core/src/common/vars/product-name.injectable.ts +++ b/packages/core/src/common/vars/product-name.injectable.ts @@ -2,8 +2,8 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "./application-information-token"; const productNameInjectable = getInjectable({ id: "product-name", diff --git a/packages/core/src/common/vars/sentry-dsn-url.injectable.ts b/packages/core/src/common/vars/sentry-dsn-url.injectable.ts index 1fa48b95f1..0ca6cab9e5 100644 --- a/packages/core/src/common/vars/sentry-dsn-url.injectable.ts +++ b/packages/core/src/common/vars/sentry-dsn-url.injectable.ts @@ -2,12 +2,12 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "./application-information-token"; const sentryDataSourceNameInjectable = getInjectable({ id: "sentry-data-source-name", - instantiate: (di) => di.inject(applicationInformationToken).config.sentryDsn, + instantiate: (di) => di.inject(applicationInformationToken).sentryDsn, }); export default sentryDataSourceNameInjectable; diff --git a/packages/core/src/common/vars/store-migration-version.injectable.ts b/packages/core/src/common/vars/store-migration-version.injectable.ts index 4a4b232da8..6dfbff2e94 100644 --- a/packages/core/src/common/vars/store-migration-version.injectable.ts +++ b/packages/core/src/common/vars/store-migration-version.injectable.ts @@ -2,8 +2,8 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "./application-information-token"; const storeMigrationVersionInjectable = getInjectable({ id: "store-migration-version", diff --git a/packages/core/src/features/application-update/main/updating-is-enabled/publish-is-configured/publish-is-configured.injectable.ts b/packages/core/src/features/application-update/main/updating-is-enabled/publish-is-configured/publish-is-configured.injectable.ts index 8581cb4860..de451ad5aa 100644 --- a/packages/core/src/features/application-update/main/updating-is-enabled/publish-is-configured/publish-is-configured.injectable.ts +++ b/packages/core/src/features/application-update/main/updating-is-enabled/publish-is-configured/publish-is-configured.injectable.ts @@ -2,12 +2,12 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "../../../../../common/vars/application-information-token"; const publishIsConfiguredInjectable = getInjectable({ id: "publish-is-configured", - instantiate: (di) => Boolean(di.inject(applicationInformationToken).build.publish?.length), + instantiate: (di) => Boolean(di.inject(applicationInformationToken).updatingIsEnabled), }); export default publishIsConfiguredInjectable; diff --git a/packages/core/src/main/cluster/store-migrations/snap.injectable.ts b/packages/core/src/main/cluster/store-migrations/snap.injectable.ts index e09874c0e5..d4efc37612 100644 --- a/packages/core/src/main/cluster/store-migrations/snap.injectable.ts +++ b/packages/core/src/main/cluster/store-migrations/snap.injectable.ts @@ -6,12 +6,12 @@ // Fix embedded kubeconfig paths under snap config import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken } from "../../../common/vars/application-information-token"; import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token"; import loggerInjectable from "../../../common/logger.injectable"; import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable"; import type { ClusterModel } from "../../../common/cluster-types"; import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable"; +import { applicationInformationToken } from "@k8slens/application"; const clusterStoreSnapMigrationInjectable = getInjectable({ id: "cluster-store-snap-migration", diff --git a/packages/core/src/main/getDi.ts b/packages/core/src/main/getDi.ts index db7decece8..e325e66ed5 100644 --- a/packages/core/src/main/getDi.ts +++ b/packages/core/src/main/getDi.ts @@ -3,15 +3,5 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { createContainer } from "@ogre-tools/injectable"; -import { runInAction } from "mobx"; -import applicationInformationInjectable from "../common/vars/application-information-injectable"; -export const getDi = () => { - const di = createContainer("main"); - - runInAction(() => { - di.register(applicationInformationInjectable); - }); - - return di; -}; +export const getDi = () => createContainer("main"); diff --git a/packages/core/src/main/getDiForUnitTesting.ts b/packages/core/src/main/getDiForUnitTesting.ts index 1698997e36..5e923433fa 100644 --- a/packages/core/src/main/getDiForUnitTesting.ts +++ b/packages/core/src/main/getDiForUnitTesting.ts @@ -29,9 +29,9 @@ import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import electronInjectable from "./utils/resolve-system-proxy/electron.injectable"; import initializeClusterManagerInjectable from "./cluster/initialize-manager.injectable"; import type { GlobalOverride } from "../common/test-utils/get-global-override"; -import applicationInformationInjectable from "../common/vars/application-information-injectable"; import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; import { getOverrideFsWithFakes } from "../test-utils/override-fs-with-fakes"; +import { applicationInformationFakeInjectable } from "../common/vars/application-information-fake-injectable"; export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) { const { @@ -57,9 +57,10 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) .filter(isInjectable) ) as Injectable[]; + registerMobX(di); + runInAction(() => { - registerMobX(di); - di.register(applicationInformationInjectable); + di.register(applicationInformationFakeInjectable); chunk(100)(injectables).forEach(chunkInjectables => { di.register(...chunkInjectables); diff --git a/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts b/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts index 0f8c84668b..b53f58c838 100644 --- a/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts +++ b/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts @@ -13,9 +13,9 @@ import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-b import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable"; import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable"; import isLinuxInjectable from "../../../../common/vars/is-linux.injectable"; -import { applicationInformationToken } from "../../../../common/vars/application-information-token"; import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable"; import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable"; +import { applicationInformationToken } from "@k8slens/application"; export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover"; diff --git a/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts b/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts index a424743697..2e26f4996b 100644 --- a/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts +++ b/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts @@ -7,8 +7,8 @@ import { docsUrl, slackUrl } from "../../../common/vars"; import type { WeblinkData } from "../../../common/weblinks-store/weblink-store"; import { getInjectable } from "@ogre-tools/injectable"; import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token"; -import { applicationInformationToken } from "../../../common/vars/application-information-token"; import { lensDocumentationWeblinkId, lensSlackWeblinkId } from "../links"; +import { applicationInformationToken } from "@k8slens/application"; const currentVersionWeblinkStoreMigrationInjectable = getInjectable({ id: "current-version-weblink-store-migration", diff --git a/packages/core/src/renderer/getDi.tsx b/packages/core/src/renderer/getDi.tsx index de61d5ab67..77d665760c 100644 --- a/packages/core/src/renderer/getDi.tsx +++ b/packages/core/src/renderer/getDi.tsx @@ -4,15 +4,5 @@ */ import { createContainer } from "@ogre-tools/injectable"; -import { runInAction } from "mobx"; -import applicationInformationInjectable from "../common/vars/application-information-injectable"; -export const getDi = () => { - const di = createContainer("renderer"); - - runInAction(() => { - di.register(applicationInformationInjectable); - }); - - return di; -}; +export const getDi = () => createContainer("renderer"); diff --git a/packages/core/src/renderer/getDiForUnitTesting.tsx b/packages/core/src/renderer/getDiForUnitTesting.tsx index ac42796d9b..07cc70ef80 100644 --- a/packages/core/src/renderer/getDiForUnitTesting.tsx +++ b/packages/core/src/renderer/getDiForUnitTesting.tsx @@ -18,8 +18,8 @@ import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import watchHistoryStateInjectable from "./remote-helpers/watch-history-state.injectable"; import legacyOnChannelListenInjectable from "./ipc/legacy-channel-listen.injectable"; import type { GlobalOverride } from "../common/test-utils/get-global-override"; -import applicationInformationInjectable from "../common/vars/application-information-injectable"; import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; +import { applicationInformationFakeInjectable } from "../common/vars/application-information-fake-injectable"; export const getDiForUnitTesting = ( opts: { doGeneralOverrides?: boolean } = {}, @@ -45,9 +45,10 @@ export const getDiForUnitTesting = ( .filter(isInjectable) ) as Injectable[]; + registerMobX(di); + runInAction(() => { - registerMobX(di); - di.register(applicationInformationInjectable); + di.register(applicationInformationFakeInjectable); chunk(100)(injectables).forEach((chunkInjectables) => { di.register(...chunkInjectables); diff --git a/packages/open-lens/package.json b/packages/open-lens/package.json index 37ec8cb07b..8507be2395 100644 --- a/packages/open-lens/package.json +++ b/packages/open-lens/package.json @@ -195,6 +195,7 @@ } }, "dependencies": { + "@k8slens/application": "^6.4.0-beta.13", "@k8slens/core": "^6.4.0-beta.13", "@k8slens/ensure-binaries": "^6.4.0-beta.13", "@k8slens/generate-tray-icons": "^6.4.0-beta.13", diff --git a/packages/open-lens/src/common/application-information.injectable.ts b/packages/open-lens/src/common/application-information.injectable.ts index 603ea171ad..49e95f4bdc 100644 --- a/packages/open-lens/src/common/application-information.injectable.ts +++ b/packages/open-lens/src/common/application-information.injectable.ts @@ -1,15 +1,49 @@ - +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ import { getInjectable } from "@ogre-tools/injectable"; -import { applicationInformationToken, ApplicationInformation } from "@k8slens/core/common"; import packageJson from "../../package.json"; +import { applicationInformationToken } from "@k8slens/application"; const applicationInformationInjectable = getInjectable({ id: "application-information", injectionToken: applicationInformationToken, - instantiate: () => { - const { version, config, productName, build, copyright, description, name } = packageJson; - return { version, config, productName, build, copyright, description, name } as ApplicationInformation; + instantiate: () => { + const { + version, + + config: { + bundledHelmVersion, + bundledKubectlVersion, + contentSecurityPolicy, + k8sProxyVersion, + sentryDsn, + welcomeRoute, + }, + + productName, + build, + copyright, + description, + name, + } = packageJson; + + return { + version, + productName, + copyright, + description, + name, + k8sProxyVersion, + bundledKubectlVersion, + bundledHelmVersion, + sentryDsn, + contentSecurityPolicy, + welcomeRoute, + updatingIsEnabled: (build as any)?.publish?.length > 0, + }; }, causesSideEffects: true, }); diff --git a/packages/technical-features/application/README.md b/packages/technical-features/application/README.md new file mode 100644 index 0000000000..451e211c85 --- /dev/null +++ b/packages/technical-features/application/README.md @@ -0,0 +1,18 @@ +# @k8slens/application + +This package contains stuff related to creating Lens-applications. + +In the beginning it will contain just the injection tokens used to configure the application. + +## Install +```bash +$ npm install @k8slens/application +``` + + +## Usage + +As of now, this package doesn't do anything alone. It just provides you way to register implementation for contract. + +Future ambition is that all stuff related to how applications are built will be relocated here. + diff --git a/packages/technical-features/application/index.ts b/packages/technical-features/application/index.ts new file mode 100644 index 0000000000..8686f83dd1 --- /dev/null +++ b/packages/technical-features/application/index.ts @@ -0,0 +1,3 @@ +export { applicationInformationToken } from "./src/application-information-token"; +export type { ApplicationInformation } from "./src/application-information-token"; + diff --git a/packages/technical-features/application/jest.config.js b/packages/technical-features/application/jest.config.js new file mode 100644 index 0000000000..23be80353b --- /dev/null +++ b/packages/technical-features/application/jest.config.js @@ -0,0 +1,2 @@ +module.exports = + require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact; diff --git a/packages/technical-features/application/package.json b/packages/technical-features/application/package.json new file mode 100644 index 0000000000..e5d08f6c49 --- /dev/null +++ b/packages/technical-features/application/package.json @@ -0,0 +1,34 @@ +{ + "name": "@k8slens/application", + "private": false, + "version": "6.4.0-beta.13", + "description": "Package for creating Lens applications", + "type": "commonjs", + "files": [ + "dist" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/lensapp/monorepo.git" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "author": { + "name": "OpenLens Authors", + "email": "info@k8slens.dev" + }, + "license": "MIT", + "homepage": "https://github.com/lensapp/lens", + "scripts": { + "build": "lens-build", + "build:remove": "lens-remove-build", + "code-style:fix": "lens-fix-code-style", + "code-style:verify": "lens-verify-code-style", + "test": "lens-test" + }, + "peerDependencies": { + "@ogre-tools/fp": "^12.0.1", + "@ogre-tools/injectable": "^12.0.1", + "lodash": "^4.17.15" + } +} diff --git a/packages/technical-features/application/src/application-information-token.ts b/packages/technical-features/application/src/application-information-token.ts new file mode 100644 index 0000000000..2f724044c1 --- /dev/null +++ b/packages/technical-features/application/src/application-information-token.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectionToken } from "@ogre-tools/injectable"; + +export type ApplicationInformation = { + name: string; + version: string; + productName: string; + copyright: string; + description: string; + k8sProxyVersion: string, + bundledKubectlVersion: string, + bundledHelmVersion: string, + sentryDsn: string, + contentSecurityPolicy: string, + welcomeRoute: string, + updatingIsEnabled: boolean; +} + +export const applicationInformationToken = getInjectionToken({ + id: "application-information-token", +}); diff --git a/packages/technical-features/application/tsconfig.json b/packages/technical-features/application/tsconfig.json new file mode 100644 index 0000000000..a4f6fa613e --- /dev/null +++ b/packages/technical-features/application/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@k8slens/typescript/config/base.json" +} diff --git a/packages/technical-features/application/webpack.config.js b/packages/technical-features/application/webpack.config.js new file mode 100644 index 0000000000..3183f30179 --- /dev/null +++ b/packages/technical-features/application/webpack.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/webpack").configForNode; From f212c90b1ab1a30eef244283bda19ba4e067fe8f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 22 Feb 2023 09:37:53 -0800 Subject: [PATCH 02/21] Remove notarize as unused (#7215) * Remove notarize as unused Signed-off-by: Sebastian Malton * Remove electron-notarize Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- package-lock.json | 1 - packages/core/build/notarize.js | 27 --------------------------- packages/core/package.json | 1 - 3 files changed, 29 deletions(-) delete mode 100644 packages/core/build/notarize.js diff --git a/package-lock.json b/package-lock.json index f5e29555c3..6cea9cb6b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32400,7 +32400,6 @@ "dompurify": "^2.4.4", "electron": "^19.1.9", "electron-builder": "^23.6.0", - "electron-notarize": "^0.3.0", "esbuild": "^0.17.8", "esbuild-loader": "^2.21.0", "eslint": "^8.33.0", diff --git a/packages/core/build/notarize.js b/packages/core/build/notarize.js deleted file mode 100644 index 0bf1903e59..0000000000 --- a/packages/core/build/notarize.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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, - }); -}; diff --git a/packages/core/package.json b/packages/core/package.json index f6d58909aa..476100d9bf 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -268,7 +268,6 @@ "dompurify": "^2.4.4", "electron": "^19.1.9", "electron-builder": "^23.6.0", - "electron-notarize": "^0.3.0", "esbuild": "^0.17.8", "esbuild-loader": "^2.21.0", "eslint": "^8.33.0", From a7181047d5b241ac6c41d056289ab9419e769360 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Thu, 23 Feb 2023 10:54:35 +0200 Subject: [PATCH 03/21] Fix windows not being able to build new Feature packages (#7220) * Add missing dev script Signed-off-by: Janne Savolainen * Inline all shared scripts to make them work in all environments Signed-off-by: Janne Savolainen * Switch to using deterministic fake for path.join in unit test Signed-off-by: Janne Savolainen * Make multi export config for webpack not fail when used in windows Signed-off-by: Janne Savolainen --------- Signed-off-by: Janne Savolainen --- package-lock.json | 7 ------- packages/infrastructure/jest/package.json | 3 --- packages/infrastructure/webpack/bin/build.sh | 2 -- packages/infrastructure/webpack/bin/remove-build.sh | 1 - packages/infrastructure/webpack/package.json | 6 +----- .../webpack/src/get-multi-export-config.js | 10 ++++++---- .../webpack/src/get-multi-export-config.test.js | 10 ++++++++++ packages/technical-features/application/package.json | 8 +++----- 8 files changed, 20 insertions(+), 27 deletions(-) delete mode 100755 packages/infrastructure/webpack/bin/build.sh delete mode 100755 packages/infrastructure/webpack/bin/remove-build.sh diff --git a/package-lock.json b/package-lock.json index 6cea9cb6b0..24cae086b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32832,9 +32832,6 @@ "jest-watch-typeahead": "^2.2.1", "lodash": "^4.17.21", "ts-jest": "^29.0.3" - }, - "bin": { - "lens-test": "bin/test.sh" } }, "packages/infrastructure/jest/node_modules/@jest/console": { @@ -34112,10 +34109,6 @@ "webpack": "^5.75.0", "webpack-cli": "^4.10.0", "webpack-node-externals": "^3.0.0" - }, - "bin": { - "lens-build": "bin/build.sh", - "lens-remove-build": "bin/remove-build.sh" } }, "packages/infrastructure/webpack/node_modules/sass-loader": { diff --git a/packages/infrastructure/jest/package.json b/packages/infrastructure/jest/package.json index b0359441dd..b6e00d1369 100644 --- a/packages/infrastructure/jest/package.json +++ b/packages/infrastructure/jest/package.json @@ -15,9 +15,6 @@ }, "license": "MIT", "homepage": "https://github.com/lensapp/lens", - "bin": { - "lens-test": "bin/test.sh" - }, "dependencies": { "@swc/core": "^1.3.20", "@swc/jest": "^0.2.23", diff --git a/packages/infrastructure/webpack/bin/build.sh b/packages/infrastructure/webpack/bin/build.sh deleted file mode 100755 index 589acdeab2..0000000000 --- a/packages/infrastructure/webpack/bin/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -set -e -webpack $@ diff --git a/packages/infrastructure/webpack/bin/remove-build.sh b/packages/infrastructure/webpack/bin/remove-build.sh deleted file mode 100755 index f2421c500d..0000000000 --- a/packages/infrastructure/webpack/bin/remove-build.sh +++ /dev/null @@ -1 +0,0 @@ -rm -rfv build diff --git a/packages/infrastructure/webpack/package.json b/packages/infrastructure/webpack/package.json index 35df41519c..2539a99bb8 100644 --- a/packages/infrastructure/webpack/package.json +++ b/packages/infrastructure/webpack/package.json @@ -15,12 +15,8 @@ }, "license": "MIT", "homepage": "https://github.com/lensapp/lens", - "bin": { - "lens-build": "bin/build.sh", - "lens-remove-build": "bin/remove-build.sh" - }, "scripts": { - "test:unit": "lens-test" + "test:unit": "jest --coverage --runInBand" }, "dependencies": { "@types/webpack-env": "^1.18.0", diff --git a/packages/infrastructure/webpack/src/get-multi-export-config.js b/packages/infrastructure/webpack/src/get-multi-export-config.js index 02944c25d3..338b3d89d7 100644 --- a/packages/infrastructure/webpack/src/get-multi-export-config.js +++ b/packages/infrastructure/webpack/src/get-multi-export-config.js @@ -14,7 +14,7 @@ const { } = require("lodash/fp"); const { pipeline } = require("@ogre-tools/fp"); -module.exports = (packageJson, dependencies = { nodeConfig, reactConfig }) => { +module.exports = (packageJson, dependencies = { nodeConfig, reactConfig, joinPath: path.join }) => { if (!packageJson.lensMultiExportConfig) { throw new Error( `Tried to get multi export config for package "${packageJson.name}" but configuration is missing.` @@ -80,7 +80,9 @@ module.exports = (packageJson, dependencies = { nodeConfig, reactConfig }) => { }; const toExpectedExport = (externalImportPath) => { - const entrypointPath = `./${path.join( + const posixJoinForPackageJson = path.posix.join; + + const entrypointPath = `./${posixJoinForPackageJson( "./dist", externalImportPath, "index.js" @@ -89,7 +91,7 @@ const toExpectedExport = (externalImportPath) => { return [ externalImportPath, { - types: `./${path.join("./dist", externalImportPath, "index.d.ts")}`, + types: `./${posixJoinForPackageJson("./dist", externalImportPath, "index.d.ts")}`, default: entrypointPath, import: entrypointPath, @@ -114,7 +116,7 @@ const toExportSpecificWebpackConfigFor = output: { ...baseConfig.output, - path: path.join(baseConfig.output.path, externalImportPath), + path: dependencies.joinPath(baseConfig.output.path, externalImportPath), }, }; }; diff --git a/packages/infrastructure/webpack/src/get-multi-export-config.test.js b/packages/infrastructure/webpack/src/get-multi-export-config.test.js index 6cad83919f..b0ce1c2231 100644 --- a/packages/infrastructure/webpack/src/get-multi-export-config.test.js +++ b/packages/infrastructure/webpack/src/get-multi-export-config.test.js @@ -1,4 +1,7 @@ import getMultiExportConfig from "./get-multi-export-config"; +import path from 'path'; + +const joinPathFake = path.posix.join; describe("get-multi-export-config", () => { let actual; @@ -52,6 +55,7 @@ describe("get-multi-export-config", () => { actual = getMultiExportConfig(maximalPackageJson, { nodeConfig: nodeConfigStub, reactConfig: reactConfigStub, + joinPath: joinPathFake, }); expect(actual).toEqual([ @@ -93,6 +97,7 @@ describe("get-multi-export-config", () => { getMultiExportConfig(maximalPackageJson, { nodeConfig: nodeConfigStub, reactConfig: reactConfigStub, + joinPath: joinPathFake, }); }).toThrow( 'Tried to get multi export config but exports of package.json for "some-name" did not match exactly:' @@ -106,6 +111,7 @@ describe("get-multi-export-config", () => { getMultiExportConfig(maximalPackageJson, { nodeConfig: nodeConfigStub, reactConfig: reactConfigStub, + joinPath: joinPathFake, }); }).toThrow( 'Tried to get multi export config but exports of package.json for "some-name" did not match exactly:' @@ -119,6 +125,7 @@ describe("get-multi-export-config", () => { getMultiExportConfig(maximalPackageJson, { nodeConfig: nodeConfigStub, reactConfig: reactConfigStub, + joinPath: joinPathFake, }); }).toThrow( 'Tried to get multi export config but exports of package.json for "some-name" did not match exactly:' @@ -132,6 +139,7 @@ describe("get-multi-export-config", () => { getMultiExportConfig(maximalPackageJson, { nodeConfig: nodeConfigStub, reactConfig: reactConfigStub, + joinPath: joinPathFake, }); }).toThrow( 'Tried to get multi export config for package "some-name" but configuration is missing.' @@ -145,6 +153,7 @@ describe("get-multi-export-config", () => { getMultiExportConfig(maximalPackageJson, { nodeConfig: nodeConfigStub, reactConfig: reactConfigStub, + joinPath: joinPathFake, }); }).toThrow( 'Tried to get multi export config for package "some-name" but build types "some-invalid" were not any of "node", "react".' @@ -159,6 +168,7 @@ describe("get-multi-export-config", () => { getMultiExportConfig(maximalPackageJson, { nodeConfig: nodeConfigStub, reactConfig: reactConfigStub, + joinPath: joinPathFake, }); }).toThrow( 'Tried to get multi export config for package "some-name" but entrypoint was missing for "./some-entrypoint".' diff --git a/packages/technical-features/application/package.json b/packages/technical-features/application/package.json index e5d08f6c49..91f1a6be34 100644 --- a/packages/technical-features/application/package.json +++ b/packages/technical-features/application/package.json @@ -20,11 +20,9 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { - "build": "lens-build", - "build:remove": "lens-remove-build", - "code-style:fix": "lens-fix-code-style", - "code-style:verify": "lens-verify-code-style", - "test": "lens-test" + "build": "webpack", + "dev": "webpack --mode=development --watch", + "test": "jest --coverage --runInBand" }, "peerDependencies": { "@ogre-tools/fp": "^12.0.1", From 898b9039dac07341b42f69df3bc91c47c45c8df8 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Thu, 23 Feb 2023 13:41:19 +0200 Subject: [PATCH 04/21] Add publish configuration to packages that are public (#7221) Signed-off-by: Janne Savolainen --- packages/infrastructure/jest/package.json | 4 ++++ packages/infrastructure/typescript/package.json | 4 ++++ packages/infrastructure/webpack/package.json | 4 ++++ packages/technical-features/application/package.json | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/packages/infrastructure/jest/package.json b/packages/infrastructure/jest/package.json index b6e00d1369..6f5ff7c642 100644 --- a/packages/infrastructure/jest/package.json +++ b/packages/infrastructure/jest/package.json @@ -4,6 +4,10 @@ "version": "0.0.1", "description": "Jest configuration and scripts for Lens packages.", "type": "commonjs", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, "repository": { "type": "git", "url": "git+https://github.com/lensapp/lens.git" diff --git a/packages/infrastructure/typescript/package.json b/packages/infrastructure/typescript/package.json index 918e996042..3469151044 100644 --- a/packages/infrastructure/typescript/package.json +++ b/packages/infrastructure/typescript/package.json @@ -4,6 +4,10 @@ "version": "0.0.1", "description": "Typescript configuration for Lens packages.", "type": "commonjs", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, "repository": { "type": "git", "url": "git+https://github.com/lensapp/lens.git" diff --git a/packages/infrastructure/webpack/package.json b/packages/infrastructure/webpack/package.json index 2539a99bb8..e79c257f26 100644 --- a/packages/infrastructure/webpack/package.json +++ b/packages/infrastructure/webpack/package.json @@ -4,6 +4,10 @@ "version": "0.0.1", "description": "Webpack configurations and scripts for Lens packages.", "type": "commonjs", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, "repository": { "type": "git", "url": "git+https://github.com/lensapp/lens.git" diff --git a/packages/technical-features/application/package.json b/packages/technical-features/application/package.json index 91f1a6be34..a78de4da0e 100644 --- a/packages/technical-features/application/package.json +++ b/packages/technical-features/application/package.json @@ -7,6 +7,10 @@ "files": [ "dist" ], + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, "repository": { "type": "git", "url": "git+https://github.com/lensapp/monorepo.git" From 2550b1efbd4ae31f277ffbfef008fb467d4c0ba6 Mon Sep 17 00:00:00 2001 From: Juho Heikka Date: Thu, 23 Feb 2023 16:03:45 +0200 Subject: [PATCH 05/21] Fix catalog avatar font size (#7226) Signed-off-by: Juho Heikka --- .../core/src/renderer/components/+catalog/catalog.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/renderer/components/+catalog/catalog.module.scss b/packages/core/src/renderer/components/+catalog/catalog.module.scss index 74e4bcc80f..4fe31c4f27 100644 --- a/packages/core/src/renderer/components/+catalog/catalog.module.scss +++ b/packages/core/src/renderer/components/+catalog/catalog.module.scss @@ -127,7 +127,7 @@ } .catalogAvatar { - font-size: 1.2ch; + font-size: 1.2ch !important; } .views { From f2a229cef6107c99b8b61b9bbc4e566ee21f8358 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 24 Feb 2023 07:44:19 -0800 Subject: [PATCH 06/21] Fix ApiManager not handling duplicate apiBases of KubeApis (#7235) * Add failing unit test Signed-off-by: Sebastian Malton * Fix failing unit test Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- .../k8s-api/__tests__/api-manager.test.ts | 38 +++++++++++++++++-- .../common/k8s-api/api-manager/api-manager.ts | 9 +++-- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts index b83f52c3f0..2e7e545f76 100644 --- a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts @@ -19,6 +19,9 @@ import { KubeObject } from "../kube-object"; import { KubeObjectStore } from "../kube-object.store"; import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; +// eslint-disable-next-line no-restricted-imports +import { KubeApi as ExternalKubeApi } from "../../../extensions/common-api/k8s-api"; + class TestApi extends KubeApi { protected async checkPreferredVersion() { return; @@ -54,7 +57,7 @@ describe("ApiManager", () => { }); describe("registerApi", () => { - it("re-register store if apiBase changed", async () => { + it("re-register store if apiBase changed", () => { const apiBase = "apis/v1/foo"; const fallbackApiBase = "/apis/extensions/v1beta1/foo"; const kubeApi = new TestApi({ @@ -72,21 +75,48 @@ describe("ApiManager", () => { logger: di.inject(loggerInjectable), }, kubeApi); - apiManager.registerApi(apiBase, kubeApi); + apiManager.registerApi(kubeApi); // Define to use test api for ingress store Object.defineProperty(kubeStore, "api", { value: kubeApi }); - apiManager.registerStore(kubeStore, [kubeApi]); + apiManager.registerStore(kubeStore); // Test that store is returned with original apiBase expect(apiManager.getStore(kubeApi)).toBe(kubeStore); // Change apiBase similar as checkPreferredVersion does Object.defineProperty(kubeApi, "apiBase", { value: fallbackApiBase }); - apiManager.registerApi(fallbackApiBase, kubeApi); + apiManager.registerApi(kubeApi); // Test that store is returned with new apiBase expect(apiManager.getStore(kubeApi)).toBe(kubeStore); }); }); + + describe("technical tests for autorun", () => { + it("given two extensions register apis with the same apibase, settle into using the second", () => { + const apiBase = "/apis/aquasecurity.github.io/v1alpha1/vulnerabilityreports"; + const firstApi = Object.assign(new ExternalKubeApi({ + objectConstructor: KubeObject, + apiBase, + kind: "VulnerabilityReport", + }), { + myField: 1, + }); + const secondApi = Object.assign(new ExternalKubeApi({ + objectConstructor: KubeObject, + apiBase, + kind: "VulnerabilityReport", + }), { + myField: 2, + }); + + void firstApi; + void secondApi; + + expect(apiManager.getApi(apiBase)).toMatchObject({ + myField: 2, + }); + }); + }); }); diff --git a/packages/core/src/common/k8s-api/api-manager/api-manager.ts b/packages/core/src/common/k8s-api/api-manager/api-manager.ts index 81b59ef9c2..0dad9a2a21 100644 --- a/packages/core/src/common/k8s-api/api-manager/api-manager.ts +++ b/packages/core/src/common/k8s-api/api-manager/api-manager.ts @@ -41,19 +41,22 @@ export class ApiManager { const apis = chain(this.dependencies.apis.get().values()) .concat(this.externalApis.values()); const removedApis = new Set(this.apis.values()); + const newState = new Map(this.apis); for (const api of apis) { removedApis.delete(api); - this.apis.set(api.apiBase, api); + newState.set(api.apiBase, api); } for (const api of removedApis) { - for (const [apiBase, storedApi] of this.apis) { + for (const [apiBase, storedApi] of newState) { if (storedApi === api) { - this.apis.delete(apiBase); + newState.delete(apiBase); } } } + + this.apis.replace(newState); }); } From dc39dc9c62e85f99695b0aeeb93760f8ff073a1d Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 24 Feb 2023 07:44:35 -0800 Subject: [PATCH 07/21] Allow extensions to opt-out of KubeApi auto registering (#7217) Signed-off-by: Sebastian Malton --- .../core/src/extensions/common-api/k8s-api.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/core/src/extensions/common-api/k8s-api.ts b/packages/core/src/extensions/common-api/k8s-api.ts index 0b9a05353f..20e687ff6d 100644 --- a/packages/core/src/extensions/common-api/k8s-api.ts +++ b/packages/core/src/extensions/common-api/k8s-api.ts @@ -47,17 +47,29 @@ const getKubeApiDeps = (): KubeApiDependencies => { }; }; +export interface ExternalKubeApiOptions { + /** + * If `true` then on creation of the `KubeApi`instance a call to `apiManager.registerApi` will be + * made. This is `true` by default to maintain backwards compatability. + * + * Setting this to `false` might make `KubeObject`'s details drawer stop working. + * + * @default true + */ + autoRegister?: boolean; +} + // NOTE: this is done to preserve `instanceOf` behaviour function KubeApiCstr< Object extends KubeObject = KubeObject, Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, ->(opts: KubeApiOptions) { +>({ autoRegister = true, ...opts }: KubeApiOptions & ExternalKubeApiOptions) { const api = new InternalKubeApi(getKubeApiDeps(), opts); const di = getLegacyGlobalDiForExtensionApi(); const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken); - if (storesAndApisCanBeCreated) { + if (storesAndApisCanBeCreated && autoRegister) { apiManager.registerApi(api); } @@ -72,7 +84,7 @@ export type KubeApi< export const KubeApi = KubeApiCstr as unknown as new< Object extends KubeObject = KubeObject, Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, ->(opts: KubeApiOptions) => InternalKubeApi; +>(opts: KubeApiOptions & ExternalKubeApiOptions) => InternalKubeApi; /** * @deprecated Switch to using `Common.createResourceStack` instead From 5ad582d88e6d6438d08d9a8f8672d3b2d8f55b88 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 24 Feb 2023 13:47:53 -0800 Subject: [PATCH 08/21] Various improvements to release-tool (#7232) * Various improvements to release-tool - Pass more IO from script to user to provide better UX - Interactive versioning using lerna directly - Remove all CMD args in favour of interactive Signed-off-by: Sebastian Malton * Remove some more unnecessary console logs Signed-off-by: Sebastian Malton * Resolve comments Signed-off-by: Sebastian Malton * Fix repoRoot issue Signed-off-by: Sebastian Malton * De-spagetti-ify release-tool Signed-off-by: Sebastian Malton * Fix bugs related to picking PRs Signed-off-by: Sebastian Malton * Fix name Signed-off-by: Sebastian Malton * Improve display after picking PRs Signed-off-by: Sebastian Malton * Rename pickWhichPRsToUse Signed-off-by: Sebastian Malton * Add line describing what to do Signed-off-by: Sebastian Malton * Fix not displaying output after cherry-pick fails Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton Co-authored-by: Roman --- package-lock.json | 364 ++++++++++++++++++--- packages/release-tool/package.json | 11 +- packages/release-tool/src/index.ts | 508 +++++++++++++++-------------- 3 files changed, 589 insertions(+), 294 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24cae086b2..f1889b6304 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5854,6 +5854,16 @@ "@types/node": "*" } }, + "node_modules/@types/inquirer": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.3.tgz", + "integrity": "sha512-CzNkWqQftcmk2jaCWdBTf9Sm7xSw4rkI1zpU/Udw3HX5//adEZUIm9STtoRP1qgWj0CWQtJ9UTvqmO2NNjhMJw==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -5955,15 +5965,6 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, - "node_modules/@types/jsonfile": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz", - "integrity": "sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -6380,6 +6381,15 @@ "@types/jest": "*" } }, + "node_modules/@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/tough-cookie": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", @@ -8837,8 +8847,7 @@ "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, "node_modules/chart.js": { "version": "2.9.4", @@ -9095,7 +9104,6 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true, "engines": { "node": ">=6" }, @@ -9144,7 +9152,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, "engines": { "node": ">=0.8" } @@ -10518,7 +10525,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, "dependencies": { "clone": "^1.0.2" }, @@ -11077,6 +11083,11 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -13149,7 +13160,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -13163,7 +13173,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -21651,8 +21660,7 @@ "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, "node_modules/nan": { "version": "2.17.0", @@ -25395,7 +25403,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -28093,6 +28100,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.1.2.tgz", "integrity": "sha512-BlIbgFryTbw3Dz6hyoWFhKk+unCcHMSkZGrTFVAx2WmttdBSonsdtRlwiuTbDqTKr+UlXIUqJVS4QT5tUzGENQ==", + "dev": true, "bin": { "rimraf": "dist/cjs/src/bin.js" }, @@ -28125,7 +28133,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -28166,7 +28173,6 @@ "version": "7.8.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -30266,7 +30272,6 @@ "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -31304,7 +31309,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, "dependencies": { "defaults": "^1.0.3" } @@ -34464,7 +34468,9 @@ "version": "6.4.0-beta.13", "license": "MIT", "dependencies": { - "rimraf": "^4.1.2" + "chalk": "^5.2.0", + "inquirer": "^9.1.4", + "semver": "^7.3.8" }, "bin": { "create-release-pr": "dist/index.js" @@ -34472,23 +34478,10 @@ "devDependencies": { "@swc/cli": "^0.1.61", "@swc/core": "^1.3.35", - "@types/command-line-args": "^5.2.0", - "@types/fs-extra": "^11.0.1", + "@types/inquirer": "^9.0.3", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "command-line-args": "^5.2.1", - "fs-extra": "^11.1.0", - "semver": "^7.3.8" - } - }, - "packages/release-tool/node_modules/@types/fs-extra": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.1.tgz", - "integrity": "sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==", - "dev": true, - "dependencies": { - "@types/jsonfile": "*", - "@types/node": "*" + "rimraf": "^4.1.2" } }, "packages/release-tool/node_modules/@types/node": { @@ -34497,18 +34490,293 @@ "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", "dev": true }, - "packages/release-tool/node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dev": true, + "packages/release-tool/node_modules/ansi-escapes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.0.0.tgz", + "integrity": "sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "type-fest": "^3.0.0" }, "engines": { - "node": ">=14.14" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "packages/release-tool/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/release-tool/node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "packages/release-tool/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "packages/release-tool/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/release-tool/node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/cli-width": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz", + "integrity": "sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==", + "engines": { + "node": ">= 12" + } + }, + "packages/release-tool/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "packages/release-tool/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/figures": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", + "dependencies": { + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/inquirer": { + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.4.tgz", + "integrity": "sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==", + "dependencies": { + "ansi-escapes": "^6.0.0", + "chalk": "^5.1.2", + "cli-cursor": "^4.0.0", + "cli-width": "^4.0.0", + "external-editor": "^3.0.3", + "figures": "^5.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^6.1.2", + "run-async": "^2.4.0", + "rxjs": "^7.5.7", + "string-width": "^5.1.2", + "strip-ansi": "^7.0.1", + "through": "^2.3.6", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "packages/release-tool/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/ora": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", + "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", + "dependencies": { + "bl": "^5.0.0", + "chalk": "^5.0.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.6.1", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.1.0", + "log-symbols": "^5.1.0", + "strip-ansi": "^7.0.1", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "packages/release-tool/node_modules/type-fest": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.6.0.tgz", + "integrity": "sha512-RqTRtKTzvPpNdDUp1dVkKQRunlPITk4mXeqFlAZoJsS+fLRn8AdPK0TcQDumGayhU7fjlBfiBjsq3pe3rIfXZQ==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/release-tool/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "packages/semver": { diff --git a/packages/release-tool/package.json b/packages/release-tool/package.json index 517b354203..8cc0f00335 100644 --- a/packages/release-tool/package.json +++ b/packages/release-tool/package.json @@ -16,15 +16,14 @@ "devDependencies": { "@swc/cli": "^0.1.61", "@swc/core": "^1.3.35", - "@types/command-line-args": "^5.2.0", - "@types/fs-extra": "^11.0.1", + "@types/inquirer": "^9.0.3", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "command-line-args": "^5.2.1", - "fs-extra": "^11.1.0", - "semver": "^7.3.8" + "rimraf": "^4.1.2" }, "dependencies": { - "rimraf": "^4.1.2" + "chalk": "^5.2.0", + "inquirer": "^9.1.4", + "semver": "^7.3.8" } } diff --git a/packages/release-tool/src/index.ts b/packages/release-tool/src/index.ts index 2f788dd117..7a4ce49a27 100755 --- a/packages/release-tool/src/index.ts +++ b/packages/release-tool/src/index.ts @@ -3,122 +3,22 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import assert from "assert"; +import chalk from "chalk"; import child_process from "child_process"; -import commandLineArgs from "command-line-args"; -import fse from "fs-extra"; -import { basename } from "path"; -import { createInterface } from "readline"; +import { readFile } from "fs/promises"; +import inquirer from "inquirer"; +import { createInterface, ReadLine } from "readline"; import semver from "semver"; import { promisify } from "util"; -const { - SemVer, - valid: semverValid, - rcompare: semverRcompare, - lte: semverLte, -} = semver; +type SemVer = semver.SemVer; + +const { SemVer } = semver; const exec = promisify(child_process.exec); +const spawn = promisify(child_process.spawn); const execFile = promisify(child_process.execFile); -const options = commandLineArgs([ - { - name: "type", - defaultOption: true, - }, - { - name: "preid", - }, - { - name: "check-commits", - type: Boolean, - }, -]); - -const validReleaseValues = [ - "major", - "minor", - "patch", -]; -const validPrereleaseValues = [ - "premajor", - "preminor", - "prepatch", - "prerelease", -]; -const validPreidValues = [ - "alpha", - "beta", -]; - -const errorMessages = { - noReleaseType: `No release type provided. Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`, - invalidRelease: (invalid: string) => `Invalid release type was provided (value was "${invalid}"). Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`, - noPreid: `No preid was provided. Use '--preid' to specify. Valid options are: ${validPreidValues.join(", ")}`, - invalidPreid: (invalid: string) => `Invalid preid was provided (value was "${invalid}"). Valid options are: ${validPreidValues.join(", ")}`, - wrongCwd: "It looks like you are running this script from the 'scripts' directory. This script assumes it is run from the root of the git repo", -}; - -if (!options.type) { - console.error(errorMessages.noReleaseType); - process.exit(1); -} - -if (validReleaseValues.includes(options.type)) { - // do nothing, is valid -} else if (validPrereleaseValues.includes(options.type)) { - if (!options.preid) { - console.error(errorMessages.noPreid); - process.exit(1); - } - - if (!validPreidValues.includes(options.preid)) { - console.error(errorMessages.invalidPreid(options.preid)); - process.exit(1); - } -} else { - console.error(errorMessages.invalidRelease(options.type)); - process.exit(1); -} - -if (basename(process.cwd()) === "scripts") { - console.error(errorMessages.wrongCwd); -} - -const currentVersion = new SemVer((await fse.readJson("./lerna.json")).version); - -console.log(`current version: ${currentVersion.format()}`); - -const newVersion = currentVersion.inc(options.type, options.preid); -const newVersionMilestone = `${newVersion.major}.${newVersion.minor}.${newVersion.patch}`; -const prBranch = `release/v${newVersion.format()}`; - -await exec(`npm run bump-version --yes ${newVersion.format()}`); -await exec(`git checkout -b ${prBranch}`); -await exec("git add lerna.json packages/*/package.json"); -await exec(`git commit -sm "Release ${newVersion.format()}"`); - -console.log(`new version: ${newVersion.format()}`); - -console.log("fetching tags..."); -await exec("git fetch --tags --force"); - -const actualTags = (await exec("git tag --list", { encoding: "utf-8" })).stdout.split(/\r?\n/).map(line => line.trim()); -const [previousReleasedVersion] = actualTags - .map((value) => semverValid(value)) - .filter((v): v is string => typeof v === "string") - .sort((l, r) => semverRcompare(l, r)) - .filter(version => semverLte(version, currentVersion)); - -const getMergedPrsArgs = [ - "gh", - "pr", - "list", - "--limit=500", // Should be big enough, if not we need to release more often ;) - "--state=merged", - "--base=master", - "--json mergeCommit,title,author,labels,number,milestone,mergedAt", -]; - interface GithubPrData { author: { login: string; @@ -147,168 +47,296 @@ interface ExtendedGithubPrData extends Omit { mergedAt: Date; } -console.log("retreiving last 500 PRs to create release PR body..."); -const mergedPrs = JSON.parse((await exec(getMergedPrsArgs.join(" "), { encoding: "utf-8" })).stdout) as GithubPrData[]; -const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone?.title === newVersionMilestone); -const relaventPrsQuery = await Promise.all( - milestoneRelevantPrs.map(async pr => ({ - pr, - stdout: (await exec(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout, - })), -); -const relaventPrs = relaventPrsQuery - .filter(query => query.stdout) - .map(query => query.pr) - .filter(pr => pr.labels.every(label => label.name !== "skip-changelog")) - .map(pr => ({ ...pr, mergedAt: new Date(pr.mergedAt) } as ExtendedGithubPrData)) - .sort((left, right) => { - const leftAge = left.mergedAt.valueOf(); - const rightAge = right.mergedAt.valueOf(); +async function getCurrentBranch(): Promise { + return (await exec("git branch --show-current")).stdout.trim(); +} - if (leftAge === rightAge) { - return 0; - } +async function getAbsolutePathToRepoRoot(): Promise { + return (await exec("git rev-parse --show-toplevel")).stdout.trim(); +} - if (leftAge > rightAge) { - return 1; - } +async function fetchAllGitTags(): Promise { + await execFile("git", ["fetch", "--tags", "--force"]); - return -1; + const { stdout } = await exec("git tag --list", { encoding: "utf-8" }); + + return stdout + .split(/\r?\n/) + .map(line => line.trim()); +} + +async function bumpPackageVersions(): Promise { + await spawn("npm", ["run", "bump-version"], { + stdio: "inherit", + }); +} + +function isDefined(value: T | null | undefined): value is T { + return value != null; +} + +function findClosestVersionTagLessThanVersion(tags: string[], version: SemVer): string { + const lessThanTags = tags + .map((value) => semver.parse(value)) + .filter(isDefined) + .filter(version => !version.prerelease.includes("cron")) + .sort(semver.rcompare) + .filter(version => semver.lte(version, version)); + + assert(lessThanTags.length > 0, `Cannot find version tag less than ${version.format()}`); + + return lessThanTags[0].format(); +} + +async function getCurrentVersionOfSubPackage(packageName: string): Promise { + const packageJson = JSON.parse(await readFile(`./packages/${packageName}/package.json`, "utf-8")); + + return new SemVer(packageJson.version); +} + +async function checkCurrentWorkingDirectory(): Promise { + const repoRoot = await getAbsolutePathToRepoRoot(); + + if (process.cwd() !== repoRoot) { + console.error("It looks like you are running this script from the 'scripts' directory. This script assumes it is run from the root of the git repo"); + process.exit(1); + } +} + +function formatSemverForMilestone(version: SemVer): string { + return `${version.major}.${version.minor}.${version.patch}`; +} + +async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prBody: string): Promise { + const prBranch = `release/v${version.format()}`; + + await spawn("git", ["checkout", "-b", prBranch], { + stdio: "inherit", + }); + await spawn("git", ["add", "lerna.json", "packages/*/package.json"], { + stdio: "inherit", + }); + await spawn("git", ["commit", "-sm", `"Release ${version.format()}"`], { + stdio: "inherit", + }); + await spawn("git", ["push", "--set-upstream", "origin", prBranch], { + stdio: "inherit", }); -const enhancementPrLabelName = "enhancement"; -const bugfixPrLabelName = "bug"; + await spawn("gh", [ + "pr", + "create", + "--base", prBase, + "--title", `Release ${version.format()}`, + "--label", "skip-changelog", + "--label", "release", + "--milestone", formatSemverForMilestone(version), + "--body-file", prBody, + ], { + stdio: "inherit" + }); +} -const isEnhancementPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === enhancementPrLabelName); -const isBugfixPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === bugfixPrLabelName); +function sortExtendedGithubPrData(left: ExtendedGithubPrData, right: ExtendedGithubPrData): number { + const leftAge = left.mergedAt.valueOf(); + const rightAge = right.mergedAt.valueOf(); -const prLines = { - enhancement: [] as string[], - bugfix: [] as string[], - maintenence: [] as string[], -}; + if (leftAge === rightAge) { + return 0; + } -function getPrEntry(pr: ExtendedGithubPrData) { + if (leftAge > rightAge) { + return 1; + } + + return -1; +} + +async function getRelevantPRs(milestone: string, previousReleasedVersion: string): Promise { + console.log("retreiving previous 500 PRs..."); + + const getMergedPrsArgs = [ + "gh", + "pr", + "list", + "--limit=500", // Should be big enough, if not we need to release more often ;) + "--state=merged", + "--base=master", + "--json mergeCommit,title,author,labels,number,milestone,mergedAt", + ]; + + const mergedPrs = JSON.parse((await exec(getMergedPrsArgs.join(" "), { encoding: "utf-8" })).stdout) as GithubPrData[]; + const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone?.title === milestone); + const relaventPrsQuery = await Promise.all( + milestoneRelevantPrs.map(async pr => ({ + pr, + stdout: (await exec(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout, + })), + ); + + return relaventPrsQuery + .filter(query => query.stdout) + .map(query => query.pr) + .filter(pr => pr.labels.every(label => label.name !== "skip-changelog")) + .map(pr => ({ ...pr, mergedAt: new Date(pr.mergedAt) } as ExtendedGithubPrData)) + .sort(sortExtendedGithubPrData); +} + +function formatPrEntry(pr: ExtendedGithubPrData) { return `- ${pr.title} (**[#${pr.number}](https://github.com/lensapp/lens/pull/${pr.number})**) https://github.com/${pr.author.login}`; } -const rl = createInterface(process.stdin); -const prBase = newVersion.patch === 0 - ? "master" - : `release/v${newVersion.major}.${newVersion.minor}`; +const isEnhancementPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "enhancement"); +const isBugfixPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "bug"); -function askQuestion(question: string): Promise { - return new Promise(resolve => { - function _askQuestion() { - console.log(question); +const cherrypickCommitWith = (rl: ReadLine) => async (commit: string) => { + try { + const cherryPick = child_process.spawn("git", ["cherry-pick", commit]); - rl.once("line", (answer) => { - const cleaned = answer.trim().toLowerCase(); + cherryPick.stdout.pipe(process.stdout); + cherryPick.stderr.pipe(process.stderr); - if (cleaned === "y") { - resolve(true); - } else if (cleaned === "n") { - resolve(false); - } else { - _askQuestion(); + await new Promise((resolve, reject) => { + const cleaners: (() => void)[] = []; + const cleanup = () => cleaners.forEach(cleaner => cleaner()); + + const onExit = (code: number | null) => { + if (code) { + reject(new Error(`git cherry-pick failed with exit code ${code}`)); + cleanup(); } - }); - } - _askQuestion(); + resolve(); + cleanup(); + }; + + cherryPick.once("exit", onExit); + cleaners.push(() => cherryPick.off("exit", onExit)); + + const onError = (error: Error) => { + cleanup(); + reject(error); + }; + + cherryPick.once("error", onError); + cleaners.push(() => cherryPick.off("error", onError)); + }); + } catch { + console.error(chalk.bold("Please resolve conflicts in a seperate terminal and then press enter here...")); + await new Promise(resolve => rl.once("line", () => resolve())); + } +}; + +async function pickWhichPRsToUse(prs: ExtendedGithubPrData[]): Promise { + const answers = await inquirer.prompt<{ commits: number[] }>({ + type: "checkbox", + name: `commits`, + message: "Pick which commits to use...", + default: [], + choices: prs.map(pr => ({ + checked: true, + key: pr.number, + name: `#${pr.number}: ${pr.title} (https://github.com/lensapp/lens/pull/${pr.number})`, + value: pr.number, + short: `#${pr.number}`, + })), + loop: false, }); + + return prs.filter(pr => answers.commits.includes(pr.number)); } -async function handleRelaventPr(pr: ExtendedGithubPrData) { - if (options["check-commits"] && !(await askQuestion(`Would you like to use #${pr.number}: ${pr.title}? - Y/N`))) { - return; - } +function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrData[]): string { + const enhancementPrLines: string[] = []; + const bugPrLines: string[] = []; + const maintenencePrLines: string[] = []; - if (prBase !== "master") { - try { - const promise = exec(`git cherry-pick ${pr.mergeCommit.oid}`); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - promise.child.stdout!.pipe(process.stdout); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - promise.child.stderr!.pipe(process.stderr); - - await promise; - } catch { - console.error(`Failed to cherry-pick ${pr.mergeCommit.oid}, please resolve conflicts and then press enter here:`); - await new Promise(resolve => rl.once("line", () => resolve())); + for (const pr of prs) { + if (isEnhancementPr(pr)) { + enhancementPrLines.push(formatPrEntry(pr)); + } else if (isBugfixPr(pr)) { + bugPrLines.push(formatPrEntry(pr)); + } else { + maintenencePrLines.push(formatPrEntry(pr)); } } - if (isEnhancementPr(pr)) { - prLines.enhancement.push(getPrEntry(pr)); - } else if (isBugfixPr(pr)) { - prLines.bugfix.push(getPrEntry(pr)); - } else { - prLines.maintenence.push(getPrEntry(pr)); + if (enhancementPrLines.length > 0) { + enhancementPrLines.unshift("## 🚀 Features", ""); + enhancementPrLines.push(""); } + + if (bugPrLines.length > 0) { + bugPrLines.unshift("## 🐛 Bug Fixes", ""); + bugPrLines.push(""); + } + + if (maintenencePrLines.length > 0) { + maintenencePrLines.unshift("## 🧰 Maintenance", ""); + maintenencePrLines.push(""); + } + + return [ + `## Changes since ${previousReleasedVersion}`, + "", + ...enhancementPrLines, + ...bugPrLines, + ...maintenencePrLines, + ].join("\n"); } -for (const pr of relaventPrs) { - await handleRelaventPr(pr); +async function cherrypickCommits(prs: ExtendedGithubPrData[]): Promise { + const rl = createInterface(process.stdin); + const cherrypickCommit = cherrypickCommitWith(rl); + + for (const pr of prs) { + await cherrypickCommit(pr.mergeCommit.oid); + } + + rl.close(); } -rl.close(); +async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise { + if (isMasterBranch) { + return prs; + } -const prBodyLines = [ - `## Changes since ${previousReleasedVersion}`, - "", - ...( - prLines.enhancement.length > 0 - ? [ - "## 🚀 Features", - "", - ...prLines.enhancement, - "", - ] - : [] - ), - ...( - prLines.bugfix.length > 0 - ? [ - "## 🐛 Bug Fixes", - "", - ...prLines.bugfix, - "", - ] - : [] - ), - ...( - prLines.maintenence.length > 0 - ? [ - "## 🧰 Maintenance", - "", - ...prLines.maintenence, - "", - ] - : [] - ), -]; -const prBody = prBodyLines.join("\n"); -const createPrArgs = [ - "pr", - "create", - "--base", prBase, - "--title", `Release ${newVersion.format()}`, - "--label", "skip-changelog", - "--label", "release", - "--milestone", `${newVersion.major}.${newVersion.minor}.${newVersion.patch}`, - "--body-file", "-", -]; + let selectedPrs: ExtendedGithubPrData[]; -await exec(`git push --set-upstream origin ${prBranch}`); + do { + selectedPrs = await pickWhichPRsToUse(prs); + } while (selectedPrs.length === 0 && (console.warn("[WARNING]: must pick at least once commit"), true)); -const createPrProcess = execFile("gh", createPrArgs); + await cherrypickCommits(selectedPrs); -createPrProcess.child.stdout?.pipe(process.stdout); -createPrProcess.child.stderr?.pipe(process.stderr); + return selectedPrs; +} -createPrProcess.child.stdin?.write(prBody); -createPrProcess.child.stdin?.end(); +async function createRelease(): Promise { + await checkCurrentWorkingDirectory(); -await createPrProcess; + const currentK8slensCoreVersion = await getCurrentVersionOfSubPackage("core"); + const prBase = await getCurrentBranch(); + const isMasterBranch = prBase === "master"; + const tags = await fetchAllGitTags(); + const previousReleasedVersion = findClosestVersionTagLessThanVersion(tags, currentK8slensCoreVersion); + + if (isMasterBranch) { + await bumpPackageVersions(); + } + + const prMilestone = formatSemverForMilestone(await getCurrentVersionOfSubPackage("core")); + const relaventPrs = await getRelevantPRs(prMilestone, previousReleasedVersion); + const selectedPrs = await pickRelaventPrs(relaventPrs, isMasterBranch); + const prBody = formatChangelog(previousReleasedVersion, selectedPrs); + + if (!isMasterBranch) { + await bumpPackageVersions(); + } + + const newK8slensCoreVersion = await getCurrentVersionOfSubPackage("core"); + + await createReleaseBranchAndCommit(prBase, newK8slensCoreVersion, prBody); +} + +await createRelease(); From 3067e82e02cf51e69bf444f587675871dd5bd9f5 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 24 Feb 2023 14:04:15 -0800 Subject: [PATCH 09/21] Remove all references to slack (#7233) * Remove all references to slack Signed-off-by: Sebastian Malton * Fix readme Signed-off-by: Sebastian Malton * Cleanup migration Signed-off-by: Sebastian Malton * Remove existing slack link from weblink store Signed-off-by: Sebastian Malton * Fix type error and wording on ErrorBoundary Signed-off-by: Sebastian Malton * Don't export forumsUrl to extension API - Also just remove slack URL Signed-off-by: Sebastian Malton * Update snapshots again Signed-off-by: Sebastian Malton * Update snapshots again v3 Signed-off-by: Sebastian Malton * Revert remove slackUrl Signed-off-by: Sebastian Malton * Fix filtering Signed-off-by: Sebastian Malton * Fix readme Signed-off-by: Sebastian Malton * More of a fix Signed-off-by: Sebastian Malton * Try again Signed-off-by: Sebastian Malton * Slightly better for now Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- README.md | 6 +-- docs/README.md | 20 ++++---- mkdocs.yml | 48 +++++++++---------- packages/core/src/common/vars.ts | 2 +- .../core/src/extensions/common-api/app.ts | 7 ++- ...acters-in-page-registrations.test.tsx.snap | 4 +- .../navigate-to-extension-page.test.tsx.snap | 4 +- ...ation-using-application-menu.test.tsx.snap | 4 +- .../installing-update.test.ts.snap | 24 +++++----- ...g-update-using-topbar-button.test.tsx.snap | 8 ++-- .../installing-update-using-tray.test.ts.snap | 24 +++++----- .../__snapshots__/force-update.test.ts.snap | 12 ++--- ...eriodical-checking-of-updates.test.ts.snap | 4 +- ...selection-of-update-stability.test.ts.snap | 4 +- .../opening-entity-details.test.tsx.snap | 4 +- .../keyboard-shortcuts.test.tsx.snap | 32 ++++++------- ...-settings-for-correct-entity.test.tsx.snap | 4 +- ...gation-using-application-menu.test.ts.snap | 4 +- ...gation-using-application-menu.test.ts.snap | 4 +- .../navigation-using-tray.test.ts.snap | 4 +- ...-originating-from-extensions.test.tsx.snap | 4 +- ...dability-using-extension-api.test.tsx.snap | 16 +++---- ...gation-using-application-menu.test.ts.snap | 8 ++-- .../core/src/main/weblinks-store/links.ts | 4 +- .../migrations/5.1.4.injectable.ts | 4 +- .../migrations/5.4.5-beta.1.injectable.ts | 36 +++++++------- .../migrations/currentVersion.injectable.ts | 14 +++--- .../renderer/components/+welcome/welcome.tsx | 6 +-- .../error-boundary/error-boundary.tsx | 10 ++-- 29 files changed, 165 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index 3949cb23c8..f97e647c6d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Lens Desktop Core ("OpenLens") [![Build Status](https://github.com/lensapp/lens/actions/workflows/test.yml/badge.svg)](https://github.com/lensapp/lens/actions/workflows/test.yml) -[![Chat on Slack](https://img.shields.io/badge/chat-on%20slack-blue.svg?logo=slack&longCache=true&style=flat)](https://k8slens.dev/slack.html) +[Explore our Forums](https://forums.k8slens.dev) ## The Repository -This repository is where Team Lens develops the core of the [Lens Desktop](https://k8slens.dev) product together with the community. +This repository is where Team Lens develops the core of the [Lens Desktop](https://k8slens.dev) product together with the community. The core is a library, powered by [Electron](https://www.electronjs.org/) and [React](https://reactjs.org/). Unlike generic Electron + React frameworks / boilerplates, it is very opinionated for creating Lens Desktop-like applications and has support for Lens Extensions. @@ -31,4 +31,4 @@ See [Contributing](https://docs.k8slens.dev/contributing/) page. ## License -See [License](LICENSE). \ No newline at end of file +See [License](LICENSE). diff --git a/docs/README.md b/docs/README.md index 67369f86fc..b22509eb16 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,15 +7,15 @@ To install your first extension you should goto the [extension page](lens://app/ This documentation describes: -* How to build, run, test, and publish an extension. -* How to take full advantage of the Lens Extension API. -* Where to find [guides](extensions/guides/README.md) and [code samples](https://github.com/lensapp/lens-extension-samples) to help get you started. +- How to build, run, test, and publish an extension. +- How to take full advantage of the Lens Extension API. +- Where to find [guides](extensions/guides/README.md) and [code samples](https://github.com/lensapp/lens-extension-samples) to help get you started. ## What Extensions Can Do Here are some examples of what you can achieve with the Extension API: -* Add custom components & views in the UI - Extending the Lens Workbench +- Add custom components & views in the UI - Extending the Lens Workbench For an overview of the Lens Extension API, refer to the [Common Capabilities](extensions/capabilities/common-capabilities.md) page. [Extension Guides Overview](extensions/guides/README.md) also includes a list of code samples and guides that illustrate various ways of using the Lens Extension API. @@ -23,11 +23,11 @@ For an overview of the Lens Extension API, refer to the [Common Capabilities](ex Here is what each section of the Lens Extension API docs can help you with: -* **Getting Started** teaches fundamental concepts for building extensions with the Hello World sample. -* **Extension Capabilities** dissects Lens's Extension API into smaller categories and points you to more detailed topics. -* **Extension Guides** includes guides and code samples that explain specific usages of Lens Extension API. -* **Testing and Publishing** includes in-depth guides on various extension development topics, such as testing and publishing extensions. -* **API Reference** contains exhaustive references for the Lens Extension API, Contribution Points, and many other topics. +- **Getting Started** teaches fundamental concepts for building extensions with the Hello World sample. +- **Extension Capabilities** dissects Lens's Extension API into smaller categories and points you to more detailed topics. +- **Extension Guides** includes guides and code samples that explain specific usages of Lens Extension API. +- **Testing and Publishing** includes in-depth guides on various extension development topics, such as testing and publishing extensions. +- **API Reference** contains exhaustive references for the Lens Extension API, Contribution Points, and many other topics. ## What's New @@ -45,7 +45,7 @@ See the [Lens v4 to v5 extension migration notes](extensions/extension-migration ## Looking for Help -If you have questions for extension development, try asking on the [Lens Dev Slack](http://k8slens.slack.com/). It's a public chatroom for Lens developers, where Lens team members chime in from time to time. +If you have questions for extension development, try asking on the [Lens Forums](http://forums.k8slens.dev/). It's a public chatroom for Lens developers, where Lens team members chime in from time to time. To provide feedback on the documentation or issues with the Lens Extension API, create new issues at [lensapp/lens](https://github.com/lensapp/lens/issues). Please use the labels `area/documentation` and/or `area/extension`. diff --git a/mkdocs.yml b/mkdocs.yml index b869a63ee9..e0f1568b87 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,33 +10,33 @@ edit_uri: "" nav: - Overview: README.md - Getting Started: - - Overview: extensions/get-started/overview.md - - Your First Extension: extensions/get-started/your-first-extension.md - - Extension Anatomy: extensions/get-started/anatomy.md - - Wrapping Up: extensions/get-started/wrapping-up.md + - Overview: extensions/get-started/overview.md + - Your First Extension: extensions/get-started/your-first-extension.md + - Extension Anatomy: extensions/get-started/anatomy.md + - Wrapping Up: extensions/get-started/wrapping-up.md - Extension Capabilities: - - Common Capabilities: extensions/capabilities/common-capabilities.md - - Styling: extensions/capabilities/styling.md + - Common Capabilities: extensions/capabilities/common-capabilities.md + - Styling: extensions/capabilities/styling.md - Extension Guides: - - Overview: extensions/guides/README.md - - Generator: extensions/guides/generator.md - - Main Extension: extensions/guides/main-extension.md - - Renderer Extension: extensions/guides/renderer-extension.md - - Catalog: extensions/guides/catalog.md - - Resource Stack: extensions/guides/resource-stack.md - - Extending KubernetesCluster: extensions/guides/extending-kubernetes-cluster.md - - Stores: extensions/guides/stores.md - - Working with MobX: extensions/guides/working-with-mobx.md - - Protocol Handlers: extensions/guides/protocol-handlers.md - - IPC: extensions/guides/ipc.md + - Overview: extensions/guides/README.md + - Generator: extensions/guides/generator.md + - Main Extension: extensions/guides/main-extension.md + - Renderer Extension: extensions/guides/renderer-extension.md + - Catalog: extensions/guides/catalog.md + - Resource Stack: extensions/guides/resource-stack.md + - Extending KubernetesCluster: extensions/guides/extending-kubernetes-cluster.md + - Stores: extensions/guides/stores.md + - Working with MobX: extensions/guides/working-with-mobx.md + - Protocol Handlers: extensions/guides/protocol-handlers.md + - IPC: extensions/guides/ipc.md - Testing and Publishing: - - Testing Extensions: extensions/testing-and-publishing/testing.md - - Publishing Extensions: extensions/testing-and-publishing/publishing.md + - Testing Extensions: extensions/testing-and-publishing/testing.md + - Publishing Extensions: extensions/testing-and-publishing/publishing.md - API Reference: extensions/api/README.md theme: - name: 'material' + name: "material" highlightjs: true - language: 'en' + language: "en" custom_dir: docs/custom_theme favicon: img/favicon.ico logo: img/lens-logo-icon.svg @@ -79,9 +79,9 @@ extra: - icon: fontawesome/brands/twitter link: https://twitter.com/k8slens name: Lens on Twitter - - icon: fontawesome/brands/slack - link: http://k8slens.slack.com/ - name: Lens on Slack + - icon: fontawesome/brands/discourse + link: https://forums.k8slens.dev/ + name: Lens Forums - icon: fontawesome/solid/link link: https://k8slens.dev/ name: Lens Website diff --git a/packages/core/src/common/vars.ts b/packages/core/src/common/vars.ts index 8f53d6354c..f4c19c16e6 100644 --- a/packages/core/src/common/vars.ts +++ b/packages/core/src/common/vars.ts @@ -18,6 +18,6 @@ export const apiKubePrefix = "/api-kube"; // k8s cluster apis // Links export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues" as string; -export const slackUrl = "https://k8slens.dev/slack.html" as string; export const supportUrl = "https://docs.k8slens.dev/support/" as string; export const docsUrl = "https://docs.k8slens.dev" as string; +export const forumsUrl = "https://forums.k8slens.dev" as string; diff --git a/packages/core/src/extensions/common-api/app.ts b/packages/core/src/extensions/common-api/app.ts index dd9227cd0e..8c190b984b 100644 --- a/packages/core/src/extensions/common-api/app.ts +++ b/packages/core/src/extensions/common-api/app.ts @@ -11,7 +11,7 @@ import isWindowsInjectable from "../../common/vars/is-windows.injectable"; import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import getEnabledExtensionsInjectable from "./get-enabled-extensions/get-enabled-extensions.injectable"; -import { slackUrl, issuesTrackerUrl } from "../../common/vars"; +import { issuesTrackerUrl } from "../../common/vars"; import { buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable"; import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; import userStoreInjectable from "../../common/user-store/user-store.injectable"; @@ -53,6 +53,9 @@ export const App = { return di.inject(isLinuxInjectable); }, - slackUrl, + /** + * @deprecated This value is now `""` and is left here for backwards compatability. + */ + slackUrl: "", issuesTrackerUrl, } as const; diff --git a/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap b/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap index ccf3a4153f..03e2a2cd8a 100644 --- a/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap +++ b/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap @@ -107,11 +107,11 @@ exports[`extension special characters in page registrations renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap b/packages/core/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap index b66ac6c4b3..5a864f8096 100644 --- a/packages/core/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap +++ b/packages/core/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap @@ -107,11 +107,11 @@ exports[`navigate to extension page renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap b/packages/core/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap index 28cdcc4b8d..4bd662e2f0 100644 --- a/packages/core/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap +++ b/packages/core/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap @@ -107,11 +107,11 @@ exports[`add-cluster - navigation using application menu renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap b/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap index 6e61ea5e5d..0b32c94831 100644 --- a/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap +++ b/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap @@ -108,11 +108,11 @@ exports[`installing update when started renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -393,11 +393,11 @@ exports[`installing update when started when user checks for updates renders 1`] If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -678,11 +678,11 @@ exports[`installing update when started when user checks for updates when new up If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -988,11 +988,11 @@ exports[`installing update when started when user checks for updates when new up If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1298,11 +1298,11 @@ exports[`installing update when started when user checks for updates when new up If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1583,11 +1583,11 @@ exports[`installing update when started when user checks for updates when no new If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap b/packages/core/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap index a4787d0de3..26fd923e16 100644 --- a/packages/core/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap +++ b/packages/core/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap @@ -133,11 +133,11 @@ exports[`encourage user to update when sufficient time passed since update was d If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -418,11 +418,11 @@ exports[`encourage user to update when sufficient time passed since update was d If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap b/packages/core/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap index a2ea7a417b..57b2dd6f48 100644 --- a/packages/core/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap +++ b/packages/core/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap @@ -108,11 +108,11 @@ exports[`installing update using tray when started renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -393,11 +393,11 @@ exports[`installing update using tray when started when user checks for updates If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -678,11 +678,11 @@ exports[`installing update using tray when started when user checks for updates If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -988,11 +988,11 @@ exports[`installing update using tray when started when user checks for updates If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1298,11 +1298,11 @@ exports[`installing update using tray when started when user checks for updates If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1583,11 +1583,11 @@ exports[`installing update using tray when started when user checks for updates If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/application-update/child-features/force-update/__snapshots__/force-update.test.ts.snap b/packages/core/src/features/application-update/child-features/force-update/__snapshots__/force-update.test.ts.snap index 95101e8294..5dd749857a 100644 --- a/packages/core/src/features/application-update/child-features/force-update/__snapshots__/force-update.test.ts.snap +++ b/packages/core/src/features/application-update/child-features/force-update/__snapshots__/force-update.test.ts.snap @@ -133,11 +133,11 @@ exports[`force user to update when too long since update was downloaded when app If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -443,11 +443,11 @@ exports[`force user to update when too long since update was downloaded when app If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -800,11 +800,11 @@ exports[`force user to update when too long since update was downloaded when app If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap b/packages/core/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap index 413477ef9a..e6ae682c92 100644 --- a/packages/core/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap +++ b/packages/core/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap @@ -108,11 +108,11 @@ exports[`periodical checking of updates given updater is enabled and configurati If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap b/packages/core/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap index 7da7f42004..9e77201bf3 100644 --- a/packages/core/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap +++ b/packages/core/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap @@ -108,11 +108,11 @@ exports[`selection of update stability when started renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap b/packages/core/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap index 49d085fc85..c7b284a08e 100644 --- a/packages/core/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap +++ b/packages/core/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap @@ -108,11 +108,11 @@ exports[`opening catalog entity details panel renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap b/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap index cbe09a0e1c..0269032531 100644 --- a/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap +++ b/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap @@ -199,11 +199,11 @@ exports[`Command Pallet: keyboard shortcut tests when on linux renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -575,11 +575,11 @@ exports[`Command Pallet: keyboard shortcut tests when on linux when pressing ESC If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -951,11 +951,11 @@ exports[`Command Pallet: keyboard shortcut tests when on linux when pressing SHI If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1339,11 +1339,11 @@ exports[`Command Pallet: keyboard shortcut tests when on linux when pressing SHI If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1624,11 +1624,11 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1909,11 +1909,11 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS when pressing ESC If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -2194,11 +2194,11 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS when pressing SHI If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -2491,11 +2491,11 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS when pressing SHI If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap b/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap index a61803d059..d176bb9f7d 100644 --- a/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap +++ b/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap @@ -108,11 +108,11 @@ exports[`Showing correct entity settings renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap b/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap index 56aa157f99..53dc28e961 100644 --- a/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -107,11 +107,11 @@ exports[`extensions - navigation using application menu renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/preferences/__snapshots__/navigation-using-application-menu.test.ts.snap b/packages/core/src/features/preferences/__snapshots__/navigation-using-application-menu.test.ts.snap index 8fd3eddd42..855c4c54eb 100644 --- a/packages/core/src/features/preferences/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/packages/core/src/features/preferences/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -107,11 +107,11 @@ exports[`preferences - navigation using application menu renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/preferences/__snapshots__/navigation-using-tray.test.ts.snap b/packages/core/src/features/preferences/__snapshots__/navigation-using-tray.test.ts.snap index 5a65444e9b..c0ab06204e 100644 --- a/packages/core/src/features/preferences/__snapshots__/navigation-using-tray.test.ts.snap +++ b/packages/core/src/features/preferences/__snapshots__/navigation-using-tray.test.ts.snap @@ -108,11 +108,11 @@ exports[`show-about-using-tray renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap b/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap index 72291a5587..f652074bf8 100644 --- a/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap +++ b/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap @@ -108,11 +108,11 @@ exports[`status-bar-items-originating-from-extensions when application starts wh If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/top-bar/extension-api/__snapshots__/extendability-using-extension-api.test.tsx.snap b/packages/core/src/features/top-bar/extension-api/__snapshots__/extendability-using-extension-api.test.tsx.snap index ed49317fcb..179c2bf3f1 100644 --- a/packages/core/src/features/top-bar/extension-api/__snapshots__/extendability-using-extension-api.test.tsx.snap +++ b/packages/core/src/features/top-bar/extension-api/__snapshots__/extendability-using-extension-api.test.tsx.snap @@ -108,11 +108,11 @@ exports[`extendability-using-extension-api given an extension with a weakly type If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -402,11 +402,11 @@ exports[`extendability-using-extension-api given an extension with top-bar items If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -687,11 +687,11 @@ exports[`extendability-using-extension-api given an extension with top-bar items If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -972,11 +972,11 @@ exports[`extendability-using-extension-api renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap b/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap index 610e62e091..4016a617c3 100644 --- a/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -107,11 +107,11 @@ exports[`welcome - navigation using application menu renders 1`] = ` If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

@@ -1142,11 +1142,11 @@ exports[`welcome - navigation using application menu when navigated somewhere el If you have any questions or feedback, please join our - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/main/weblinks-store/links.ts b/packages/core/src/main/weblinks-store/links.ts index ae53b6ec27..a19e7f7548 100644 --- a/packages/core/src/main/weblinks-store/links.ts +++ b/packages/core/src/main/weblinks-store/links.ts @@ -10,8 +10,8 @@ export const lensWebsiteLinkName = "Lens Website"; export const lensDocumentationWeblinkId = "lens-documentation-link"; export const lensDocumentationWeblinkName = "Lens Documentation"; -export const lensSlackWeblinkId = "lens-slack-link"; -export const lensSlackWeblinkName = "Lens Community Slack"; +export const lensForumsWeblinkId = "lens-forums-link"; +export const lensForumsWeblinkName = "Lens Forums"; export const lensTwitterWeblinkId = "lens-twitter-link"; export const lensTwitterWeblinkName = "Lens on Twitter"; diff --git a/packages/core/src/main/weblinks-store/migrations/5.1.4.injectable.ts b/packages/core/src/main/weblinks-store/migrations/5.1.4.injectable.ts index fe27b9ae3e..000937abba 100644 --- a/packages/core/src/main/weblinks-store/migrations/5.1.4.injectable.ts +++ b/packages/core/src/main/weblinks-store/migrations/5.1.4.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { docsUrl, slackUrl } from "../../../common/vars"; +import { docsUrl, forumsUrl } from "../../../common/vars"; import type { WeblinkData } from "../../../common/weblinks-store/weblink-store"; import { getInjectable } from "@ogre-tools/injectable"; import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token"; @@ -20,7 +20,7 @@ const v514WeblinkStoreMigrationInjectable = getInjectable({ weblinks.push( { id: "https://k8slens.dev", name: links.lensWebsiteLinkName, url: "https://k8slens.dev" }, { id: docsUrl, name: links.lensDocumentationWeblinkName, url: docsUrl }, - { id: slackUrl, name: links.lensSlackWeblinkName, url: slackUrl }, + { id: forumsUrl, name: links.lensForumsWeblinkName, url: forumsUrl }, { id: "https://twitter.com/k8slens", name: links.lensTwitterWeblinkName, url: "https://twitter.com/k8slens" }, { id: "https://medium.com/k8slens", name: links.lensBlogWeblinkName, url: "https://medium.com/k8slens" }, { id: "https://kubernetes.io/docs/home/", name: links.kubernetesDocumentationWeblinkName, url: "https://kubernetes.io/docs/home/" }, diff --git a/packages/core/src/main/weblinks-store/migrations/5.4.5-beta.1.injectable.ts b/packages/core/src/main/weblinks-store/migrations/5.4.5-beta.1.injectable.ts index dbe580bec3..fc67e4a2f5 100644 --- a/packages/core/src/main/weblinks-store/migrations/5.4.5-beta.1.injectable.ts +++ b/packages/core/src/main/weblinks-store/migrations/5.4.5-beta.1.injectable.ts @@ -16,40 +16,40 @@ const v545Beta1WeblinkStoreMigrationInjectable = getInjectable({ const weblinksRaw = store.get("weblinks"); const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; - const lensWebsiteLink = weblinks.find(weblink => weblink.name === links.lensWebsiteLinkName); + const lensWebsite = weblinks.find(weblink => weblink.name === links.lensWebsiteLinkName); - if (lensWebsiteLink) { - lensWebsiteLink.id = links.lensWebsiteWeblinkId; + if (lensWebsite) { + lensWebsite.id = links.lensWebsiteWeblinkId; } - const lensDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === links.lensDocumentationWeblinkName); + const lensDocumentationWeblink = weblinks.find(weblink => weblink.name === links.lensDocumentationWeblinkName); - if (lensDocumentationWeblinkLink) { - lensDocumentationWeblinkLink.id = links.lensDocumentationWeblinkId; + if (lensDocumentationWeblink) { + lensDocumentationWeblink.id = links.lensDocumentationWeblinkId; } - const lensSlackWeblinkLink = weblinks.find(weblink => weblink.name === links.lensSlackWeblinkName); + const lensForumsWeblink = weblinks.find(weblink => weblink.name === links.lensForumsWeblinkName); - if (lensSlackWeblinkLink) { - lensSlackWeblinkLink.id = links.lensSlackWeblinkId; + if (lensForumsWeblink) { + lensForumsWeblink.id = links.lensForumsWeblinkId; } - const lensTwitterWeblinkLink = weblinks.find(weblink => weblink.name === links.lensTwitterWeblinkName); + const lensTwitterWeblink = weblinks.find(weblink => weblink.name === links.lensTwitterWeblinkName); - if (lensTwitterWeblinkLink) { - lensTwitterWeblinkLink.id = links.lensTwitterWeblinkId; + if (lensTwitterWeblink) { + lensTwitterWeblink.id = links.lensTwitterWeblinkId; } - const lensBlogWeblinkLink = weblinks.find(weblink => weblink.name === links.lensBlogWeblinkName); + const lensBlogWeblink = weblinks.find(weblink => weblink.name === links.lensBlogWeblinkName); - if (lensBlogWeblinkLink) { - lensBlogWeblinkLink.id = links.lensBlogWeblinkId; + if (lensBlogWeblink) { + lensBlogWeblink.id = links.lensBlogWeblinkId; } - const kubernetesDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === links.kubernetesDocumentationWeblinkName); + const kubernetesDocumentationWeblink = weblinks.find(weblink => weblink.name === links.kubernetesDocumentationWeblinkName); - if (kubernetesDocumentationWeblinkLink) { - kubernetesDocumentationWeblinkLink.id = links.kubernetesDocumentationWeblinkId; + if (kubernetesDocumentationWeblink) { + kubernetesDocumentationWeblink.id = links.kubernetesDocumentationWeblinkId; } store.set("weblinks", weblinks); diff --git a/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts b/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts index 2e26f4996b..4b873d78c4 100644 --- a/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts +++ b/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts @@ -3,11 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { docsUrl, slackUrl } from "../../../common/vars"; +import { docsUrl, forumsUrl } from "../../../common/vars"; import type { WeblinkData } from "../../../common/weblinks-store/weblink-store"; import { getInjectable } from "@ogre-tools/injectable"; import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token"; -import { lensDocumentationWeblinkId, lensSlackWeblinkId } from "../links"; +import { lensDocumentationWeblinkId, lensForumsWeblinkId } from "../links"; import { applicationInformationToken } from "@k8slens/application"; const currentVersionWeblinkStoreMigrationInjectable = getInjectable({ @@ -20,10 +20,10 @@ const currentVersionWeblinkStoreMigrationInjectable = getInjectable({ run(store) { const weblinksRaw = store.get("weblinks"); const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; - const slackWeblink = weblinks.find(weblink => weblink.id === lensSlackWeblinkId); + const forumsWeblink = weblinks.find(weblink => weblink.id === lensForumsWeblinkId); - if (slackWeblink) { - slackWeblink.url = slackUrl; + if (forumsWeblink) { + forumsWeblink.url = forumsUrl; } const docsWeblink = weblinks.find(weblink => weblink.id === lensDocumentationWeblinkId); @@ -32,7 +32,9 @@ const currentVersionWeblinkStoreMigrationInjectable = getInjectable({ docsWeblink.url = docsUrl; } - store.set("weblinks", weblinks); + const removedSlackLink = weblinks.filter(weblink => weblink.id !== "lens-slack-link"); + + store.set("weblinks", removedSlackLink); }, }; }, diff --git a/packages/core/src/renderer/components/+welcome/welcome.tsx b/packages/core/src/renderer/components/+welcome/welcome.tsx index cf413748bc..590eaafee5 100644 --- a/packages/core/src/renderer/components/+welcome/welcome.tsx +++ b/packages/core/src/renderer/components/+welcome/welcome.tsx @@ -10,7 +10,7 @@ import type { IComputedValue } from "mobx"; import type { CarouselProps } from "react-material-ui-carousel"; import LegacyCarousel from "react-material-ui-carousel"; import { Icon } from "../icon"; -import { slackUrl } from "../../../common/vars"; +import { forumsUrl } from "../../../common/vars"; import { withInjectables } from "@ogre-tools/injectable-react"; import welcomeMenuItemsInjectable from "./welcome-menu-items/welcome-menu-items.injectable"; import type { WelcomeMenuRegistration } from "./welcome-menu-items/welcome-menu-registration"; @@ -93,12 +93,12 @@ const NonInjectedWelcome = observer(({
{"If you have any questions or feedback, please join our "} - Lens Community slack channel + Lens Forums .

diff --git a/packages/core/src/renderer/components/error-boundary/error-boundary.tsx b/packages/core/src/renderer/components/error-boundary/error-boundary.tsx index 75e5cf4689..6e39867109 100644 --- a/packages/core/src/renderer/components/error-boundary/error-boundary.tsx +++ b/packages/core/src/renderer/components/error-boundary/error-boundary.tsx @@ -9,7 +9,7 @@ import type { ErrorInfo } from "react"; import React from "react"; import { observer } from "mobx-react"; import { Button } from "../button"; -import { issuesTrackerUrl, slackUrl } from "../../../common/vars"; +import { issuesTrackerUrl, forumsUrl } from "../../../common/vars"; import type { SingleOrMany } from "../../utils"; import type { ObservableHistory } from "mobx-observable-history"; import { withInjectables } from "@ogre-tools/injectable-react"; @@ -53,15 +53,15 @@ class NonInjectedErrorBoundary extends React.Component

- {"To help us improve the product please report bugs to "} + {"To help us improve the product please report bugs on"} - Slack + Lens Forums - {" community or "} + {" or on our"} Date: Mon, 27 Feb 2023 14:30:30 +0700 Subject: [PATCH 10/21] Throw on errors in kubectlApplyFolder (#7239) Signed-off-by: Panu Horsmalahti --- .../__tests__/create-resource-stack.test.ts | 61 +++++++++++++++++++ .../core/src/common/k8s/resource-stack.ts | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/common/__tests__/create-resource-stack.test.ts diff --git a/packages/core/src/common/__tests__/create-resource-stack.test.ts b/packages/core/src/common/__tests__/create-resource-stack.test.ts new file mode 100644 index 0000000000..340e5c86ee --- /dev/null +++ b/packages/core/src/common/__tests__/create-resource-stack.test.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { DiContainer } from "@ogre-tools/injectable"; +import kubectlApplyAllInjectable from "../../main/kubectl/kubectl-apply-all.injectable"; +import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; +import type { KubernetesCluster } from "../catalog-entities"; +import readDirectoryInjectable from "../fs/read-directory.injectable"; +import readFileInjectable from "../fs/read-file.injectable"; +import createResourceStackInjectable from "../k8s/create-resource-stack.injectable"; +import appPathsStateInjectable from "../app-paths/app-paths-state.injectable"; +import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; + +describe("create resource stack tests", () => { + let di: DiContainer; + let cluster: KubernetesCluster; + + beforeEach(async () => { + di = getDiForUnitTesting({ doGeneralOverrides: true }); + cluster = { + getId: () => "test-cluster", + } as any; + + di.override(readDirectoryInjectable, () => () => Promise.resolve(["file1"]) as any); + di.override(readFileInjectable, () => () => Promise.resolve("filecontents")); + di.override(appPathsStateInjectable, () => ({ + get: () => ({}), + })); + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); + + }); + + describe("kubectlApplyFolder", () => { + it("returns response", async () => { + di.override(kubectlApplyAllInjectable, () => () => Promise.resolve({ + callWasSuccessful: true as const, + response: "success", + })); + + const createResourceStack = di.inject(createResourceStackInjectable); + const resourceStack = createResourceStack(cluster, "test"); + + const response = await resourceStack.kubectlApplyFolder("/foo/bar"); + + expect(response).toEqual("success"); + }); + + it("throws on error", async () => { + di.override(kubectlApplyAllInjectable, () => () => Promise.resolve({ + callWasSuccessful: false as const, + error: "No permissions", + })); + + const createResourceStack = di.inject(createResourceStackInjectable); + const resourceStack = createResourceStack(cluster, "test"); + + await expect(() => resourceStack.kubectlApplyFolder("/foo/bar")).rejects.toThrow("No permissions"); + }); + }); +}); diff --git a/packages/core/src/common/k8s/resource-stack.ts b/packages/core/src/common/k8s/resource-stack.ts index 771b48b413..5bdafdc58d 100644 --- a/packages/core/src/common/k8s/resource-stack.ts +++ b/packages/core/src/common/k8s/resource-stack.ts @@ -51,7 +51,7 @@ export class ResourceStack { this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to apply resources: ${result.error}`); - return ""; + throw new Error(result.error); } /** From 003dc44681437029f106f2445fa124616ca6af9f Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Mon, 27 Feb 2023 21:27:36 +0200 Subject: [PATCH 11/21] Upgrade and adapt to new version of libraries in ogre-tools (#7241) * Update injectable version Signed-off-by: Janne Savolainen * Adapt to new version of ogre-tools Signed-off-by: Janne Savolainen * Fix "lint:fix" -command in core Signed-off-by: Janne Savolainen --------- Signed-off-by: Janne Savolainen --- package-lock.json | 72 +++++++++---------- packages/core/package.json | 12 ++-- .../common/__tests__/cluster-store.test.ts | 20 +++++- .../src/common/__tests__/user-store.test.ts | 6 +- .../k8s-api/__tests__/api-manager.test.ts | 4 +- .../kube-api-version-detection.test.ts | 4 +- .../common/k8s-api/__tests__/kube-api.test.ts | 6 +- .../src/common/utils/channel/channel.test.ts | 36 +++++++--- .../with-orphan-promise.injectable.ts | 2 +- .../catalog/opening-entity-details.test.tsx | 20 +++--- ...-settings-for-correct-entity.test.tsx.snap | 35 ++++++++- ...owing-settings-for-correct-entity.test.tsx | 19 ++--- .../core/src/main/register-injectables.ts | 3 +- .../kube-object-list-layout.test.tsx | 4 +- .../core/src/renderer/getDiForUnitTesting.tsx | 2 + .../core/src/renderer/register-injectables.ts | 8 ++- packages/open-lens/package.json | 10 +-- packages/open-lens/src/main/index.ts | 3 +- packages/open-lens/src/renderer/index.ts | 3 +- .../application/package.json | 4 +- 20 files changed, 176 insertions(+), 97 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1889b6304..2cfc549628 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4651,55 +4651,55 @@ } }, "node_modules/@ogre-tools/fp": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@ogre-tools/fp/-/fp-12.0.1.tgz", - "integrity": "sha512-BzMhkI4wPnuI+hXJDbtHUXQn/uBjJLx3W0oDaIFV+lLpkneUU0oW9D5uZFHNOouzCgf67/tnmUC6Ohevbr7/VA==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@ogre-tools/fp/-/fp-15.1.1.tgz", + "integrity": "sha512-WuLl0lBFjMHcy6o+HZLw2eN9zSUx6210DqLbhjo110PtpMvXqzQOIfmIiKv+awKxs7F2lIj1QUUJ6PpxCXVWSg==", "peerDependencies": { "lodash": "^4.17.21" } }, "node_modules/@ogre-tools/injectable": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@ogre-tools/injectable/-/injectable-12.0.1.tgz", - "integrity": "sha512-uOx8STN2wSc9hknDSTGqViyR89Vwg7rGacwrVNchgyo48/QJsmZZz6cd1Aw3nT4vr7ekjTc2lh0Rz6zGIv47hg==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@ogre-tools/injectable/-/injectable-15.1.1.tgz", + "integrity": "sha512-koB4z1FkaRbTEW77ULK1viVORlBCDnUtxAhxYiZrUzQcCvd7Fi4izs/YzDWLPc2HHay+EdJw11CuNC1JfzhaaA==", "peerDependencies": { - "@ogre-tools/fp": "^12.0.0", + "@ogre-tools/fp": "*", "lodash": "^4.17.21" } }, "node_modules/@ogre-tools/injectable-extension-for-auto-registration": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-12.0.1.tgz", - "integrity": "sha512-itKcxEJ/J8SKGD/Wwj0UYOA+/nqwnrwanhikY6qhlibj8guujX77Iip7vMBzJFc2nIrRaQRcpNV2eXe+tjQUdg==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-15.1.1.tgz", + "integrity": "sha512-kByRoG1FTWnB412nkF4GnKzim1ldLbSd9H2PUR6UF0EmjPg3QstyZXSE341bWlWZMAi/1HPiagfZ9E1wOP609w==", "peerDependencies": { - "@ogre-tools/fp": "^12.0.0", - "@ogre-tools/injectable": "^12.0.0", + "@ogre-tools/fp": "*", + "@ogre-tools/injectable": "*", "lodash": "^4.17.21" } }, "node_modules/@ogre-tools/injectable-extension-for-mobx": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-12.0.1.tgz", - "integrity": "sha512-M1penOpZfO3/rJMb6WN4IL86p9Lx9tOMCipiNkAyitNLGWfeDPG279JlCs9E3Uw8R9nkFPiw8Je2SLnwnM9o+A==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-15.1.1.tgz", + "integrity": "sha512-ZdIZGG9Zr/okGktICQFY5PzENerjdNAlwvuP1Na8bmIHJAs7yEwi6KlSuoOkZ1oNvQcHAsi9V2WVBh8jGyHN8g==", "peerDependencies": { - "@ogre-tools/fp": "^12.0.0", - "@ogre-tools/injectable": "^12.0.0", + "@ogre-tools/fp": "*", + "@ogre-tools/injectable": "*", "lodash": "^4.17.21", "mobx": "^6.3.0" } }, "node_modules/@ogre-tools/injectable-react": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@ogre-tools/injectable-react/-/injectable-react-12.0.1.tgz", - "integrity": "sha512-LAOh/EHKqk/pQcBRZUAz0VcJwgBeIPxHwlV/Apw0aEBBoMuYLsLZh47rES8sMYMV6N5x7oVfkjMscujY0DCgaQ==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@ogre-tools/injectable-react/-/injectable-react-15.1.1.tgz", + "integrity": "sha512-nJ1mH3FsL+9WbiWoIbs955rKONf/0jkw4UmEM2dBdi5dQ7G6MCZu/lh4sTcPm5u3g6ZoV7o6rUZjkwkM1qUcZw==", "peerDependencies": { - "@ogre-tools/fp": "^12.0.0", - "@ogre-tools/injectable": "^12.0.0", + "@ogre-tools/fp": "*", + "@ogre-tools/injectable": "*", "lodash": "^4.17.21", "mobx": "^6.3.0", "mobx-react": "^7.2.0", - "react": "^17.0.0", - "react-dom": "^17.0.0" + "react": "^17 || ^18", + "react-dom": "^17 || ^18" } }, "node_modules/@parcel/watcher": { @@ -32266,11 +32266,11 @@ "@k8slens/node-fetch": "^6.4.0-beta.13", "@kubernetes/client-node": "^0.18.1", "@material-ui/styles": "^4.11.5", - "@ogre-tools/fp": "^12.0.1", - "@ogre-tools/injectable": "^12.0.1", - "@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1", - "@ogre-tools/injectable-extension-for-mobx": "^12.0.1", - "@ogre-tools/injectable-react": "^12.0.1", + "@ogre-tools/fp": "^15.1.1", + "@ogre-tools/injectable": "^15.1.1", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.1", + "@ogre-tools/injectable-extension-for-mobx": "^15.1.1", + "@ogre-tools/injectable-react": "^15.1.1", "@sentry/electron": "^3.0.8", "@sentry/integrations": "^6.19.3", "@side/jest-runtime": "^1.1.0", @@ -34297,11 +34297,11 @@ "@k8slens/core": "^6.4.0-beta.13", "@k8slens/ensure-binaries": "^6.4.0-beta.13", "@k8slens/generate-tray-icons": "^6.4.0-beta.13", - "@ogre-tools/fp": "^12.0.1", - "@ogre-tools/injectable": "^12.0.1", - "@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1", - "@ogre-tools/injectable-extension-for-mobx": "^12.0.1", - "@ogre-tools/injectable-react": "^12.0.1", + "@ogre-tools/fp": "^15.1.1", + "@ogre-tools/injectable": "^15.1.1", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.1", + "@ogre-tools/injectable-extension-for-mobx": "^15.1.1", + "@ogre-tools/injectable-react": "^15.1.1", "mobx": "^6.8.0", "rimraf": "^4.1.2" }, @@ -34807,8 +34807,8 @@ "version": "6.4.0-beta.13", "license": "MIT", "peerDependencies": { - "@ogre-tools/fp": "^12.0.1", - "@ogre-tools/injectable": "^12.0.1", + "@ogre-tools/fp": "^15.1.1", + "@ogre-tools/injectable": "^15.1.1", "lodash": "^4.17.15" } } diff --git a/packages/core/package.json b/packages/core/package.json index 476100d9bf..83fd3629ce 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -57,7 +57,7 @@ "test:unit": "jest --testPathIgnorePatterns integration", "test:watch": "func() { jest ${1} --watch --testPathIgnorePatterns integration; }; func", "lint": "PROD=true eslint --ext js,ts,tsx --max-warnings=0 .", - "lint:fix": "npm run lint --fix" + "lint:fix": "npm run lint -- --fix" }, "config": { "k8sProxyVersion": "0.3.0", @@ -130,11 +130,11 @@ "@k8slens/node-fetch": "^6.4.0-beta.13", "@kubernetes/client-node": "^0.18.1", "@material-ui/styles": "^4.11.5", - "@ogre-tools/fp": "^12.0.1", - "@ogre-tools/injectable": "^12.0.1", - "@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1", - "@ogre-tools/injectable-extension-for-mobx": "^12.0.1", - "@ogre-tools/injectable-react": "^12.0.1", + "@ogre-tools/fp": "^15.1.1", + "@ogre-tools/injectable": "^15.1.1", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.1", + "@ogre-tools/injectable-extension-for-mobx": "^15.1.1", + "@ogre-tools/injectable-react": "^15.1.1", "@sentry/electron": "^3.0.8", "@sentry/integrations": "^6.19.3", "@side/jest-runtime": "^1.1.0", diff --git a/packages/core/src/common/__tests__/cluster-store.test.ts b/packages/core/src/common/__tests__/cluster-store.test.ts index 9dd1a29a5b..bbb6457e43 100644 --- a/packages/core/src/common/__tests__/cluster-store.test.ts +++ b/packages/core/src/common/__tests__/cluster-store.test.ts @@ -74,8 +74,7 @@ describe("cluster-store", () => { di.override(kubectlBinaryNameInjectable, () => "kubectl"); di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); di.override(normalizedPlatformInjectable, () => "darwin"); - createCluster = di.inject(createClusterInjectionToken); - getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); + writeJsonSync = di.inject(writeJsonSyncInjectable); writeFileSync = di.inject(writeFileSyncInjectable); writeBufferSync = di.inject(writeBufferSyncInjectable); @@ -85,6 +84,9 @@ describe("cluster-store", () => { describe("empty config", () => { beforeEach(async () => { + createCluster = di.inject(createClusterInjectionToken); + getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); + writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {}); clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); @@ -198,6 +200,10 @@ describe("cluster-store", () => { }, ], }); + + createCluster = di.inject(createClusterInjectionToken); + getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); + clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); }); @@ -249,6 +255,10 @@ describe("cluster-store", () => { }, ], }); + + createCluster = di.inject(createClusterInjectionToken); + getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); + clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); }); @@ -262,6 +272,11 @@ describe("cluster-store", () => { describe("pre 3.6.0-beta.1 config with an existing cluster", () => { beforeEach(() => { + di.override(storeMigrationVersionInjectable, () => "3.6.0"); + + createCluster = di.inject(createClusterInjectionToken); + getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); + writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { __internal__: { migrations: { @@ -281,7 +296,6 @@ describe("cluster-store", () => { }); writeBufferSync("/some-directory-for-user-data/icon_path", testDataIcon); - di.override(storeMigrationVersionInjectable, () => "3.6.0"); clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); diff --git a/packages/core/src/common/__tests__/user-store.test.ts b/packages/core/src/common/__tests__/user-store.test.ts index 7071fc5c17..486bb31bef 100644 --- a/packages/core/src/common/__tests__/user-store.test.ts +++ b/packages/core/src/common/__tests__/user-store.test.ts @@ -30,9 +30,9 @@ describe("user store tests", () => { get: () => "latest" as const, init: async () => {}, })); + await di.inject(defaultUpdateChannelInjectable).init(); - userStore = di.inject(userStoreInjectable); }); describe("for an empty config", () => { @@ -42,6 +42,8 @@ describe("user store tests", () => { writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {}); writeJsonSync("/some-directory-for-user-data/kube_config", {}); + userStore = di.inject(userStoreInjectable); + userStore.load(); }); @@ -90,6 +92,8 @@ describe("user store tests", () => { di.override(storeMigrationVersionInjectable, () => "10.0.0"); + userStore = di.inject(userStoreInjectable); + userStore.load(); }); diff --git a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts index 2e7e545f76..bec3cff646 100644 --- a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts @@ -4,7 +4,6 @@ */ import type { DiContainer } from "@ogre-tools/injectable"; -import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable"; import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; @@ -18,6 +17,7 @@ import { KubeApi } from "../kube-api"; import { KubeObject } from "../kube-object"; import { KubeObjectStore } from "../kube-object.store"; import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; +import { createClusterInjectionToken } from "../../cluster/create-cluster-injection-token"; // eslint-disable-next-line no-restricted-imports import { KubeApi as ExternalKubeApi } from "../../../extensions/common-api/k8s-api"; @@ -43,7 +43,7 @@ describe("ApiManager", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); - const createCluster = di.inject(createClusterInjectable); + const createCluster = di.inject(createClusterInjectionToken); di.override(hostedClusterInjectable, () => createCluster({ contextName: "some-context-name", diff --git a/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts b/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts index dd65f58762..16c2ae0e04 100644 --- a/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts @@ -15,7 +15,6 @@ import setupAutoRegistrationInjectable from "../../../renderer/before-frame-star import { createMockResponseFromString } from "../../../test-utils/mock-responses"; import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import apiManagerInjectable from "../api-manager/manager.injectable"; @@ -23,6 +22,7 @@ import type { DiContainer } from "@ogre-tools/injectable"; import ingressApiInjectable from "../endpoints/ingress.api.injectable"; import loggerInjectable from "../../logger.injectable"; import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; +import { createClusterInjectionToken } from "../../cluster/create-cluster-injection-token"; describe("KubeApi", () => { let fetchMock: AsyncFnMock; @@ -39,7 +39,7 @@ describe("KubeApi", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); - const createCluster = di.inject(createClusterInjectable); + const createCluster = di.inject(createClusterInjectionToken); di.override(hostedClusterInjectable, () => createCluster({ contextName: "some-context-name", diff --git a/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts b/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts index 7bce8a3b7c..c5b0364da9 100644 --- a/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts @@ -24,7 +24,6 @@ import setupAutoRegistrationInjectable from "../../../renderer/before-frame-star import { createMockResponseFromStream, createMockResponseFromString } from "../../../test-utils/mock-responses"; import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable"; @@ -36,6 +35,7 @@ import namespaceApiInjectable from "../endpoints/namespace.api.injectable"; // NOTE: this is fine because we are testing something that only exported // eslint-disable-next-line no-restricted-imports import { PodsApi } from "../../../extensions/common-api/k8s-api"; +import { createClusterInjectionToken } from "../../cluster/create-cluster-injection-token"; describe("createKubeApiForRemoteCluster", () => { let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster; @@ -48,7 +48,7 @@ describe("createKubeApiForRemoteCluster", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); - const createCluster = di.inject(createClusterInjectable); + const createCluster = di.inject(createClusterInjectionToken); di.override(hostedClusterInjectable, () => createCluster({ contextName: "some-context-name", @@ -154,7 +154,7 @@ describe("KubeApi", () => { fetchMock = asyncFn(); di.override(fetchInjectable, () => fetchMock); - const createCluster = di.inject(createClusterInjectable); + const createCluster = di.inject(createClusterInjectionToken); const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); di.override(hostedClusterInjectable, () => createCluster({ diff --git a/packages/core/src/common/utils/channel/channel.test.ts b/packages/core/src/common/utils/channel/channel.test.ts index 9a361b6770..72204d4fb6 100644 --- a/packages/core/src/common/utils/channel/channel.test.ts +++ b/packages/core/src/common/utils/channel/channel.test.ts @@ -19,7 +19,10 @@ import asyncFn from "@async-fn/jest"; import { getPromiseStatus } from "../../test-utils/get-promise-status"; import { runInAction } from "mobx"; import type { RequestChannelHandler } from "../../../main/utils/channel/channel-listeners/listener-tokens"; -import { getRequestChannelListenerInjectable } from "../../../main/utils/channel/channel-listeners/listener-tokens"; +import { + getRequestChannelListenerInjectable, + requestChannelListenerInjectionToken, +} from "../../../main/utils/channel/channel-listeners/listener-tokens"; type TestMessageChannel = MessageChannel; type TestRequestChannel = RequestChannel; @@ -199,21 +202,32 @@ describe("channel", () => { it("when registering multiple handlers for the same channel, throws", async () => { const applicationBuilder = getApplicationBuilder(); - const testChannelListenerInMainInjectable = getRequestChannelListenerInjectable({ - channel: testRequestChannel, - handler: () => () => "some-value", - }); - const testChannelListenerInMain2Injectable = getRequestChannelListenerInjectable({ - channel: testRequestChannel, - handler: () => () => "some-other-value", + const someChannelListenerInjectable = getInjectable({ + id: "some-channel-listener", + + instantiate: () => ({ + channel: testRequestChannel, + handler: () => () => "irrelevant", + }), + + injectionToken: requestChannelListenerInjectionToken, }); - testChannelListenerInMain2Injectable.id += "2"; + const someOtherChannelListenerInjectable = getInjectable({ + id: "some-other-channel-listener", + + instantiate: () => ({ + channel: testRequestChannel, + handler: () => () => "irrelevant", + }), + + injectionToken: requestChannelListenerInjectionToken, + }); applicationBuilder.beforeApplicationStart((mainDi) => { runInAction(() => { - mainDi.register(testChannelListenerInMainInjectable); - mainDi.register(testChannelListenerInMain2Injectable); + mainDi.register(someChannelListenerInjectable); + mainDi.register(someOtherChannelListenerInjectable); }); }); diff --git a/packages/core/src/common/utils/with-orphan-promise/with-orphan-promise.injectable.ts b/packages/core/src/common/utils/with-orphan-promise/with-orphan-promise.injectable.ts index 42e6cb9a61..2a005001da 100644 --- a/packages/core/src/common/utils/with-orphan-promise/with-orphan-promise.injectable.ts +++ b/packages/core/src/common/utils/with-orphan-promise/with-orphan-promise.injectable.ts @@ -19,7 +19,7 @@ const withOrphanPromiseInjectable = getInjectable({ toBeDecorated, withErrorLoggingFor(() => "Orphan promise rejection encountered"), withErrorSuppression, - ); + ) as ((...args: any[]) => any); decorated(...args); }; diff --git a/packages/core/src/features/catalog/opening-entity-details.test.tsx b/packages/core/src/features/catalog/opening-entity-details.test.tsx index 8ae49ff548..cb36218dbb 100644 --- a/packages/core/src/features/catalog/opening-entity-details.test.tsx +++ b/packages/core/src/features/catalog/opening-entity-details.test.tsx @@ -25,6 +25,17 @@ describe("opening catalog entity details panel", () => { beforeEach(async () => { builder = getApplicationBuilder(); + builder.beforeWindowStart((windowDi) => { + // TODO: remove once ClusterStore can be used without overriding it + windowDi.override(getClusterByIdInjectable, () => (clusterId) => { + if (clusterId === cluster.id) { + return cluster; + } + + return undefined; + }); + }); + builder.afterWindowStart((windowDi) => { const createCluster = windowDi.inject(createClusterInjectable); @@ -78,15 +89,6 @@ describe("opening catalog entity details panel", () => { clusterServerUrl: "https://localhost:9999", }); - // TODO: remove once ClusterStore can be used without overriding it - windowDi.override(getClusterByIdInjectable, () => (clusterId) => { - if (clusterId === cluster.id) { - return cluster; - } - - return undefined; - }); - // TODO: replace with proper entity source once syncing entities between main and windows is injectable const catalogEntityRegistry = windowDi.inject(catalogEntityRegistryInjectable); diff --git a/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap b/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap index d176bb9f7d..bb30b0dc71 100644 --- a/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap +++ b/packages/core/src/features/entity-settings/__snapshots__/showing-settings-for-correct-entity.test.tsx.snap @@ -830,7 +830,40 @@ exports[`Showing correct entity settings when navigating to non-local cluster en > Proxy -

+
+
+
+ HTTP Proxy + +
+
+ +
+
+ + HTTP Proxy server. Used for communicating with Kubernetes API. + +
+
{ beforeEach(async () => { builder = getApplicationBuilder(); + builder.beforeWindowStart((windowDi) => { + // TODO: remove once ClusterStore can be used without overriding it + windowDi.override(getClusterByIdInjectable, () => (clusterId) => { + if (clusterId === cluster.id) { + return cluster; + } + + return undefined; + }); + }); + builder.afterWindowStart((windowDi) => { const createCluster = windowDi.inject(createClusterInjectable); @@ -78,14 +89,6 @@ describe("Showing correct entity settings", () => { clusterServerUrl: "https://localhost:9999", }); - // TODO: remove once ClusterStore can be used without overriding it - windowDi.override(getClusterByIdInjectable, () => (clusterId) => { - if (clusterId === cluster.id) { - return cluster; - } - - return undefined; - }); // TODO: replace with proper entity source once syncing entities between main and windows is injectable const catalogEntityRegistry = windowDi.inject(catalogEntityRegistryInjectable); diff --git a/packages/core/src/main/register-injectables.ts b/packages/core/src/main/register-injectables.ts index 6d0117c512..5ddcb640c1 100644 --- a/packages/core/src/main/register-injectables.ts +++ b/packages/core/src/main/register-injectables.ts @@ -16,7 +16,8 @@ export function registerInjectables(di: DiContainer) { autoRegister({ di, - requireContexts: [ + targetModule: module, + getRequireContexts: () => [ require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), require.context("../extensions", true, CONTEXT_MATCHER_FOR_NON_FEATURES), require.context("../common", true, CONTEXT_MATCHER_FOR_NON_FEATURES), diff --git a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx index d50d70c723..356b3ee34d 100644 --- a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx +++ b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx @@ -19,8 +19,8 @@ import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-create import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; import type { PodStore } from "../+workloads-pods/store"; +import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token"; describe("kube-object-list-layout", () => { let di: DiContainer; @@ -34,7 +34,7 @@ describe("kube-object-list-layout", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); - const createCluster = di.inject(createClusterInjectable); + const createCluster = di.inject(createClusterInjectionToken); di.override(hostedClusterInjectable, () => createCluster({ contextName: "some-context-name", diff --git a/packages/core/src/renderer/getDiForUnitTesting.tsx b/packages/core/src/renderer/getDiForUnitTesting.tsx index 07cc70ef80..15ddef9804 100644 --- a/packages/core/src/renderer/getDiForUnitTesting.tsx +++ b/packages/core/src/renderer/getDiForUnitTesting.tsx @@ -20,6 +20,7 @@ import legacyOnChannelListenInjectable from "./ipc/legacy-channel-listen.injecta import type { GlobalOverride } from "../common/test-utils/get-global-override"; import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; import { applicationInformationFakeInjectable } from "../common/vars/application-information-fake-injectable"; +import { registerInjectableReact } from "@ogre-tools/injectable-react"; export const getDiForUnitTesting = ( opts: { doGeneralOverrides?: boolean } = {}, @@ -46,6 +47,7 @@ export const getDiForUnitTesting = ( ) as Injectable[]; registerMobX(di); + registerInjectableReact(di); runInAction(() => { di.register(applicationInformationFakeInjectable); diff --git a/packages/core/src/renderer/register-injectables.ts b/packages/core/src/renderer/register-injectables.ts index d801e04625..41724d9413 100644 --- a/packages/core/src/renderer/register-injectables.ts +++ b/packages/core/src/renderer/register-injectables.ts @@ -6,18 +6,22 @@ import type { DiContainer } from "@ogre-tools/injectable"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; +import { registerInjectableReact } from "@ogre-tools/injectable-react"; import { runInAction } from "mobx"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; export function registerInjectables(di: DiContainer) { setLegacyGlobalDiForExtensionApi(di, Environments.renderer); + registerMobX(di); + registerInjectableReact(di); + runInAction(() => { - registerMobX(di); autoRegister({ di, - requireContexts: [ + targetModule: module, + getRequireContexts: () => [ require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), require.context("../common", true, CONTEXT_MATCHER_FOR_NON_FEATURES), require.context("../extensions", true, CONTEXT_MATCHER_FOR_NON_FEATURES), diff --git a/packages/open-lens/package.json b/packages/open-lens/package.json index 8507be2395..d47b0f1c34 100644 --- a/packages/open-lens/package.json +++ b/packages/open-lens/package.json @@ -199,11 +199,11 @@ "@k8slens/core": "^6.4.0-beta.13", "@k8slens/ensure-binaries": "^6.4.0-beta.13", "@k8slens/generate-tray-icons": "^6.4.0-beta.13", - "@ogre-tools/fp": "^12.0.1", - "@ogre-tools/injectable": "^12.0.1", - "@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1", - "@ogre-tools/injectable-extension-for-mobx": "^12.0.1", - "@ogre-tools/injectable-react": "^12.0.1", + "@ogre-tools/fp": "^15.1.1", + "@ogre-tools/injectable": "^15.1.1", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.1", + "@ogre-tools/injectable-extension-for-mobx": "^15.1.1", + "@ogre-tools/injectable-react": "^15.1.1", "mobx": "^6.8.0", "rimraf": "^4.1.2" }, diff --git a/packages/open-lens/src/main/index.ts b/packages/open-lens/src/main/index.ts index d806e9fe49..aca3da2bb4 100644 --- a/packages/open-lens/src/main/index.ts +++ b/packages/open-lens/src/main/index.ts @@ -13,7 +13,8 @@ runInAction(() => { try { autoRegister({ di, - requireContexts: [ + targetModule: module, + getRequireContexts: () => [ require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), require.context("../common", true, CONTEXT_MATCHER_FOR_NON_FEATURES), ], diff --git a/packages/open-lens/src/renderer/index.ts b/packages/open-lens/src/renderer/index.ts index c1ebf0f588..2774644e94 100644 --- a/packages/open-lens/src/renderer/index.ts +++ b/packages/open-lens/src/renderer/index.ts @@ -13,7 +13,8 @@ const app = createApp({ runInAction(() => { autoRegister({ di, - requireContexts: [ + targetModule: module, + getRequireContexts: () => [ require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), require.context("../common", true, CONTEXT_MATCHER_FOR_NON_FEATURES), ], diff --git a/packages/technical-features/application/package.json b/packages/technical-features/application/package.json index a78de4da0e..44f922ec47 100644 --- a/packages/technical-features/application/package.json +++ b/packages/technical-features/application/package.json @@ -29,8 +29,8 @@ "test": "jest --coverage --runInBand" }, "peerDependencies": { - "@ogre-tools/fp": "^12.0.1", - "@ogre-tools/injectable": "^12.0.1", + "@ogre-tools/fp": "^15.1.1", + "@ogre-tools/injectable": "^15.1.1", "lodash": "^4.17.15" } } From d29615c089aeb93a130db67fc5d7adc95c4105a6 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 27 Feb 2023 13:31:45 -0800 Subject: [PATCH 12/21] Quick fix for store migration version being wrong (#7243) Signed-off-by: Sebastian Malton --- .../core/src/common/vars/store-migration-version.injectable.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/common/vars/store-migration-version.injectable.ts b/packages/core/src/common/vars/store-migration-version.injectable.ts index 6dfbff2e94..37169463ae 100644 --- a/packages/core/src/common/vars/store-migration-version.injectable.ts +++ b/packages/core/src/common/vars/store-migration-version.injectable.ts @@ -2,12 +2,11 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { applicationInformationToken } from "@k8slens/application"; import { getInjectable } from "@ogre-tools/injectable"; const storeMigrationVersionInjectable = getInjectable({ id: "store-migration-version", - instantiate: (di) => di.inject(applicationInformationToken).version, + instantiate: () => "6.4.0", }); export default storeMigrationVersionInjectable; From 1b808cf7df060ba777f3aeb905d38a719eff2daf Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 28 Feb 2023 04:56:56 -0800 Subject: [PATCH 13/21] General fixes for release-tool (#7238) * General fixes for release-tool Signed-off-by: Sebastian Malton * Revert change to number of PRs retrieved Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- packages/release-tool/src/index.ts | 137 +++++++++++++++-------------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/packages/release-tool/src/index.ts b/packages/release-tool/src/index.ts index 7a4ce49a27..8c1e465ffd 100755 --- a/packages/release-tool/src/index.ts +++ b/packages/release-tool/src/index.ts @@ -5,7 +5,7 @@ */ import assert from "assert"; import chalk from "chalk"; -import child_process from "child_process"; +import child_process, { spawn } from "child_process"; import { readFile } from "fs/promises"; import inquirer from "inquirer"; import { createInterface, ReadLine } from "readline"; @@ -16,9 +16,21 @@ type SemVer = semver.SemVer; const { SemVer } = semver; const exec = promisify(child_process.exec); -const spawn = promisify(child_process.spawn); const execFile = promisify(child_process.execFile); +async function pipeExecFile(file: string, args: string[], opts?: { stdin: string }) { + const p = execFile(file, args); + + p.child.stdout?.pipe(process.stdout); + p.child.stderr?.pipe(process.stderr); + + if (opts) { + p.child.stdin?.end(opts.stdin); + } + + await p; +} + interface GithubPrData { author: { login: string; @@ -65,9 +77,35 @@ async function fetchAllGitTags(): Promise { .map(line => line.trim()); } -async function bumpPackageVersions(): Promise { - await spawn("npm", ["run", "bump-version"], { - stdio: "inherit", +function bumpPackageVersions() { + const bumpPackages = spawn("npm", ["run", "bump-version"], { + stdio: "inherit" + }); + const cleaners: (() => void)[] = [ + () => bumpPackages.stdout?.unpipe(), + () => bumpPackages.stderr?.unpipe(), + ]; + const cleanup = () => cleaners.forEach(clean => clean()); + + return new Promise((resolve, reject) => { + const onExit = (code: number | null) => { + cleanup(); + if (code) { + reject(new Error(`"npm run bump-version" failed with code ${code}`)); + } else { + resolve(); + } + }; + const onError = (error: Error) => { + cleanup(); + reject(error); + }; + + bumpPackages.once("error", onError); + cleaners.push(() => bumpPackages.off("error", onError)); + + bumpPackages.once("exit", onExit); + cleaners.push(() => bumpPackages.off("exit", onExit)); }); } @@ -110,20 +148,12 @@ function formatSemverForMilestone(version: SemVer): string { async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prBody: string): Promise { const prBranch = `release/v${version.format()}`; - await spawn("git", ["checkout", "-b", prBranch], { - stdio: "inherit", - }); - await spawn("git", ["add", "lerna.json", "packages/*/package.json"], { - stdio: "inherit", - }); - await spawn("git", ["commit", "-sm", `"Release ${version.format()}"`], { - stdio: "inherit", - }); - await spawn("git", ["push", "--set-upstream", "origin", prBranch], { - stdio: "inherit", - }); + await pipeExecFile("git", ["checkout", "-b", prBranch]); + await pipeExecFile("git", ["add", "lerna.json", "packages/*/package.json"]); + await pipeExecFile("git", ["commit", "-sm", `Release ${version.format()}`]); + await pipeExecFile("git", ["push", "--set-upstream", "origin", prBranch]); - await spawn("gh", [ + await pipeExecFile("gh", [ "pr", "create", "--base", prBase, @@ -131,9 +161,9 @@ async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prB "--label", "skip-changelog", "--label", "release", "--milestone", formatSemverForMilestone(version), - "--body-file", prBody, + "--body-file", "-", ], { - stdio: "inherit" + stdin: prBody, }); } @@ -153,7 +183,7 @@ function sortExtendedGithubPrData(left: ExtendedGithubPrData, right: ExtendedGit } async function getRelevantPRs(milestone: string, previousReleasedVersion: string): Promise { - console.log("retreiving previous 500 PRs..."); + console.log("retrieving previous 200 PRs..."); const getMergedPrsArgs = [ "gh", @@ -167,14 +197,14 @@ async function getRelevantPRs(milestone: string, previousReleasedVersion: string const mergedPrs = JSON.parse((await exec(getMergedPrsArgs.join(" "), { encoding: "utf-8" })).stdout) as GithubPrData[]; const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone?.title === milestone); - const relaventPrsQuery = await Promise.all( + const relevantPrsQuery = await Promise.all( milestoneRelevantPrs.map(async pr => ({ pr, stdout: (await exec(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout, })), ); - return relaventPrsQuery + return relevantPrsQuery .filter(query => query.stdout) .map(query => query.pr) .filter(pr => pr.labels.every(label => label.name !== "skip-changelog")) @@ -189,40 +219,11 @@ function formatPrEntry(pr: ExtendedGithubPrData) { const isEnhancementPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "enhancement"); const isBugfixPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "bug"); -const cherrypickCommitWith = (rl: ReadLine) => async (commit: string) => { +const cherryPickCommitWith = (rl: ReadLine) => async (commit: string) => { try { - const cherryPick = child_process.spawn("git", ["cherry-pick", commit]); - - cherryPick.stdout.pipe(process.stdout); - cherryPick.stderr.pipe(process.stderr); - - await new Promise((resolve, reject) => { - const cleaners: (() => void)[] = []; - const cleanup = () => cleaners.forEach(cleaner => cleaner()); - - const onExit = (code: number | null) => { - if (code) { - reject(new Error(`git cherry-pick failed with exit code ${code}`)); - cleanup(); - } - - resolve(); - cleanup(); - }; - - cherryPick.once("exit", onExit); - cleaners.push(() => cherryPick.off("exit", onExit)); - - const onError = (error: Error) => { - cleanup(); - reject(error); - }; - - cherryPick.once("error", onError); - cleaners.push(() => cherryPick.off("error", onError)); - }); + await pipeExecFile("git", ["cherry-pick", commit]); } catch { - console.error(chalk.bold("Please resolve conflicts in a seperate terminal and then press enter here...")); + console.error(chalk.bold("Please resolve conflicts in a separate terminal and then press enter here...")); await new Promise(resolve => rl.once("line", () => resolve())); } }; @@ -249,7 +250,7 @@ async function pickWhichPRsToUse(prs: ExtendedGithubPrData[]): Promise 0) { - maintenencePrLines.unshift("## 🧰 Maintenance", ""); - maintenencePrLines.push(""); + if (maintenancePrLines.length > 0) { + maintenancePrLines.unshift("## 🧰 Maintenance", ""); + maintenancePrLines.push(""); } return [ @@ -281,22 +282,22 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD "", ...enhancementPrLines, ...bugPrLines, - ...maintenencePrLines, + ...maintenancePrLines, ].join("\n"); } -async function cherrypickCommits(prs: ExtendedGithubPrData[]): Promise { +async function cherryPickCommits(prs: ExtendedGithubPrData[]): Promise { const rl = createInterface(process.stdin); - const cherrypickCommit = cherrypickCommitWith(rl); + const cherryPickCommit = cherryPickCommitWith(rl); for (const pr of prs) { - await cherrypickCommit(pr.mergeCommit.oid); + await cherryPickCommit(pr.mergeCommit.oid); } rl.close(); } -async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise { +async function pickRelevantPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise { if (isMasterBranch) { return prs; } @@ -307,7 +308,7 @@ async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: bool selectedPrs = await pickWhichPRsToUse(prs); } while (selectedPrs.length === 0 && (console.warn("[WARNING]: must pick at least once commit"), true)); - await cherrypickCommits(selectedPrs); + await cherryPickCommits(selectedPrs); return selectedPrs; } @@ -326,8 +327,8 @@ async function createRelease(): Promise { } const prMilestone = formatSemverForMilestone(await getCurrentVersionOfSubPackage("core")); - const relaventPrs = await getRelevantPRs(prMilestone, previousReleasedVersion); - const selectedPrs = await pickRelaventPrs(relaventPrs, isMasterBranch); + const relevantPrs = await getRelevantPRs(prMilestone, previousReleasedVersion); + const selectedPrs = await pickRelevantPrs(relevantPrs, isMasterBranch); const prBody = formatChangelog(previousReleasedVersion, selectedPrs); if (!isMasterBranch) { From c174965708ff33f6bdf036bf2883f21daec3acea Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Tue, 28 Feb 2023 15:37:07 +0200 Subject: [PATCH 14/21] Introduce package for Features (#7242) Signed-off-by: Janne Savolainen --- package-lock.json | 11 + .../technical-features/feature-core/README.md | 41 +++ .../technical-features/feature-core/index.ts | 3 + .../feature-core/jest.config.js | 2 + .../feature-core/package.json | 30 ++ .../feature-core/src/deregister-feature.ts | 82 +++++ .../src/feature-context-map-injectable.ts | 27 ++ .../src/feature-dependencies.test.ts | 281 ++++++++++++++++++ .../feature-core/src/feature.ts | 12 + .../feature-core/src/register-feature.ts | 90 ++++++ .../src/registration-of-feature.test.ts | 147 +++++++++ .../feature-core/tsconfig.json | 3 + .../feature-core/webpack.config.js | 1 + 13 files changed, 730 insertions(+) create mode 100644 packages/technical-features/feature-core/README.md create mode 100644 packages/technical-features/feature-core/index.ts create mode 100644 packages/technical-features/feature-core/jest.config.js create mode 100644 packages/technical-features/feature-core/package.json create mode 100644 packages/technical-features/feature-core/src/deregister-feature.ts create mode 100644 packages/technical-features/feature-core/src/feature-context-map-injectable.ts create mode 100644 packages/technical-features/feature-core/src/feature-dependencies.test.ts create mode 100644 packages/technical-features/feature-core/src/feature.ts create mode 100644 packages/technical-features/feature-core/src/register-feature.ts create mode 100644 packages/technical-features/feature-core/src/registration-of-feature.test.ts create mode 100644 packages/technical-features/feature-core/tsconfig.json create mode 100644 packages/technical-features/feature-core/webpack.config.js diff --git a/package-lock.json b/package-lock.json index 2cfc549628..43e976a625 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3301,6 +3301,10 @@ "resolved": "packages/extension-api", "link": true }, + "node_modules/@k8slens/feature-core": { + "resolved": "packages/technical-features/feature-core", + "link": true + }, "node_modules/@k8slens/generate-tray-icons": { "resolved": "packages/generate-tray-icons", "link": true @@ -34811,6 +34815,13 @@ "@ogre-tools/injectable": "^15.1.1", "lodash": "^4.17.15" } + }, + "packages/technical-features/feature-core": { + "version": "0.0.1", + "license": "MIT", + "peerDependencies": { + "@ogre-tools/injectable": "^15.1.1" + } } } } diff --git a/packages/technical-features/feature-core/README.md b/packages/technical-features/feature-core/README.md new file mode 100644 index 0000000000..272893dc7b --- /dev/null +++ b/packages/technical-features/feature-core/README.md @@ -0,0 +1,41 @@ +# @k8slens/feature-core + +Feature is set of injectables that are registered and deregistered simultaneously. + +## Install +```bash +$ npm install @k8slens/feature-core +``` + +## Usage + +```typescript +import { createContainer } from "@ogre-tools/injectable" +import { getFeature, registerFeature, deregisterFeature } from "@k8slens/feature-core" + +// Notice that this Feature is usually exported from another NPM package. +const someFeature = getFeature({ + id: "some-feature", + + register: (di) => { + di.register(someInjectable, someOtherInjectable); + }, + + // Feature dependencies are automatically registered and + // deregistered when necessary. + dependencies: [someOtherFeature] +}); + +const di = createContainer("some-container"); + +registerFeature(di, someFeature); + +// Or perhaps you want to deregister? +deregisterFeature(di, someFeature); +``` + +## Need to know + +#### NPM packages exporting a Feature +- Prefer `peerDependencies` since they are installed from the application and are not allowed to be in the built bundle. +- Prefer exporting `injectionToken` instead of `injectable` for not allowing other features to access technical details like the `injectable` diff --git a/packages/technical-features/feature-core/index.ts b/packages/technical-features/feature-core/index.ts new file mode 100644 index 0000000000..13f6306665 --- /dev/null +++ b/packages/technical-features/feature-core/index.ts @@ -0,0 +1,3 @@ +export { getFeature } from "./src/feature"; +export { registerFeature } from "./src/register-feature"; +export type { Feature, GetFeatureArgs } from "./src/feature"; diff --git a/packages/technical-features/feature-core/jest.config.js b/packages/technical-features/feature-core/jest.config.js new file mode 100644 index 0000000000..23be80353b --- /dev/null +++ b/packages/technical-features/feature-core/jest.config.js @@ -0,0 +1,2 @@ +module.exports = + require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact; diff --git a/packages/technical-features/feature-core/package.json b/packages/technical-features/feature-core/package.json new file mode 100644 index 0000000000..fa4f821fef --- /dev/null +++ b/packages/technical-features/feature-core/package.json @@ -0,0 +1,30 @@ +{ + "name": "@k8slens/feature-core", + "private": false, + "version": "0.0.1", + "description": "Code that is common to all Features and those registering them.", + "type": "commonjs", + "files": [ + "dist" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/lensapp/lens.git" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "author": { + "name": "OpenLens Authors", + "email": "info@k8slens.dev" + }, + "license": "MIT", + "homepage": "https://github.com/lensapp/lens", + "scripts": { + "build": "webpack", + "dev": "webpack --mode=development --watch", + "test": "jest --coverage --runInBand" + }, + "peerDependencies": { + "@ogre-tools/injectable": "^15.1.1" + } +} diff --git a/packages/technical-features/feature-core/src/deregister-feature.ts b/packages/technical-features/feature-core/src/deregister-feature.ts new file mode 100644 index 0000000000..3d77759659 --- /dev/null +++ b/packages/technical-features/feature-core/src/deregister-feature.ts @@ -0,0 +1,82 @@ +import type { DiContainer } from "@ogre-tools/injectable"; +import type { Feature } from "./feature"; +import { featureContextMapInjectable } from "./feature-context-map-injectable"; + +export const deregisterFeature = (di: DiContainer, ...features: Feature[]) => { + features.forEach((feature) => { + deregisterFeatureRecursed(di, feature); + }); +}; + +const deregisterFeatureRecursed = ( + di: DiContainer, + feature: Feature, + dependedBy?: Feature +) => { + const featureContextMap = di.inject(featureContextMapInjectable); + + const featureContext = featureContextMap.get(feature); + + if (!featureContext) { + throw new Error( + `Tried to deregister feature "${feature.id}", but it was not registered.` + ); + } + + featureContext.numberOfRegistrations--; + + const getDependingFeatures = getDependingFeaturesFor(featureContextMap); + + const dependingFeatures = getDependingFeatures(feature); + + if (!dependedBy && dependingFeatures.length) { + throw new Error( + `Tried to deregister Feature "${ + feature.id + }", but it is the dependency of Features "${dependingFeatures.join( + ", " + )}"` + ); + } + + if (dependedBy) { + const oldNumberOfDependents = featureContext.dependedBy.get(dependedBy)!; + const newNumberOfDependants = oldNumberOfDependents - 1; + featureContext.dependedBy.set(dependedBy, newNumberOfDependants); + + if (newNumberOfDependants === 0) { + featureContext.dependedBy.delete(dependedBy); + } + } + + if (featureContext.numberOfRegistrations === 0) { + featureContextMap.delete(feature); + + featureContext.deregister(); + } + + feature.dependencies?.forEach((dependency) => { + deregisterFeatureRecursed(di, dependency, feature); + }); +}; + +const getDependingFeaturesFor = ( + featureContextMap: Map }> +) => { + const getDependingFeaturesForRecursion = ( + feature: Feature, + atRoot = true + ): string[] => { + const context = featureContextMap.get(feature); + + if (context?.dependedBy.size) { + return [...context!.dependedBy.entries()].flatMap(([dependant]) => + getDependingFeaturesForRecursion(dependant, false) + ); + } + + return atRoot ? [] : [feature.id]; + }; + + return getDependingFeaturesForRecursion; +}; diff --git a/packages/technical-features/feature-core/src/feature-context-map-injectable.ts b/packages/technical-features/feature-core/src/feature-context-map-injectable.ts new file mode 100644 index 0000000000..8c34524fee --- /dev/null +++ b/packages/technical-features/feature-core/src/feature-context-map-injectable.ts @@ -0,0 +1,27 @@ +import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; +import type { Feature } from "./feature"; + +export type FeatureContextMap = Map< + Feature, + { + register: () => void; + deregister: () => void; + dependedBy: Map; + numberOfRegistrations: number; + } +>; + +export const featureContextMapInjectionToken = + getInjectionToken({ + id: "feature-context-map-injection-token", + }); + +const featureContextMapInjectable = getInjectable({ + id: "feature-store", + + instantiate: (): FeatureContextMap => new Map(), + + injectionToken: featureContextMapInjectionToken, +}); + +export { featureContextMapInjectable }; diff --git a/packages/technical-features/feature-core/src/feature-dependencies.test.ts b/packages/technical-features/feature-core/src/feature-dependencies.test.ts new file mode 100644 index 0000000000..cdb7267419 --- /dev/null +++ b/packages/technical-features/feature-core/src/feature-dependencies.test.ts @@ -0,0 +1,281 @@ +import { + createContainer, + DiContainer, + getInjectable, + Injectable, +} from "@ogre-tools/injectable"; + +import type { Feature } from "./feature"; +import { registerFeature } from "./register-feature"; +import { deregisterFeature } from "./deregister-feature"; +import { getFeature } from "./feature" ; + +describe("feature-dependencies", () => { + describe("given a parent Feature with another Features as dependency", () => { + let di: DiContainer; + let someInjectable: Injectable; + let someInjectableInDependencyFeature: Injectable; + let someParentFeature: Feature; + let someDependencyFeature: Feature; + + beforeEach(() => { + di = createContainer("irrelevant"); + + someInjectable = getInjectable({ + id: "some-injectable-2", + instantiate: () => "some-instance", + }); + + someInjectableInDependencyFeature = getInjectable({ + id: "some-injectable", + instantiate: () => "some-instance-2", + }); + + someDependencyFeature = getFeature({ + id: "some-dependency-feature", + register: (di) => di.register(someInjectableInDependencyFeature), + }); + + someParentFeature = getFeature({ + id: "some-feature", + register: (di) => di.register(someInjectable), + dependencies: [someDependencyFeature], + }); + + registerFeature(di, someParentFeature); + }); + + it("when an injectable from the dependency Feature is injected, does so", () => { + const actual = di.inject(someInjectableInDependencyFeature); + + expect(actual).toBe("some-instance-2"); + }); + + it("when the dependency Feature is deregistered, throws", () => { + expect(() => { + deregisterFeature(di, someDependencyFeature); + }).toThrow( + 'Tried to deregister Feature "some-dependency-feature", but it is the dependency of Features "some-feature"' + ); + }); + + it("given the parent Feature is already deregistered, when also the dependency Feature is deregistered, throws", () => { + deregisterFeature(di, someParentFeature); + + expect(() => { + deregisterFeature(di, someDependencyFeature); + }).toThrow( + 'Tried to deregister feature "some-dependency-feature", but it was not registered.' + ); + }); + + it("given the parent Feature is deregistered, when injecting an injectable from the dependency Feature, throws", () => { + deregisterFeature(di, someParentFeature); + + expect(() => { + di.inject(someInjectableInDependencyFeature); + }).toThrow( + 'Tried to inject non-registered injectable "irrelevant" -> "some-injectable".' + ); + }); + }); + + describe("given a first Feature is registered, when second Feature using the first Feature as dependency gets registered", () => { + let di: DiContainer; + let someInjectable: Injectable; + let someFeature2: Feature; + let someFeature1: Feature; + + beforeEach(() => { + di = createContainer("irrelevant"); + + someInjectable = getInjectable({ + id: "some-injectable", + instantiate: () => "some-instance", + }); + + someFeature1 = getFeature({ + id: "some-feature-1", + register: (di) => di.register(someInjectable), + }); + + someFeature2 = getFeature({ + id: "some-feature-2", + register: () => {}, + dependencies: [someFeature1], + }); + + registerFeature(di, someFeature1, someFeature2); + }); + + it("when the first Feature is deregistered, throws", () => { + expect(() => { + deregisterFeature(di, someFeature1); + }).toThrow( + 'Tried to deregister Feature "some-feature-1", but it is the dependency of Features "some-feature-2"' + ); + }); + + it("given the second Feature is deregistered, when injecting an injectable from the first Feature, still does so", () => { + deregisterFeature(di, someFeature2); + + const actual = di.inject(someInjectable); + + expect(actual).toBe("some-instance"); + }); + }); + + describe("given parent Features with a shared Feature as dependency", () => { + let di: DiContainer; + let someInjectableInDependencyFeature: Injectable; + let someFeature1: Feature; + let someFeature2: Feature; + let someSharedDependencyFeature: Feature; + + beforeEach(() => { + di = createContainer("irrelevant"); + + someInjectableInDependencyFeature = getInjectable({ + id: "some-injectable-in-dependency-feature", + instantiate: () => "some-instance", + }); + + someSharedDependencyFeature = getFeature({ + id: "some-dependency-feature", + register: (di) => di.register(someInjectableInDependencyFeature), + }); + + const someFeatureForAdditionalHierarchy = getFeature({ + id: "some-feature-for-additional-hierarchy", + register: () => {}, + dependencies: [someSharedDependencyFeature], + }); + + someFeature1 = getFeature({ + id: "some-feature-1", + register: () => {}, + dependencies: [someFeatureForAdditionalHierarchy], + }); + + someFeature2 = getFeature({ + id: "some-feature-2", + register: () => {}, + dependencies: [someFeatureForAdditionalHierarchy], + }); + + registerFeature(di, someFeature1, someFeature2); + }); + + it("when the shared Feature is deregistered, throws", () => { + expect(() => { + deregisterFeature(di, someSharedDependencyFeature); + }).toThrow( + 'Tried to deregister Feature "some-dependency-feature", but it is the dependency of Features "some-feature-1, some-feature-2"' + ); + }); + + it("given only part of the parent Features get deregistered, when injecting an injectable from the shared Feature, does so", () => { + deregisterFeature(di, someFeature1); + + const actual = di.inject(someInjectableInDependencyFeature); + + expect(actual).toBe("some-instance"); + }); + + it("given all of the parent Features get deregistered, when injecting an injectable from the shared Feature, throws", () => { + deregisterFeature(di, someFeature1, someFeature2); + + expect(() => { + di.inject(someInjectableInDependencyFeature); + }).toThrow( + 'Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".' + ); + }); + }); + + describe("given parent Features with a shared Feature as dependency and registered, when the shared Feature gets registered again", () => { + let di: DiContainer; + let someInjectableInDependencyFeature: Injectable; + let someFeature1: Feature; + let someFeature2: Feature; + let someSharedDependencyFeature: Feature; + + beforeEach(() => { + di = createContainer("irrelevant"); + + someInjectableInDependencyFeature = getInjectable({ + id: "some-injectable-in-dependency-feature", + instantiate: () => "some-instance", + }); + + someSharedDependencyFeature = getFeature({ + id: "some-dependency-feature", + register: (di) => di.register(someInjectableInDependencyFeature), + }); + + const someFeatureForAdditionalHierarchy = getFeature({ + id: "some-feature-for-additional-hierarchy", + register: () => {}, + dependencies: [someSharedDependencyFeature], + }); + + someFeature1 = getFeature({ + id: "some-feature-1", + register: () => {}, + dependencies: [someFeatureForAdditionalHierarchy], + }); + + someFeature2 = getFeature({ + id: "some-feature-2", + register: () => {}, + dependencies: [someFeatureForAdditionalHierarchy], + }); + + registerFeature( + di, + someFeature1, + someFeature2, + someSharedDependencyFeature + ); + }); + + it("when the shared Feature is deregistered, throws", () => { + expect(() => { + deregisterFeature(di, someSharedDependencyFeature); + }).toThrow( + 'Tried to deregister Feature "some-dependency-feature", but it is the dependency of Features "some-feature-1, some-feature-2"' + ); + }); + + it("given only part of the parent Features get deregistered, when injecting an injectable from the shared Feature, does so", () => { + deregisterFeature(di, someFeature1); + + const actual = di.inject(someInjectableInDependencyFeature); + + expect(actual).toBe("some-instance"); + }); + + it("given all of the parent Features get deregistered, when injecting an injectable from the shared Feature, still does so", () => { + deregisterFeature(di, someFeature1, someFeature2); + + const actual = di.inject(someInjectableInDependencyFeature); + + expect(actual).toBe("some-instance"); + }); + + it("given all of the Features get deregistered, when injecting an injectable from the shared Feature, throws", () => { + deregisterFeature( + di, + someFeature1, + someFeature2, + someSharedDependencyFeature + ); + + expect(() => { + di.inject(someInjectableInDependencyFeature); + }).toThrow( + 'Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".' + ); + }); + }); +}); diff --git a/packages/technical-features/feature-core/src/feature.ts b/packages/technical-features/feature-core/src/feature.ts new file mode 100644 index 0000000000..6a8084c92a --- /dev/null +++ b/packages/technical-features/feature-core/src/feature.ts @@ -0,0 +1,12 @@ +import type { DiContainerForInjection } from "@ogre-tools/injectable"; + +export interface Feature { + id: string; + register: (di: DiContainerForInjection) => void; + dependencies?: Feature[]; +} + +export interface GetFeatureArgs extends Feature {} + +export const getFeature = (getFeatureArgs: GetFeatureArgs): Feature => + getFeatureArgs; diff --git a/packages/technical-features/feature-core/src/register-feature.ts b/packages/technical-features/feature-core/src/register-feature.ts new file mode 100644 index 0000000000..391bd60fa3 --- /dev/null +++ b/packages/technical-features/feature-core/src/register-feature.ts @@ -0,0 +1,90 @@ +import type { DiContainer } from "@ogre-tools/injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import type { Feature } from "./feature"; +import { + featureContextMapInjectable, + featureContextMapInjectionToken, +} from "./feature-context-map-injectable"; + +export const registerFeature = (di: DiContainer, ...features: Feature[]) => { + features.forEach((feature) => { + registerFeatureRecursed(di, feature); + }); +}; + +const registerFeatureRecursed = ( + di: DiContainer, + feature: Feature, + dependedBy?: Feature +) => { + const featureContextMaps = di.injectMany(featureContextMapInjectionToken); + + if (featureContextMaps.length === 0) { + di.register(featureContextMapInjectable); + } + + const featureContextMap = di.inject(featureContextMapInjectable); + + const existingFeatureContext = featureContextMap.get(feature); + if ( + !dependedBy && + existingFeatureContext && + existingFeatureContext.dependedBy.size === 0 + ) { + throw new Error( + `Tried to register feature "${feature.id}", but it was already registered.` + ); + } + + const featureContext = + existingFeatureContext || createFeatureContext(feature, di); + + featureContext.numberOfRegistrations++; + + if (dependedBy) { + const oldNumberOfDependents = + featureContext.dependedBy.get(dependedBy) || 0; + + const newNumberOfDependants = oldNumberOfDependents + 1; + featureContext.dependedBy.set(dependedBy, newNumberOfDependants); + } + + if (!existingFeatureContext) { + featureContext.register(); + } + + feature.dependencies?.forEach((dependency) => { + registerFeatureRecursed(di, dependency, feature); + }); +}; + +const createFeatureContext = (feature: Feature, di: DiContainer) => { + const featureContextInjectable = getInjectable({ + id: feature.id, + + instantiate: (diForContextOfFeature) => ({ + register: () => { + feature.register(diForContextOfFeature); + }, + + deregister: () => { + diForContextOfFeature.deregister(featureContextInjectable); + }, + + dependedBy: new Map(), + + numberOfRegistrations: 0, + }), + + scope: true, + }); + + di.register(featureContextInjectable); + + const featureContextMap = di.inject(featureContextMapInjectable); + const featureContext = di.inject(featureContextInjectable); + + featureContextMap.set(feature, featureContext); + + return featureContext; +}; diff --git a/packages/technical-features/feature-core/src/registration-of-feature.test.ts b/packages/technical-features/feature-core/src/registration-of-feature.test.ts new file mode 100644 index 0000000000..ee7d6d7c86 --- /dev/null +++ b/packages/technical-features/feature-core/src/registration-of-feature.test.ts @@ -0,0 +1,147 @@ +import { registerFeature } from "./register-feature"; +import { + createContainer, + DiContainer, + getInjectable, + Injectable, +} from "@ogre-tools/injectable"; +import type { Feature } from "./feature"; +import { getFeature } from "./feature"; +import { deregisterFeature } from "./deregister-feature"; + +describe("register-feature", () => { + describe("given di-container and a Features with injectables, and given Features are registered", () => { + let di: DiContainer; + let someInjectable: Injectable; + let someInjectable2: Injectable; + let someFeature: Feature; + let someFeature2: Feature; + let instance: string; + + beforeEach(() => { + di = createContainer("irrelevant"); + + someInjectable = getInjectable({ + id: "some-injectable", + instantiate: () => "some-instance", + }); + + someInjectable2 = getInjectable({ + id: "some-injectable-2", + instantiate: () => "some-instance-2", + }); + + someFeature = getFeature({ + id: "some-feature-1", + register: (di) => di.register(someInjectable), + }); + + someFeature2 = getFeature({ + id: "some-feature-2", + register: (di) => di.register(someInjectable2), + }); + + registerFeature(di, someFeature); + registerFeature(di, someFeature2); + }); + + it("when an injectable is injected, does so", () => { + instance = di.inject(someInjectable); + + expect(instance).toBe("some-instance"); + }); + + describe("given a Feature is deregistered", () => { + beforeEach(() => { + deregisterFeature(di, someFeature); + }); + + it("when injecting a related injectable, throws", () => { + expect(() => { + di.inject(someInjectable); + }).toThrow(); + }); + + it("when injecting an unrelated injectable, does so", () => { + const instance = di.inject(someInjectable2); + + expect(instance).toBe("some-instance-2"); + }); + + describe("given the Feature is registered again", () => { + beforeEach(() => { + registerFeature(di, someFeature); + }); + + it("when injecting a related injectable, does so", () => { + const instance = di.inject(someInjectable); + + expect(instance).toBe("some-instance"); + }); + + it("when injecting an unrelated injectable, does so", () => { + const instance = di.inject(someInjectable2); + + expect(instance).toBe("some-instance-2"); + }); + }); + }); + + it("when a Feature is registered again, throws", () => { + expect(() => { + registerFeature(di, someFeature); + }).toThrow( + 'Tried to register feature "some-feature-1", but it was already registered.' + ); + }); + + it("given a Feature deregistered, when deregistered again, throws", () => { + deregisterFeature(di, someFeature); + + expect(() => { + deregisterFeature(di, someFeature); + }).toThrow( + 'Tried to deregister feature "some-feature-1", but it was not registered.' + ); + }); + }); + + it("given di-container and registered Features with injectables forming a cycle, when an injectable is injected, throws with namespaced error about cycle", () => { + const someInjectable: Injectable = getInjectable({ + id: "some-injectable-1", + instantiate: (di) => di.inject(someInjectable2), + }); + + const someInjectable2: Injectable = getInjectable({ + id: "some-injectable-2", + instantiate: (di) => di.inject(someInjectable), + }); + + const di = createContainer("some-container"); + + const someFeature = getFeature({ + id: "some-feature-1", + + register: (di) => { + di.register(someInjectable); + }, + }); + + const someFeature2 = getFeature({ + id: "some-feature-2", + + register: (di) => { + di.register(someInjectable2); + }, + }); + + registerFeature(di, someFeature, someFeature2); + + expect(() => { + di.inject(someInjectable); + }).toThrow( + // 'Cycle of injectables encountered: "some-container" -> "some-feature-1:some-injectable-1" -> "some-feature-2:some-injectable-2" -> "some-feature-1:some-injectable-1"' + 'Maximum call stack size exceeded' + ); + }); +}); diff --git a/packages/technical-features/feature-core/tsconfig.json b/packages/technical-features/feature-core/tsconfig.json new file mode 100644 index 0000000000..a4f6fa613e --- /dev/null +++ b/packages/technical-features/feature-core/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@k8slens/typescript/config/base.json" +} diff --git a/packages/technical-features/feature-core/webpack.config.js b/packages/technical-features/feature-core/webpack.config.js new file mode 100644 index 0000000000..3183f30179 --- /dev/null +++ b/packages/technical-features/feature-core/webpack.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/webpack").configForNode; From ec81af4e6c5f8d0c25469a56dfa602894f85734b Mon Sep 17 00:00:00 2001 From: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:02:42 +0200 Subject: [PATCH 15/21] Revert "Renderer file logging transport (#6795)" (#7245) Renderer file logging still caused UI freezing (at least on apple silicon macs) when cluster frame was open and main frame was reloaded. See #544 This reverts commit ac2d0e46ff9db63bbebb58b47a9a6455fc5ced84. Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com> --- packages/core/src/common/logger.injectable.ts | 11 ++++- .../src/common/winston-logger.injectable.ts | 20 --------- packages/core/src/renderer/bootstrap.tsx | 4 +- .../init-cluster-frame.injectable.ts | 2 - .../init-cluster-frame/init-cluster-frame.ts | 12 ++--- .../root-frame/init-root-frame.injectable.ts | 4 +- .../close-renderer-log-file.injectable.ts | 22 ---------- .../logger/file-transport.injectable.ts | 44 ------------------- 8 files changed, 14 insertions(+), 105 deletions(-) delete mode 100644 packages/core/src/common/winston-logger.injectable.ts delete mode 100644 packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts delete mode 100644 packages/core/src/renderer/logger/file-transport.injectable.ts diff --git a/packages/core/src/common/logger.injectable.ts b/packages/core/src/common/logger.injectable.ts index 82ff682c46..8e9dd2a6a7 100644 --- a/packages/core/src/common/logger.injectable.ts +++ b/packages/core/src/common/logger.injectable.ts @@ -3,13 +3,20 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import { createLogger, format } from "winston"; import type { Logger } from "./logger"; -import winstonLoggerInjectable from "./winston-logger.injectable"; +import { loggerTransportInjectionToken } from "./logger/transports"; const loggerInjectable = getInjectable({ id: "logger", instantiate: (di): Logger => { - const baseLogger = di.inject(winstonLoggerInjectable); + const baseLogger = createLogger({ + format: format.combine( + format.splat(), + format.simple(), + ), + transports: di.injectMany(loggerTransportInjectionToken), + }); return { debug: (message, ...data) => baseLogger.debug(message, ...data), diff --git a/packages/core/src/common/winston-logger.injectable.ts b/packages/core/src/common/winston-logger.injectable.ts deleted file mode 100644 index ec3854d8b9..0000000000 --- a/packages/core/src/common/winston-logger.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { createLogger, format } from "winston"; -import { loggerTransportInjectionToken } from "./logger/transports"; - -const winstonLoggerInjectable = getInjectable({ - id: "winston-logger", - instantiate: (di) => createLogger({ - format: format.combine( - format.splat(), - format.simple(), - ), - transports: di.injectMany(loggerTransportInjectionToken), - }), -}); - -export default winstonLoggerInjectable; diff --git a/packages/core/src/renderer/bootstrap.tsx b/packages/core/src/renderer/bootstrap.tsx index 818cd033f7..88688d4a75 100644 --- a/packages/core/src/renderer/bootstrap.tsx +++ b/packages/core/src/renderer/bootstrap.tsx @@ -46,9 +46,7 @@ export async function bootstrap(di: DiContainer) { } try { - await initializeApp(() => { - unmountComponentAtNode(rootElem); - }); + await initializeApp(() => unmountComponentAtNode(rootElem)); } catch (error) { console.error(`[BOOTSTRAP]: view initialization error: ${error}`, { origin: location.href, diff --git a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable.ts b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable.ts index a9a923f860..c640264ee3 100644 --- a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable.ts +++ b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable.ts @@ -12,7 +12,6 @@ import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event. import loadExtensionsInjectable from "../../load-extensions.injectable"; import loggerInjectable from "../../../../common/logger.injectable"; import showErrorNotificationInjectable from "../../../components/notifications/show-error-notification.injectable"; -import closeRendererLogFileInjectable from "../../../logger/close-renderer-log-file.injectable"; const initClusterFrameInjectable = getInjectable({ id: "init-cluster-frame", @@ -30,7 +29,6 @@ const initClusterFrameInjectable = getInjectable({ emitAppEvent: di.inject(emitAppEventInjectable), logger: di.inject(loggerInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable), - closeFileLogging: di.inject(closeRendererLogFileInjectable), }); }, }); diff --git a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts index 31f4bfe96e..109ae0f0bc 100644 --- a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts +++ b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts @@ -2,7 +2,6 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { once } from "lodash"; import type { Cluster } from "../../../../common/cluster/cluster"; import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry"; import type { ShowNotification } from "../../../components/notifications"; @@ -19,7 +18,6 @@ interface Dependencies { emitAppEvent: EmitAppEvent; logger: Logger; showErrorNotification: ShowNotification; - closeFileLogging: () => void; } const logPrefix = "[CLUSTER-FRAME]:"; @@ -32,7 +30,6 @@ export const initClusterFrame = ({ emitAppEvent, logger, showErrorNotification, - closeFileLogging, }: Dependencies) => async (unmountRoot: () => void) => { // TODO: Make catalogEntityRegistry already initialized when passed as dependency @@ -76,14 +73,11 @@ export const initClusterFrame = ({ }); }); - const onCloseFrame = once(() => { + window.onbeforeunload = () => { logger.info( `${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`, ); - closeFileLogging(); - unmountRoot(); - }); - window.addEventListener("beforeunload", onCloseFrame); - window.addEventListener("pagehide", onCloseFrame); + unmountRoot(); + }; }; diff --git a/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts b/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts index 2f698c5c89..8d2c3a43be 100644 --- a/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts +++ b/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts @@ -13,7 +13,6 @@ import loggerInjectable from "../../../common/logger.injectable"; import { delay } from "../../../common/utils"; import { broadcastMessage } from "../../../common/ipc"; import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling"; -import closeRendererLogFileInjectable from "../../logger/close-renderer-log-file.injectable"; const initRootFrameInjectable = getInjectable({ id: "init-root-frame", @@ -25,7 +24,6 @@ const initRootFrameInjectable = getInjectable({ const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable); const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); const logger = di.inject(loggerInjectable); - const closeRendererLogFile = di.inject(closeRendererLogFileInjectable); return async (unmountRoot: () => void) => { catalogEntityRegistry.init(); @@ -61,7 +59,7 @@ const initRootFrameInjectable = getInjectable({ window.addEventListener("beforeunload", () => { logger.info("[ROOT-FRAME]: Unload app"); - closeRendererLogFile(); + unmountRoot(); }); }; diff --git a/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts b/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts deleted file mode 100644 index 8480589c39..0000000000 --- a/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import winstonLoggerInjectable from "../../common/winston-logger.injectable"; -import rendererFileLoggerTransportInjectable from "./file-transport.injectable"; - -const closeRendererLogFileInjectable = getInjectable({ - id: "close-renderer-log-file", - instantiate: (di) => { - const winstonLogger = di.inject(winstonLoggerInjectable); - const fileLoggingTransport = di.inject(rendererFileLoggerTransportInjectable); - - return () => { - fileLoggingTransport.close?.(); - winstonLogger.remove(fileLoggingTransport); - }; - }, -}); - -export default closeRendererLogFileInjectable; diff --git a/packages/core/src/renderer/logger/file-transport.injectable.ts b/packages/core/src/renderer/logger/file-transport.injectable.ts deleted file mode 100644 index 0cd8607e83..0000000000 --- a/packages/core/src/renderer/logger/file-transport.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { transports } from "winston"; -import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable"; -import { loggerTransportInjectionToken } from "../../common/logger/transports"; -import windowLocationInjectable from "../../common/k8s-api/window-location.injectable"; -import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable"; -import { getClusterIdFromHost } from "../utils"; - -const rendererFileLoggerTransportInjectable = getInjectable({ - id: "renderer-file-logger-transport", - instantiate: (di) => { - let frameId: string; - - const currentlyInClusterFrame = di.inject( - currentlyInClusterFrameInjectable, - ); - - if (currentlyInClusterFrame) { - const { host } = di.inject(windowLocationInjectable); - const clusterId = getClusterIdFromHost(host); - - frameId = clusterId ? `cluster-${clusterId}` : "cluster"; - } else { - frameId = "main"; - } - - return new transports.File({ - handleExceptions: false, - level: "info", - filename: `lens-renderer-${frameId}.log`, - dirname: di.inject(directoryForLogsInjectable), - maxsize: 1024 * 1024, - maxFiles: 2, - tailable: true, - }); - }, - injectionToken: loggerTransportInjectionToken, -}); - -export default rendererFileLoggerTransportInjectable; From 9d5461dd81c7a796672b7ea9d42ef128c3cc73ec Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Wed, 1 Mar 2023 13:39:03 +0200 Subject: [PATCH 16/21] Make builds of TS-declarations more deterministic (#7248) The cause for this was unknown, and was fixed by using "fork-ts-checker-webpack-plugin" instead of "ts-loader". Signed-off-by: Janne Savolainen Co-authored-by: Iku-turso --- package-lock.json | 18 +- packages/infrastructure/webpack/package.json | 1 + .../get-multi-export-config.test.js.snap | 226 ++++++++++++++++++ .../webpack/src/get-multi-export-config.js | 54 +++-- .../src/get-multi-export-config.test.js | 128 ++++++---- .../webpack/src/get-node-config.js | 78 ++++++ .../webpack/src/get-react-config.js | 60 +++++ .../infrastructure/webpack/src/node-config.js | 71 +----- .../webpack/src/react-config.js | 49 +--- 9 files changed, 493 insertions(+), 192 deletions(-) create mode 100644 packages/infrastructure/webpack/src/__snapshots__/get-multi-export-config.test.js.snap create mode 100644 packages/infrastructure/webpack/src/get-node-config.js create mode 100644 packages/infrastructure/webpack/src/get-react-config.js diff --git a/package-lock.json b/package-lock.json index 43e976a625..7593c1590c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6070,8 +6070,7 @@ "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/parse5": { "version": "6.0.3", @@ -9990,7 +9989,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -13746,7 +13744,6 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.3.0.tgz", "integrity": "sha512-IN+XTzusCjR5VgntYFgxbxVx3WraPRnKehBFrf00cMSrtUuW9MsG9dhL6MWpY6MkjC3wVwoujfCDgZZCQwbswA==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", "chalk": "^4.1.2", @@ -13780,7 +13777,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -13894,8 +13890,7 @@ "node_modules/fs-monkey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -15203,7 +15198,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -15219,7 +15213,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -20784,7 +20777,6 @@ "version": "3.4.13", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", - "dev": true, "dependencies": { "fs-monkey": "^1.0.3" }, @@ -21762,8 +21754,7 @@ "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" }, "node_modules/node-addon-api": { "version": "1.7.2", @@ -25703,7 +25694,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -34110,6 +34100,7 @@ "dependencies": { "@types/webpack-env": "^1.18.0", "css-loader": "^6.7.2", + "fork-ts-checker-webpack-plugin": "^7.3.0", "mini-css-extract-plugin": "^2.7.0", "sass-loader": "^13.2.0", "style-loader": "^3.3.1", @@ -34817,6 +34808,7 @@ } }, "packages/technical-features/feature-core": { + "name": "@k8slens/feature-core", "version": "0.0.1", "license": "MIT", "peerDependencies": { diff --git a/packages/infrastructure/webpack/package.json b/packages/infrastructure/webpack/package.json index e79c257f26..4d2ebd5f3a 100644 --- a/packages/infrastructure/webpack/package.json +++ b/packages/infrastructure/webpack/package.json @@ -25,6 +25,7 @@ "dependencies": { "@types/webpack-env": "^1.18.0", "css-loader": "^6.7.2", + "fork-ts-checker-webpack-plugin": "^7.3.0", "mini-css-extract-plugin": "^2.7.0", "sass-loader": "^13.2.0", "style-loader": "^3.3.1", diff --git a/packages/infrastructure/webpack/src/__snapshots__/get-multi-export-config.test.js.snap b/packages/infrastructure/webpack/src/__snapshots__/get-multi-export-config.test.js.snap new file mode 100644 index 0000000000..2db11a39ef --- /dev/null +++ b/packages/infrastructure/webpack/src/__snapshots__/get-multi-export-config.test.js.snap @@ -0,0 +1,226 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`get-multi-export-config given maximal package.json, when creating configuration works 1`] = ` +Array [ + Object { + "entry": Object { + "index": "./index.ts", + }, + "externals": Array [ + [Function], + [Function], + ], + "externalsPresets": Object { + "node": true, + }, + "mode": "production", + "module": Object { + "rules": Array [ + Object { + "loader": "ts-loader", + "test": /\\\\\\.ts\\(x\\)\\?\\$/, + }, + ], + }, + "name": "./index.ts", + "node": Object { + "__dirname": true, + "__filename": true, + }, + "output": Object { + "filename": [Function], + "libraryTarget": "commonjs2", + "path": "/some-working-directory/dist", + }, + "performance": Object { + "hints": "error", + "maxEntrypointSize": 100000, + }, + "plugins": Array [ + ForkTsCheckerWebpackPlugin { + "options": Object { + "typescript": Object { + "configOverwrite": Object { + "compilerOptions": Object { + "declaration": true, + "declarationDir": "/some-working-directory/dist", + }, + "include": Array [ + "./index.ts", + ], + }, + "mode": "write-dts", + }, + }, + }, + ], + "resolve": Object { + "extensions": Array [ + ".ts", + ".tsx", + ], + }, + "target": "node", + }, + Object { + "entry": Object { + "index": "./some-entrypoint/index.ts", + }, + "externals": Array [ + [Function], + [Function], + ], + "externalsPresets": Object { + "node": true, + }, + "mode": "production", + "module": Object { + "rules": Array [ + Object { + "loader": "ts-loader", + "test": /\\\\\\.ts\\(x\\)\\?\\$/, + }, + ], + }, + "name": "./some-entrypoint/index.ts", + "node": Object { + "__dirname": true, + "__filename": true, + }, + "output": Object { + "filename": [Function], + "libraryTarget": "commonjs2", + "path": "/some-working-directory/dist/some-entrypoint", + }, + "performance": Object { + "hints": "error", + "maxEntrypointSize": 100000, + }, + "plugins": Array [ + ForkTsCheckerWebpackPlugin { + "options": Object { + "typescript": Object { + "configOverwrite": Object { + "compilerOptions": Object { + "declaration": true, + "declarationDir": "/some-working-directory/dist/some-entrypoint", + }, + "include": Array [ + "./some-entrypoint/index.ts", + ], + }, + "mode": "write-dts", + }, + }, + }, + ], + "resolve": Object { + "extensions": Array [ + ".ts", + ".tsx", + ], + }, + "target": "node", + }, + Object { + "entry": Object { + "index": "./some-other-entrypoint/index.ts", + }, + "externals": Array [ + [Function], + [Function], + ], + "externalsPresets": Object { + "node": true, + }, + "mode": "production", + "module": Object { + "rules": Array [ + Object { + "loader": "ts-loader", + "test": /\\\\\\.ts\\(x\\)\\?\\$/, + }, + Object { + "test": /\\\\\\.s\\?css\\$/, + "use": Array [ + Object { + "some": "miniCssExtractPluginLoader", + }, + Object { + "loader": "css-loader", + "options": Object { + "modules": Object { + "auto": /\\\\\\.module\\\\\\./i, + "localIdentName": "[name]__[local]--[hash:base64:5]", + "mode": "local", + }, + "sourceMap": false, + }, + }, + Object { + "loader": "sass-loader", + "options": Object { + "sourceMap": false, + }, + }, + ], + }, + ], + }, + "name": "./some-other-entrypoint/index.ts", + "node": Object { + "__dirname": true, + "__filename": true, + }, + "output": Object { + "filename": [Function], + "libraryTarget": "commonjs2", + "path": "/some-working-directory/dist/some-other-entrypoint", + }, + "performance": Object { + "hints": "error", + "maxEntrypointSize": 100000, + }, + "plugins": Array [ + ForkTsCheckerWebpackPlugin { + "options": Object { + "typescript": Object { + "configOverwrite": Object { + "compilerOptions": Object { + "declaration": true, + "declarationDir": "/some-working-directory/dist/some-other-entrypoint", + }, + "include": Array [ + "./some-other-entrypoint/index.ts", + ], + }, + "mode": "write-dts", + }, + }, + }, + MiniCssExtractPlugin { + "_sortedModulesCache": WeakMap {}, + "options": Object { + "chunkFilename": "[name].css", + "experimentalUseImportModule": undefined, + "filename": "[name].css", + "ignoreOrder": false, + "runtime": true, + }, + "runtimeOptions": Object { + "attributes": undefined, + "insert": undefined, + "linkType": "text/css", + }, + }, + ], + "resolve": Object { + "extensions": Array [ + ".ts", + ".tsx", + ], + }, + "target": "node", + }, +] +`; diff --git a/packages/infrastructure/webpack/src/get-multi-export-config.js b/packages/infrastructure/webpack/src/get-multi-export-config.js index 338b3d89d7..b4612ebad5 100644 --- a/packages/infrastructure/webpack/src/get-multi-export-config.js +++ b/packages/infrastructure/webpack/src/get-multi-export-config.js @@ -1,5 +1,5 @@ -const nodeConfig = require("./node-config"); -const reactConfig = require("./react-config"); +const getNodeConfig = require("./get-node-config"); +const getReactConfigFor = require("./get-react-config"); const path = require("path"); const { map, @@ -14,7 +14,15 @@ const { } = require("lodash/fp"); const { pipeline } = require("@ogre-tools/fp"); -module.exports = (packageJson, dependencies = { nodeConfig, reactConfig, joinPath: path.join }) => { +module.exports = ( + packageJson, + + dependencies = { + resolvePath: path.resolve, + workingDirectory: process.cwd(), + getReactConfig: getReactConfigFor() + } +) => { if (!packageJson.lensMultiExportConfig) { throw new Error( `Tried to get multi export config for package "${packageJson.name}" but configuration is missing.` @@ -72,10 +80,13 @@ module.exports = (packageJson, dependencies = { nodeConfig, reactConfig, joinPat ); } + const toExportSpecificWebpackConfig = + toExportSpecificWebpackConfigFor(dependencies); + return pipeline( packageJson.lensMultiExportConfig, toPairs, - map(toExportSpecificWebpackConfigFor(dependencies)) + map(toExportSpecificWebpackConfig) ); }; @@ -91,7 +102,11 @@ const toExpectedExport = (externalImportPath) => { return [ externalImportPath, { - types: `./${posixJoinForPackageJson("./dist", externalImportPath, "index.d.ts")}`, + types: `./${posixJoinForPackageJson( + "./dist", + externalImportPath, + "index.d.ts" + )}`, default: entrypointPath, import: entrypointPath, @@ -103,20 +118,19 @@ const toExpectedExport = (externalImportPath) => { const toExportSpecificWebpackConfigFor = (dependencies) => ([externalImportPath, { buildType, entrypoint }]) => { - const baseConfig = - buildType === "node" ? dependencies.nodeConfig : dependencies.reactConfig; + const outputDirectory = dependencies.resolvePath( + dependencies.workingDirectory, + "dist", + externalImportPath + ); - return { - ...baseConfig, - name: entrypoint, - - entry: { - index: entrypoint, - }, - - output: { - ...baseConfig.output, - path: dependencies.joinPath(baseConfig.output.path, externalImportPath), - }, - }; + return buildType === "node" + ? getNodeConfig({ + entrypointFilePath: entrypoint, + outputDirectory, + }) + : dependencies.getReactConfig({ + entrypointFilePath: entrypoint, + outputDirectory, + }); }; diff --git a/packages/infrastructure/webpack/src/get-multi-export-config.test.js b/packages/infrastructure/webpack/src/get-multi-export-config.test.js index b0ce1c2231..86e3d7d14c 100644 --- a/packages/infrastructure/webpack/src/get-multi-export-config.test.js +++ b/packages/infrastructure/webpack/src/get-multi-export-config.test.js @@ -1,10 +1,12 @@ +import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"; import getMultiExportConfig from "./get-multi-export-config"; -import path from 'path'; +import path from "path"; +const getReactConfigFor = require("./get-react-config"); -const joinPathFake = path.posix.join; +const resolvePathFake = path.posix.resolve; describe("get-multi-export-config", () => { - let actual; + let configs; let maximalPackageJson; beforeEach(() => { @@ -51,43 +53,69 @@ describe("get-multi-export-config", () => { }; }); - it("given maximal package.json, when creating configuration, works", () => { - actual = getMultiExportConfig(maximalPackageJson, { - nodeConfig: nodeConfigStub, - reactConfig: reactConfigStub, - joinPath: joinPathFake, + describe("given maximal package.json, when creating configuration", () => { + beforeEach(() => { + configs = getMultiExportConfig(maximalPackageJson, { + resolvePath: resolvePathFake, + workingDirectory: "/some-working-directory", + + getReactConfig: getReactConfigFor({ + miniCssExtractPluginLoader: { some: "miniCssExtractPluginLoader" }, + }), + }); }); - expect(actual).toEqual([ + it("works", () => { + expect(configs).toMatchSnapshot(); + }); + + [ { - name: "./index.ts", - stub: "node", - entry: { index: "./index.ts" }, - output: { some: "value", path: "/some-build-directory" }, + name: "config for node export in default entrypoint", + entrypoint: "./index.ts", + outputDirectory: "/some-working-directory/dist", }, - { - name: "./some-entrypoint/index.ts", - stub: "node", - entry: { index: "./some-entrypoint/index.ts" }, - - output: { - some: "value", - path: "/some-build-directory/some-entrypoint", - }, + name: "config for node export in a non-default entrypoint", + entrypoint: "./some-entrypoint/index.ts", + outputDirectory: "/some-working-directory/dist/some-entrypoint", }, - { - name: "./some-other-entrypoint/index.ts", - stub: "react", - entry: { index: "./some-other-entrypoint/index.ts" }, - - output: { - some: "other-value", - path: "/some-build-directory/some-other-entrypoint", - }, + name: "config for react export in a non-default entrypoint", + entrypoint: "./some-other-entrypoint/index.ts", + outputDirectory: "/some-working-directory/dist/some-other-entrypoint", }, - ]); + ].forEach((scenario) => { + describe(scenario.name, () => { + let config; + + beforeEach(() => { + config = configs.find(({ name }) => name === scenario.entrypoint); + }); + + it("has correct entrypoint", () => { + expect(config).toHaveProperty("entry.index", scenario.entrypoint); + }); + + it("has correct output directory", () => { + expect(config).toHaveProperty( + "output.path", + scenario.outputDirectory + ); + }); + + it("has correct declaration directory", () => { + expect( + config.plugins.find( + ({ constructor }) => constructor === ForkTsCheckerPlugin + ) + ).toHaveProperty( + "options.typescript.configOverwrite.compilerOptions.declarationDir", + scenario.outputDirectory + ); + }); + }); + }); }); it("given maximal package.json but path for entrypoint in exports do not match output, when creating configuration, throws", () => { @@ -95,9 +123,9 @@ describe("get-multi-export-config", () => { expect(() => { getMultiExportConfig(maximalPackageJson, { - nodeConfig: nodeConfigStub, - reactConfig: reactConfigStub, - joinPath: joinPathFake, + getNodeConfig: () => nodeConfigStub, + getReactConfig: () => reactConfigStub, + joinPath: resolvePathFake, }); }).toThrow( 'Tried to get multi export config but exports of package.json for "some-name" did not match exactly:' @@ -109,9 +137,9 @@ describe("get-multi-export-config", () => { expect(() => { getMultiExportConfig(maximalPackageJson, { - nodeConfig: nodeConfigStub, - reactConfig: reactConfigStub, - joinPath: joinPathFake, + getNodeConfig: () => nodeConfigStub, + getReactConfig: () => reactConfigStub, + joinPath: resolvePathFake, }); }).toThrow( 'Tried to get multi export config but exports of package.json for "some-name" did not match exactly:' @@ -123,9 +151,9 @@ describe("get-multi-export-config", () => { expect(() => { getMultiExportConfig(maximalPackageJson, { - nodeConfig: nodeConfigStub, - reactConfig: reactConfigStub, - joinPath: joinPathFake, + getNodeConfig: () => nodeConfigStub, + getReactConfig: () => reactConfigStub, + joinPath: resolvePathFake, }); }).toThrow( 'Tried to get multi export config but exports of package.json for "some-name" did not match exactly:' @@ -137,9 +165,9 @@ describe("get-multi-export-config", () => { expect(() => { getMultiExportConfig(maximalPackageJson, { - nodeConfig: nodeConfigStub, - reactConfig: reactConfigStub, - joinPath: joinPathFake, + getNodeConfig: () => nodeConfigStub, + getReactConfig: () => reactConfigStub, + joinPath: resolvePathFake, }); }).toThrow( 'Tried to get multi export config for package "some-name" but configuration is missing.' @@ -151,9 +179,9 @@ describe("get-multi-export-config", () => { expect(() => { getMultiExportConfig(maximalPackageJson, { - nodeConfig: nodeConfigStub, - reactConfig: reactConfigStub, - joinPath: joinPathFake, + getNodeConfig: () => nodeConfigStub, + getReactConfig: () => reactConfigStub, + joinPath: resolvePathFake, }); }).toThrow( 'Tried to get multi export config for package "some-name" but build types "some-invalid" were not any of "node", "react".' @@ -166,9 +194,9 @@ describe("get-multi-export-config", () => { expect(() => { getMultiExportConfig(maximalPackageJson, { - nodeConfig: nodeConfigStub, - reactConfig: reactConfigStub, - joinPath: joinPathFake, + getNodeConfig: () => nodeConfigStub, + getReactConfig: () => reactConfigStub, + joinPath: resolvePathFake, }); }).toThrow( 'Tried to get multi export config for package "some-name" but entrypoint was missing for "./some-entrypoint".' diff --git a/packages/infrastructure/webpack/src/get-node-config.js b/packages/infrastructure/webpack/src/get-node-config.js new file mode 100644 index 0000000000..4ffd9f831a --- /dev/null +++ b/packages/infrastructure/webpack/src/get-node-config.js @@ -0,0 +1,78 @@ +const ForkTsCheckerPlugin = require("fork-ts-checker-webpack-plugin"); +const nodeExternals = require("webpack-node-externals"); +const path = require("path"); + +module.exports = ({ entrypointFilePath, outputDirectory }) => ({ + name: entrypointFilePath, + entry: { index: entrypointFilePath }, + target: "node", + mode: "production", + + performance: { + maxEntrypointSize: 100000, + hints: "error", + }, + + resolve: { + extensions: [".ts", ".tsx"], + }, + + plugins: [ + new ForkTsCheckerPlugin({ + typescript: { + mode: "write-dts", + + configOverwrite: { + include: [entrypointFilePath], + + compilerOptions: { + declaration: true, + declarationDir: outputDirectory, + }, + }, + }, + }), + ], + + output: { + path: outputDirectory, + + filename: (pathData) => + pathData.chunk.name === "index" + ? "index.js" + : `${pathData.chunk.name}/index.js`, + + libraryTarget: "commonjs2", + }, + + externals: [ + nodeExternals({ modulesFromFile: true }), + + nodeExternals({ + modulesDir: path.resolve( + __dirname, + "..", + "..", + "..", + "..", + "node_modules" + ), + }), + ], + + externalsPresets: { node: true }, + + node: { + __dirname: true, + __filename: true, + }, + + module: { + rules: [ + { + test: /\.ts(x)?$/, + loader: "ts-loader", + }, + ], + }, +}); diff --git a/packages/infrastructure/webpack/src/get-react-config.js b/packages/infrastructure/webpack/src/get-react-config.js new file mode 100644 index 0000000000..6b83b6af86 --- /dev/null +++ b/packages/infrastructure/webpack/src/get-react-config.js @@ -0,0 +1,60 @@ +const getNodeConfig = require("./get-node-config"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +module.exports = + ({ miniCssExtractPluginLoader = MiniCssExtractPlugin.loader } = {}) => + ({ entrypointFilePath, outputDirectory }) => { + const nodeConfig = getNodeConfig({ + entrypointFilePath, + outputDirectory, + }); + + return { + ...nodeConfig, + + plugins: [ + ...nodeConfig.plugins, + + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + ], + + module: { + ...nodeConfig.module, + + rules: [ + ...nodeConfig.module.rules, + + { + test: /\.s?css$/, + + use: [ + miniCssExtractPluginLoader, + + { + loader: "css-loader", + + options: { + sourceMap: false, + + modules: { + auto: /\.module\./i, // https://github.com/webpack-contrib/css-loader#auto + mode: "local", // :local(.selector) by default + localIdentName: "[name]__[local]--[hash:base64:5]", + }, + }, + }, + + { + loader: "sass-loader", + options: { + sourceMap: false, + }, + }, + ], + }, + ], + }, + }; + }; diff --git a/packages/infrastructure/webpack/src/node-config.js b/packages/infrastructure/webpack/src/node-config.js index d01f58b882..97ade8cd37 100644 --- a/packages/infrastructure/webpack/src/node-config.js +++ b/packages/infrastructure/webpack/src/node-config.js @@ -1,68 +1,7 @@ -const nodeExternals = require("webpack-node-externals"); const path = require("path"); +const getNodeConfig = require("./get-node-config"); -const buildDirectory = path.resolve(process.cwd(), "dist"); - -module.exports = { - entry: { index: "./index.ts" }, - target: "node", - mode: "production", - - performance: { - maxEntrypointSize: 100000, - hints: "error", - }, - - resolve: { - extensions: [".ts", ".tsx"], - }, - - output: { - path: buildDirectory, - - filename: (pathData) => - pathData.chunk.name === "index" - ? "index.js" - : `${pathData.chunk.name}/index.js`, - - libraryTarget: "commonjs2", - }, - - externals: [ - nodeExternals({ modulesFromFile: true }), - - nodeExternals({ - modulesDir: path.resolve( - __dirname, - "..", - "..", - "..", - "..", - "node_modules" - ), - }), - ], - - externalsPresets: { node: true }, - - node: { - __dirname: true, - __filename: true, - }, - - module: { - rules: [ - { - test: /\.ts(x)?$/, - loader: "ts-loader", - - options: { - compilerOptions: { - declaration: true, - declarationDir: "./dist", - }, - }, - }, - ], - }, -}; +module.exports = getNodeConfig({ + entrypointFilePath: "./index.ts", + outputDirectory: path.resolve(process.cwd(), "dist"), +}); diff --git a/packages/infrastructure/webpack/src/react-config.js b/packages/infrastructure/webpack/src/react-config.js index 36ad7cca46..3b25515208 100644 --- a/packages/infrastructure/webpack/src/react-config.js +++ b/packages/infrastructure/webpack/src/react-config.js @@ -1,44 +1,7 @@ -const nodeConfig = require("./node-config"); -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const path = require("path"); +const getReactConfig = require("./get-react-config"); -module.exports = { - ...nodeConfig, - - plugins: [ - new MiniCssExtractPlugin({ - filename: "[name].css", - }), - ], - - module: { - ...nodeConfig.module, - rules: [ - ...nodeConfig.module.rules, - - { - test: /\.s?css$/, - - use: [ - MiniCssExtractPlugin.loader, - { - loader: "css-loader", - options: { - sourceMap: false, - modules: { - auto: /\.module\./i, // https://github.com/webpack-contrib/css-loader#auto - mode: "local", // :local(.selector) by default - localIdentName: "[name]__[local]--[hash:base64:5]", - }, - }, - }, - { - loader: "sass-loader", - options: { - sourceMap: false, - }, - }, - ], - }, - ], - }, -}; +module.exports = getReactConfig({ + entrypointFilePath: "./index.ts", + outputDirectory: path.resolve(process.cwd(), "dist"), +}); From 3433bc6fe0511d2513fe2e3a5426519ee922a5b1 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 1 Mar 2023 05:11:49 -0800 Subject: [PATCH 17/21] Fix extension install (#7247) * Fix extension install - Remove old bundled extension dependencies - Make sure external extensions are installed as optional Signed-off-by: Sebastian Malton * Ignore ENOENT errors Signed-off-by: Sebastian Malton * Add comment Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- packages/core/src/common/fs/fs.injectable.ts | 2 +- .../extension-discovery.injectable.ts | 4 +- .../extension-discovery.test.ts | 2 +- .../extension-installer.injectable.ts | 21 ---- .../extension-installer.ts | 78 ------------ .../install-extension.injectable.ts | 13 -- ...nsion-package-root-directory.injectable.ts | 2 +- .../install-extension.injectable.ts | 111 ++++++++++++++++++ 8 files changed, 116 insertions(+), 117 deletions(-) delete mode 100644 packages/core/src/extensions/extension-installer/extension-installer.injectable.ts delete mode 100644 packages/core/src/extensions/extension-installer/extension-installer.ts delete mode 100644 packages/core/src/extensions/extension-installer/install-extension/install-extension.injectable.ts rename packages/core/src/extensions/{extension-installer/extension-package-root-directory => install-extension}/extension-package-root-directory.injectable.ts (76%) create mode 100644 packages/core/src/extensions/install-extension/install-extension.injectable.ts diff --git a/packages/core/src/common/fs/fs.injectable.ts b/packages/core/src/common/fs/fs.injectable.ts index f80375095c..600e4eaa40 100644 --- a/packages/core/src/common/fs/fs.injectable.ts +++ b/packages/core/src/common/fs/fs.injectable.ts @@ -7,7 +7,7 @@ import type { ReadOptions } from "fs-extra"; import fse from "fs-extra"; /** - * NOTE: Add corrisponding a corrisponding override of this injecable in `src/test-utils/override-fs-with-fakes.ts` + * NOTE: Add corresponding override of this injectable in `src/test-utils/override-fs-with-fakes.ts` */ const fsInjectable = getInjectable({ id: "fs", diff --git a/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts b/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts index 378f519bb7..5e2a4cd84d 100644 --- a/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts +++ b/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts @@ -8,8 +8,8 @@ import extensionLoaderInjectable from "../extension-loader/extension-loader.inje import isCompatibleExtensionInjectable from "./is-compatible-extension/is-compatible-extension.injectable"; import extensionsStoreInjectable from "../extensions-store/extensions-store.injectable"; import extensionInstallationStateStoreInjectable from "../extension-installation-state-store/extension-installation-state-store.injectable"; -import installExtensionInjectable from "../extension-installer/install-extension/install-extension.injectable"; -import extensionPackageRootDirectoryInjectable from "../extension-installer/extension-package-root-directory/extension-package-root-directory.injectable"; +import installExtensionInjectable from "../install-extension/install-extension.injectable"; +import extensionPackageRootDirectoryInjectable from "../install-extension/extension-package-root-directory.injectable"; import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; import loggerInjectable from "../../common/logger.injectable"; import pathExistsInjectable from "../../common/fs/path-exists.injectable"; diff --git a/packages/core/src/extensions/extension-discovery/extension-discovery.test.ts b/packages/core/src/extensions/extension-discovery/extension-discovery.test.ts index d71f8c5292..77e9cd0623 100644 --- a/packages/core/src/extensions/extension-discovery/extension-discovery.test.ts +++ b/packages/core/src/extensions/extension-discovery/extension-discovery.test.ts @@ -7,7 +7,7 @@ import type { FSWatcher } from "chokidar"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; import extensionDiscoveryInjectable from "../extension-discovery/extension-discovery.injectable"; import type { ExtensionDiscovery } from "../extension-discovery/extension-discovery"; -import installExtensionInjectable from "../extension-installer/install-extension/install-extension.injectable"; +import installExtensionInjectable from "../install-extension/install-extension.injectable"; import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import { delay } from "../../renderer/utils"; import { observable, runInAction, when } from "mobx"; diff --git a/packages/core/src/extensions/extension-installer/extension-installer.injectable.ts b/packages/core/src/extensions/extension-installer/extension-installer.injectable.ts deleted file mode 100644 index 92b4436701..0000000000 --- a/packages/core/src/extensions/extension-installer/extension-installer.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import pathToNpmCliInjectable from "../../common/app-paths/path-to-npm-cli.injectable"; -import loggerInjectable from "../../common/logger.injectable"; -import { ExtensionInstaller } from "./extension-installer"; -import extensionPackageRootDirectoryInjectable from "./extension-package-root-directory/extension-package-root-directory.injectable"; - -const extensionInstallerInjectable = getInjectable({ - id: "extension-installer", - - instantiate: (di) => new ExtensionInstaller({ - extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable), - logger: di.inject(loggerInjectable), - pathToNpmCli: di.inject(pathToNpmCliInjectable), - }), -}); - -export default extensionInstallerInjectable; diff --git a/packages/core/src/extensions/extension-installer/extension-installer.ts b/packages/core/src/extensions/extension-installer/extension-installer.ts deleted file mode 100644 index 223477d0c4..0000000000 --- a/packages/core/src/extensions/extension-installer/extension-installer.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import AwaitLock from "await-lock"; -import child_process from "child_process"; -import type { Logger } from "../../common/logger"; - -const logModule = "[EXTENSION-INSTALLER]"; - -interface Dependencies { - readonly extensionPackageRootDirectory: string; - readonly logger: Logger; - readonly pathToNpmCli: string; -} - -const baseNpmInstallArgs = [ - "install", - "--audit=false", - "--fund=false", - // NOTE: we do not omit the `optional` dependencies because that is how we specify the non-bundled extensions - "--omit=dev", - "--omit=peer", - "--prefer-offline", -]; - -/** - * Installs dependencies for extensions - */ -export class ExtensionInstaller { - private readonly installLock = new AwaitLock(); - - constructor(private readonly dependencies: Dependencies) {} - - /** - * Install single package using npm - */ - installPackage = async (name: string): Promise => { - // Mutual exclusion to install packages in sequence - await this.installLock.acquireAsync(); - - try { - this.dependencies.logger.info(`${logModule} installing package from ${name} to ${this.dependencies.extensionPackageRootDirectory}`); - await this.npm(...baseNpmInstallArgs, name); - this.dependencies.logger.info(`${logModule} package ${name} installed to ${this.dependencies.extensionPackageRootDirectory}`); - } finally { - this.installLock.release(); - } - }; - - private npm(...args: string[]): Promise { - return new Promise((resolve, reject) => { - const child = child_process.fork(this.dependencies.pathToNpmCli, args, { - cwd: this.dependencies.extensionPackageRootDirectory, - silent: true, - env: {}, - }); - let stderr = ""; - - child.stderr?.on("data", data => { - stderr += String(data); - }); - - child.on("close", (code) => { - if (code !== 0) { - reject(new Error(stderr)); - } else { - resolve(); - } - }); - - child.on("error", error => { - reject(error); - }); - }); - } -} diff --git a/packages/core/src/extensions/extension-installer/install-extension/install-extension.injectable.ts b/packages/core/src/extensions/extension-installer/install-extension/install-extension.injectable.ts deleted file mode 100644 index 940c5987a5..0000000000 --- a/packages/core/src/extensions/extension-installer/install-extension/install-extension.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import extensionInstallerInjectable from "../extension-installer.injectable"; - -const installExtensionInjectable = getInjectable({ - id: "install-extension", - instantiate: (di) => di.inject(extensionInstallerInjectable).installPackage, -}); - -export default installExtensionInjectable; diff --git a/packages/core/src/extensions/extension-installer/extension-package-root-directory/extension-package-root-directory.injectable.ts b/packages/core/src/extensions/install-extension/extension-package-root-directory.injectable.ts similarity index 76% rename from packages/core/src/extensions/extension-installer/extension-package-root-directory/extension-package-root-directory.injectable.ts rename to packages/core/src/extensions/install-extension/extension-package-root-directory.injectable.ts index 72bd0ad8c2..ffa0a7666d 100644 --- a/packages/core/src/extensions/extension-installer/extension-package-root-directory/extension-package-root-directory.injectable.ts +++ b/packages/core/src/extensions/install-extension/extension-package-root-directory.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; const extensionPackageRootDirectoryInjectable = getInjectable({ id: "extension-package-root-directory", diff --git a/packages/core/src/extensions/install-extension/install-extension.injectable.ts b/packages/core/src/extensions/install-extension/install-extension.injectable.ts new file mode 100644 index 0000000000..ca46772eb3 --- /dev/null +++ b/packages/core/src/extensions/install-extension/install-extension.injectable.ts @@ -0,0 +1,111 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { fork } from "child_process"; +import AwaitLock from "await-lock"; +import pathToNpmCliInjectable from "../../common/app-paths/path-to-npm-cli.injectable"; +import extensionPackageRootDirectoryInjectable from "./extension-package-root-directory.injectable"; +import prefixedLoggerInjectable from "../../common/logger/prefixed-logger.injectable"; +import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; +import joinPathsInjectable from "../../common/path/join-paths.injectable"; +import type { PackageJson } from "../common-api"; +import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable"; +import { once } from "lodash"; +import { isErrnoException } from "../../common/utils"; + +const baseNpmInstallArgs = [ + "install", + "--save-optional", + "--audit=false", + "--fund=false", + // NOTE: we do not omit the `optional` dependencies because that is how we specify the non-bundled extensions + "--omit=dev", + "--omit=peer", + "--prefer-offline", +]; + +export type InstallExtension = (name: string) => Promise; + +const installExtensionInjectable = getInjectable({ + id: "install-extension", + instantiate: (di): InstallExtension => { + const pathToNpmCli = di.inject(pathToNpmCliInjectable); + const extensionPackageRootDirectory = di.inject(extensionPackageRootDirectoryInjectable); + const readJsonFile = di.inject(readJsonFileInjectable); + const writeJsonFile = di.inject(writeJsonFileInjectable); + const joinPaths = di.inject(joinPathsInjectable); + const logger = di.inject(prefixedLoggerInjectable, "EXTENSION-INSTALLER"); + + const forkNpm = (...args: string[]) => new Promise((resolve, reject) => { + const child = fork(pathToNpmCli, args, { + cwd: extensionPackageRootDirectory, + silent: true, + env: {}, + }); + let stderr = ""; + + child.stderr?.on("data", data => { + stderr += String(data); + }); + + child.on("close", (code) => { + if (code !== 0) { + reject(new Error(stderr)); + } else { + resolve(); + } + }); + + child.on("error", error => { + reject(error); + }); + }); + + const packageJsonPath = joinPaths(extensionPackageRootDirectory, "package.json"); + + /** + * NOTES: + * - We have to keep the `package.json` because `npm install` removes files from `node_modules` + * if they are no longer in the `package.json` + * - In v6.2.X we saved bundled extensions as `"dependencies"` and external extensions as + * `"optionalDependencies"` at startup. This was done because `"optionalDependencies"` can + * fail to install and that is OK. + * - We continue to maintain this behavior here by only installing new dependencies as + * `"optionalDependencies"` + */ + const fixupPackageJson = once(async () => { + try { + const packageJson = await readJsonFile(packageJsonPath) as PackageJson; + + delete packageJson.dependencies; + + await writeJsonFile(packageJsonPath, packageJson); + } catch (error) { + if (isErrnoException(error) && error.code === "ENOENT") { + return; + } + + throw error; + } + }); + + const installLock = new AwaitLock(); + + return async (name) => { + await installLock.acquireAsync(); + await fixupPackageJson(); + + try { + logger.info(`installing package for extension "${name}"`); + await forkNpm(...baseNpmInstallArgs, name); + logger.info(`installed package for extension "${name}"`); + } finally { + installLock.release(); + } + }; + }, +}); + +export default installExtensionInjectable; From b5f0960ee170739aa610c7a58e6d7305aa35fd3c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 1 Mar 2023 07:30:07 -0800 Subject: [PATCH 18/21] Only run unit tests on linux for PRs (#7252) * Only run unit tests on linux for PRs Signed-off-by: Sebastian Malton * Run unit tests on all systems periodically Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- .github/workflows/cron-test.yaml | 54 ++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/cron-test.yaml diff --git a/.github/workflows/cron-test.yaml b/.github/workflows/cron-test.yaml new file mode 100644 index 0000000000..812da0368f --- /dev/null +++ b/.github/workflows/cron-test.yaml @@ -0,0 +1,54 @@ +name: Cron Test +on: + schedule: + - cron: "0 0 * * 1" # Run on the first day over every week +jobs: + test: + name: cron unit tests on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, macos-11, windows-2019] + node-version: [16.x] + steps: + - name: Checkout Release from lens + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Add the current IP address, long hostname and short hostname record to /etc/hosts file + if: runner.os == 'Linux' + run: | + echo -e "$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)\t$(hostname -f) $(hostname -s)" | sudo tee -a /etc/hosts + + - name: Using Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Get npm cache directory path + if: ${{ runner.os != 'Windows' }} + id: npm-cache-dir-path + shell: bash + run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + if: ${{ runner.os != 'Windows' }} + id: npm-cache # use this to check for `cache-hit` (`steps.npm-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.npm-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-npm- + + - uses: nick-fields/retry@v2 + name: Install dependencies + with: + timeout_minutes: 20 + max_attempts: 3 + retry_on: error + command: npm ci + + - run: npm run test:unit + name: Run tests diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 47567ac44b..4f43c1a343 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,7 +57,7 @@ jobs: - run: npm run test:unit name: Run tests - if: ${{ matrix.type == 'unit' }} + if: ${{ runner.os == 'Linux' && matrix.type == 'unit' }} - name: Install integration test dependencies id: minikube From c67230f32269af4d2d7c3a5b6cf605bae8ba1588 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 1 Mar 2023 07:46:08 -0800 Subject: [PATCH 19/21] Cleanup getDi and registering injectables (#7251) * Cleanup getDi and registering injectables Signed-off-by: Sebastian Malton * Create more explicit application - Make testing use more production code Signed-off-by: Sebastian Malton * Fix uses of getEnvironmentSpecificLegacyGlobalDiForExtensionApi Signed-off-by: Sebastian Malton * Fix unit tests Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- .../src/common/catalog-entities/web-link.ts | 4 +- packages/core/src/common/create-app.ts | 17 +++++++++ ...nformation-fake.injectable.testing-env.ts} | 0 .../vars/node-env.injectable.testing-env.ts | 14 +++++++ ...r-extension-api-with-modifications.test.ts | 4 +- .../legacy-global-di-for-extension-api.ts | 12 ++---- .../src/extensions/main-api/navigation.ts | 9 +---- packages/core/src/main/create-app.ts | 20 ++++------ packages/core/src/main/getDi.ts | 12 +++++- packages/core/src/main/getDiForUnitTesting.ts | 37 +++++------------- packages/core/src/main/library.ts | 3 +- .../core/src/main/register-injectables.ts | 8 ---- packages/core/src/renderer/create-app.ts | 19 +++++----- packages/core/src/renderer/getDi.tsx | 14 ++++++- .../core/src/renderer/getDiForUnitTesting.tsx | 38 +++++-------------- packages/core/src/renderer/library.ts | 3 +- .../core/src/renderer/register-injectables.ts | 11 ------ packages/open-lens/src/main/index.ts | 9 ++--- packages/open-lens/src/renderer/index.ts | 9 ++--- 19 files changed, 110 insertions(+), 133 deletions(-) create mode 100644 packages/core/src/common/create-app.ts rename packages/core/src/common/vars/{application-information-fake-injectable.ts => application-information-fake.injectable.testing-env.ts} (100%) create mode 100644 packages/core/src/common/vars/node-env.injectable.testing-env.ts diff --git a/packages/core/src/common/catalog-entities/web-link.ts b/packages/core/src/common/catalog-entities/web-link.ts index 7c83051c8b..833f05d65b 100644 --- a/packages/core/src/common/catalog-entities/web-link.ts +++ b/packages/core/src/common/catalog-entities/web-link.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import { getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog"; import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; import productNameInjectable from "../vars/product-name.injectable"; @@ -32,7 +32,7 @@ export class WebLink extends CatalogEntity Promise; + readonly di: DiContainerForInjection; +} + +export type CreateApplication = (config: ApplicationConfig) => Application; diff --git a/packages/core/src/common/vars/application-information-fake-injectable.ts b/packages/core/src/common/vars/application-information-fake.injectable.testing-env.ts similarity index 100% rename from packages/core/src/common/vars/application-information-fake-injectable.ts rename to packages/core/src/common/vars/application-information-fake.injectable.testing-env.ts diff --git a/packages/core/src/common/vars/node-env.injectable.testing-env.ts b/packages/core/src/common/vars/node-env.injectable.testing-env.ts new file mode 100644 index 0000000000..29231a4010 --- /dev/null +++ b/packages/core/src/common/vars/node-env.injectable.testing-env.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import nodeEnvInjectionToken from "./node-env-injection-token"; + +const nodeEnvFakeInjectable = getInjectable({ + id: "node-env-fake", + instantiate: () => "production", + injectionToken: nodeEnvInjectionToken, +}); + +export default nodeEnvFakeInjectable; diff --git a/packages/core/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts b/packages/core/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts index ebfc77f40e..5c6c60f8e0 100644 --- a/packages/core/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts +++ b/packages/core/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts @@ -9,7 +9,7 @@ import { createContainer, getInjectable, } from "@ogre-tools/injectable"; -import { Environments, setLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; +import { setLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; import { asLegacyGlobalObjectForExtensionApiWithModifications } from "./as-legacy-global-object-for-extension-api-with-modifications"; describe("asLegacyGlobalObjectForExtensionApiWithModifications", () => { @@ -25,7 +25,7 @@ describe("asLegacyGlobalObjectForExtensionApiWithModifications", () => { jest.spyOn(di, "inject"); - setLegacyGlobalDiForExtensionApi(di, Environments.renderer); + setLegacyGlobalDiForExtensionApi(di, "renderer"); someInjectable = getInjectable({ id: "some-injectable", diff --git a/packages/core/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts b/packages/core/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts index 4d7d9ca8f5..59d3a5465c 100644 --- a/packages/core/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts +++ b/packages/core/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts @@ -4,17 +4,11 @@ */ import type { DiContainer } from "@ogre-tools/injectable"; +export type Environments = "main" | "renderer"; + const legacyGlobalDis = new Map(); -export enum Environments { - renderer, - main, -} - -export const setLegacyGlobalDiForExtensionApi = ( - di: DiContainer, - environment: Environments, -) => { +export const setLegacyGlobalDiForExtensionApi = (di: DiContainer, environment: Environments) => { legacyGlobalDis.set(environment, di); }; diff --git a/packages/core/src/extensions/main-api/navigation.ts b/packages/core/src/extensions/main-api/navigation.ts index 375d2bac74..2bd870a262 100644 --- a/packages/core/src/extensions/main-api/navigation.ts +++ b/packages/core/src/extensions/main-api/navigation.ts @@ -3,16 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { - Environments, - getEnvironmentSpecificLegacyGlobalDiForExtensionApi, -} from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; - +import { getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import navigateInjectable from "../../main/start-main-application/lens-window/navigate.injectable"; export function navigate(url: string) { - const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main); - + const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi("main"); const navigate = di.inject(navigateInjectable); return navigate(url); diff --git a/packages/core/src/main/create-app.ts b/packages/core/src/main/create-app.ts index d0cccae5f8..57f764706f 100644 --- a/packages/core/src/main/create-app.ts +++ b/packages/core/src/main/create-app.ts @@ -3,20 +3,17 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DiContainer } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable"; import { runInAction } from "mobx"; +import type { CreateApplication } from "../common/create-app"; import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; +import { getDi } from "./getDi"; import { registerInjectables } from "./register-injectables"; import startMainApplicationInjectable from "./start-main-application/start-main-application.injectable"; -interface AppConfig { - di: DiContainer; - mode: string; -} - -export function createApp(conf: AppConfig) { - const { di, mode } = conf; +export const createApplication: CreateApplication = (config) => { + const { mode } = config; + const di = getDi(); runInAction(() => { di.register(getInjectable({ @@ -28,9 +25,8 @@ export function createApp(conf: AppConfig) { registerInjectables(di); }); - const startMainApplication = di.inject(startMainApplicationInjectable); - return { - start: () => startMainApplication(), + start: di.inject(startMainApplicationInjectable), + di, }; -} +}; diff --git a/packages/core/src/main/getDi.ts b/packages/core/src/main/getDi.ts index e325e66ed5..8d4b1ff57f 100644 --- a/packages/core/src/main/getDi.ts +++ b/packages/core/src/main/getDi.ts @@ -3,5 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { createContainer } from "@ogre-tools/injectable"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; +import { setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -export const getDi = () => createContainer("main"); +export const getDi = () => { + const environment = "main"; + const di = createContainer(environment); + + registerMobX(di); + setLegacyGlobalDiForExtensionApi(di, environment); + + return di; +}; diff --git a/packages/core/src/main/getDiForUnitTesting.ts b/packages/core/src/main/getDiForUnitTesting.ts index 5e923433fa..1e4c3291b9 100644 --- a/packages/core/src/main/getDiForUnitTesting.ts +++ b/packages/core/src/main/getDiForUnitTesting.ts @@ -4,9 +4,8 @@ */ import { chunk } from "lodash/fp"; -import type { DiContainer, Injectable } from "@ogre-tools/injectable"; -import { createContainer, isInjectable, getInjectable } from "@ogre-tools/injectable"; -import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import type { DiContainer } from "@ogre-tools/injectable"; +import { isInjectable } from "@ogre-tools/injectable"; import spawnInjectable from "./child-process/spawn.injectable"; import initializeExtensionsInjectable from "./start-main-application/runnables/initialize-extensions.injectable"; import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable"; @@ -25,46 +24,30 @@ import electronQuitAndInstallUpdateInjectable from "./electron-app/features/elec import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable"; import setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-quit.injectable"; import waitUntilBundledExtensionsAreLoadedInjectable from "./start-main-application/lens-window/application-window/wait-until-bundled-extensions-are-loaded.injectable"; -import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import electronInjectable from "./utils/resolve-system-proxy/electron.injectable"; import initializeClusterManagerInjectable from "./cluster/initialize-manager.injectable"; import type { GlobalOverride } from "../common/test-utils/get-global-override"; -import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; import { getOverrideFsWithFakes } from "../test-utils/override-fs-with-fakes"; -import { applicationInformationFakeInjectable } from "../common/vars/application-information-fake-injectable"; +import { getDi } from "./getDi"; export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) { const { doGeneralOverrides = false, } = opts; - const di = createContainer("main"); - - di.register(getInjectable({ - id: "node-env", - instantiate: () => "production", - injectionToken: nodeEnvInjectionToken, - })); - - setLegacyGlobalDiForExtensionApi(di, Environments.main); + const di = getDi(); di.preventSideEffects(); - const injectables = ( - global.injectablePaths.main.paths + runInAction(() => { + const injectables = global.injectablePaths.main.paths .map(path => require(path)) .flatMap(Object.values) - .filter(isInjectable) - ) as Injectable[]; + .filter(isInjectable); - registerMobX(di); - - runInAction(() => { - di.register(applicationInformationFakeInjectable); - - chunk(100)(injectables).forEach(chunkInjectables => { - di.register(...chunkInjectables); - }); + for (const block of chunk(100)(injectables)) { + di.register(...block); + } }); if (doGeneralOverrides) { diff --git a/packages/core/src/main/library.ts b/packages/core/src/main/library.ts index a8b4507b69..ab1f56528e 100644 --- a/packages/core/src/main/library.ts +++ b/packages/core/src/main/library.ts @@ -8,7 +8,8 @@ export { afterApplicationIsLoadedInjectionToken } from "./start-main-application export { beforeApplicationIsLoadingInjectionToken } from "./start-main-application/runnable-tokens/before-application-is-loading-injection-token"; export { beforeElectronIsReadyInjectionToken } from "./start-main-application/runnable-tokens/before-electron-is-ready-injection-token"; export { onLoadOfApplicationInjectionToken } from "./start-main-application/runnable-tokens/on-load-of-application-injection-token"; -export { createApp } from "./create-app"; +export { createApplication } from "./create-app"; +export type { CreateApplication, Application, ApplicationConfig } from "../common/create-app"; export * as Mobx from "mobx"; export * as mainExtensionApi from "../extensions/main-api"; export * as commonExtensionApi from "../extensions/common-api"; diff --git a/packages/core/src/main/register-injectables.ts b/packages/core/src/main/register-injectables.ts index 5ddcb640c1..eb18df3d3f 100644 --- a/packages/core/src/main/register-injectables.ts +++ b/packages/core/src/main/register-injectables.ts @@ -4,16 +4,10 @@ */ import type { DiContainer } from "@ogre-tools/injectable"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; -import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import { runInAction } from "mobx"; -import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; export function registerInjectables(di: DiContainer) { - setLegacyGlobalDiForExtensionApi(di, Environments.main); - runInAction(() => { - registerMobX(di); - autoRegister({ di, targetModule: module, @@ -25,6 +19,4 @@ export function registerInjectables(di: DiContainer) { ], }); }); - - return di; } diff --git a/packages/core/src/renderer/create-app.ts b/packages/core/src/renderer/create-app.ts index 3a511af5ec..e2064a7ae2 100644 --- a/packages/core/src/renderer/create-app.ts +++ b/packages/core/src/renderer/create-app.ts @@ -5,19 +5,16 @@ import "./components/app.scss"; import { bootstrap } from "./bootstrap"; -import type { DiContainer } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable"; import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; import { runInAction } from "mobx"; import { registerInjectables } from "./register-injectables"; +import type { CreateApplication } from "../common/create-app"; +import { getDi } from "./getDi"; -interface AppConfig { - di: DiContainer; - mode: string; -} - -export function createApp(conf: AppConfig) { - const { di, mode } = conf; +export const createApplication: CreateApplication = (config) => { + const { mode } = config; + const di = getDi(); runInAction(() => { di.register(getInjectable({ @@ -25,10 +22,12 @@ export function createApp(conf: AppConfig) { instantiate: () => mode, injectionToken: nodeEnvInjectionToken, })); + registerInjectables(di); }); - + return { start: () => bootstrap(di), + di, }; -} +}; diff --git a/packages/core/src/renderer/getDi.tsx b/packages/core/src/renderer/getDi.tsx index 77d665760c..22e545e780 100644 --- a/packages/core/src/renderer/getDi.tsx +++ b/packages/core/src/renderer/getDi.tsx @@ -4,5 +4,17 @@ */ import { createContainer } from "@ogre-tools/injectable"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; +import { registerInjectableReact } from "@ogre-tools/injectable-react"; +import { setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -export const getDi = () => createContainer("renderer"); +export const getDi = () => { + const environment = "renderer"; + const di = createContainer(environment); + + registerMobX(di); + registerInjectableReact(di); + setLegacyGlobalDiForExtensionApi(di, environment); + + return di; +}; diff --git a/packages/core/src/renderer/getDiForUnitTesting.tsx b/packages/core/src/renderer/getDiForUnitTesting.tsx index 15ddef9804..e1f6186874 100644 --- a/packages/core/src/renderer/getDiForUnitTesting.tsx +++ b/packages/core/src/renderer/getDiForUnitTesting.tsx @@ -4,9 +4,7 @@ */ import { noop, chunk } from "lodash/fp"; -import type { Injectable } from "@ogre-tools/injectable"; -import { createContainer, isInjectable, getInjectable } from "@ogre-tools/injectable"; -import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import { isInjectable } from "@ogre-tools/injectable"; import requestFromChannelInjectable from "./utils/channel/request-from-channel.injectable"; import { getOverrideFsWithFakes } from "../test-utils/override-fs-with-fakes"; import terminalSpawningPoolInjectable from "./components/dock/terminal/terminal-spawning-pool.injectable"; @@ -14,47 +12,29 @@ import hostedClusterIdInjectable from "./cluster-frame-context/hosted-cluster-id import { runInAction } from "mobx"; import requestAnimationFrameInjectable from "./components/animate/request-animation-frame.injectable"; import startTopbarStateSyncInjectable from "./components/layout/top-bar/start-state-sync.injectable"; -import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import watchHistoryStateInjectable from "./remote-helpers/watch-history-state.injectable"; import legacyOnChannelListenInjectable from "./ipc/legacy-channel-listen.injectable"; import type { GlobalOverride } from "../common/test-utils/get-global-override"; -import nodeEnvInjectionToken from "../common/vars/node-env-injection-token"; -import { applicationInformationFakeInjectable } from "../common/vars/application-information-fake-injectable"; -import { registerInjectableReact } from "@ogre-tools/injectable-react"; +import { getDi } from "./getDi"; export const getDiForUnitTesting = ( opts: { doGeneralOverrides?: boolean } = {}, ) => { const { doGeneralOverrides = false } = opts; - const di = createContainer("renderer"); - - di.register(getInjectable({ - id: "node-env", - instantiate: () => "production", - injectionToken: nodeEnvInjectionToken, - })); + const di = getDi(); di.preventSideEffects(); - setLegacyGlobalDiForExtensionApi(di, Environments.renderer); - - const injectables = ( - global.injectablePaths.renderer.paths + runInAction(() => { + const injectables = global.injectablePaths.renderer.paths .map(path => require(path)) .flatMap(Object.values) - .filter(isInjectable) - ) as Injectable[]; + .filter(isInjectable); - registerMobX(di); - registerInjectableReact(di); - - runInAction(() => { - di.register(applicationInformationFakeInjectable); - - chunk(100)(injectables).forEach((chunkInjectables) => { - di.register(...chunkInjectables); - }); + for (const block of chunk(100)(injectables)) { + di.register(...block); + } }); if (doGeneralOverrides) { diff --git a/packages/core/src/renderer/library.ts b/packages/core/src/renderer/library.ts index be72fc6c89..aa9c1fc907 100644 --- a/packages/core/src/renderer/library.ts +++ b/packages/core/src/renderer/library.ts @@ -14,4 +14,5 @@ export * as ReactRouter from "react-router"; export * as ReactRouterDom from "react-router-dom"; export * as rendererExtensionApi from "../extensions/renderer-api"; export * as commonExtensionApi from "../extensions/common-api"; -export { createApp } from "./create-app"; +export { createApplication } from "./create-app"; +export type { CreateApplication, Application, ApplicationConfig } from "../common/create-app"; diff --git a/packages/core/src/renderer/register-injectables.ts b/packages/core/src/renderer/register-injectables.ts index 41724d9413..0974a1a08b 100644 --- a/packages/core/src/renderer/register-injectables.ts +++ b/packages/core/src/renderer/register-injectables.ts @@ -5,19 +5,10 @@ import type { DiContainer } from "@ogre-tools/injectable"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; -import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; -import { registerInjectableReact } from "@ogre-tools/injectable-react"; import { runInAction } from "mobx"; -import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; export function registerInjectables(di: DiContainer) { - setLegacyGlobalDiForExtensionApi(di, Environments.renderer); - - registerMobX(di); - registerInjectableReact(di); - runInAction(() => { - autoRegister({ di, targetModule: module, @@ -29,6 +20,4 @@ export function registerInjectables(di: DiContainer) { ], }); }); - - return di; } diff --git a/packages/open-lens/src/main/index.ts b/packages/open-lens/src/main/index.ts index aca3da2bb4..39500b2240 100644 --- a/packages/open-lens/src/main/index.ts +++ b/packages/open-lens/src/main/index.ts @@ -1,18 +1,15 @@ -import { createContainer } from "@ogre-tools/injectable"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; import { runInAction } from "mobx"; -import { createApp, mainExtensionApi as Main, commonExtensionApi as Common } from "@k8slens/core/main"; +import { createApplication, mainExtensionApi as Main, commonExtensionApi as Common } from "@k8slens/core/main"; -const di = createContainer("main"); -const app = createApp({ - di, +const app = createApplication({ mode: process.env.NODE_ENV || "development" }); runInAction(() => { try { autoRegister({ - di, + di: app.di, targetModule: module, getRequireContexts: () => [ require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), diff --git a/packages/open-lens/src/renderer/index.ts b/packages/open-lens/src/renderer/index.ts index 2774644e94..ed28f26d3a 100644 --- a/packages/open-lens/src/renderer/index.ts +++ b/packages/open-lens/src/renderer/index.ts @@ -1,18 +1,15 @@ import "@k8slens/core/styles"; -import { createContainer } from "@ogre-tools/injectable"; import { runInAction } from "mobx"; -import { createApp, rendererExtensionApi as Renderer, commonExtensionApi as Common } from "@k8slens/core/renderer"; +import { createApplication, rendererExtensionApi as Renderer, commonExtensionApi as Common } from "@k8slens/core/renderer"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; -const di = createContainer("renderer"); -const app = createApp({ - di, +const app = createApplication({ mode: process.env.NODE_ENV || "development" }); runInAction(() => { autoRegister({ - di, + di: app.di, targetModule: module, getRequireContexts: () => [ require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), From 95b8c2aaa6017b7b19cc4ed9c25f196131d7e03c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 1 Mar 2023 08:12:09 -0800 Subject: [PATCH 20/21] For unit test workflow to only start on linux (#7254) Signed-off-by: Sebastian Malton --- .github/workflows/test.yml | 93 ++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f43c1a343..c733570f07 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,14 +7,80 @@ on: branches: - master jobs: - test: - name: ${{ matrix.type }} tests on ${{ matrix.os }} + integration-test: + name: integration tests on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-20.04, macos-11, windows-2019] - type: [unit, smoke] + node-version: [16.x] + steps: + - name: Checkout Release from lens + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Add the current IP address, long hostname and short hostname record to /etc/hosts file + if: runner.os == 'Linux' + run: | + echo -e "$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)\t$(hostname -f) $(hostname -s)" | sudo tee -a /etc/hosts + + - name: Using Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Get npm cache directory path + if: ${{ runner.os != 'Windows' }} + id: npm-cache-dir-path + shell: bash + run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + if: ${{ runner.os != 'Windows' }} + id: npm-cache # use this to check for `cache-hit` (`steps.npm-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.npm-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-npm- + + - uses: nick-fields/retry@v2 + name: Install dependencies + with: + timeout_minutes: 20 + max_attempts: 3 + retry_on: error + command: npm ci + + - name: Install integration test dependencies + id: minikube + uses: medyagh/setup-minikube@master + with: + minikube-version: latest + if: ${{ runner.os == 'Linux' }} + + - run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' npm run test:integration + name: Run Linux integration tests + if: ${{ runner.os == 'Linux' }} + + - run: npm run test:integration + name: Run macOS integration tests + shell: bash + if: ${{ runner.os == 'macOS' }} + + - run: npm run test:integration + name: Run Windows integration tests + if: ${{ runner.os == 'Windows' }} + + unit-test: + name: unit tests on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04] node-version: [16.x] steps: - name: Checkout Release from lens @@ -57,24 +123,3 @@ jobs: - run: npm run test:unit name: Run tests - if: ${{ runner.os == 'Linux' && matrix.type == 'unit' }} - - - name: Install integration test dependencies - id: minikube - uses: medyagh/setup-minikube@master - with: - minikube-version: latest - if: ${{ runner.os == 'Linux' && matrix.type == 'smoke' }} - - - run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' npm run test:integration - name: Run Linux integration tests - if: ${{ runner.os == 'Linux' && matrix.type == 'smoke' }} - - - run: npm run test:integration - name: Run macOS integration tests - shell: bash - if: ${{ runner.os == 'macOS' && matrix.type == 'smoke' }} - - - run: npm run test:integration - name: Run Windows integration tests - if: ${{ runner.os == 'Windows' && matrix.type == 'smoke' }} From 6df01ba468b6970ef4b13a8ae8b1982e32ce7c6a Mon Sep 17 00:00:00 2001 From: Juho Heikka Date: Wed, 1 Mar 2023 18:49:18 +0200 Subject: [PATCH 21/21] Fix cluster metadata detectors (#7255) Signed-off-by: Juho Heikka --- .../detect-cluster-metadata.injectable.ts | 12 +- .../detect-cluster-metadata.test.ts | 107 ++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts diff --git a/packages/core/src/main/cluster-detectors/detect-cluster-metadata.injectable.ts b/packages/core/src/main/cluster-detectors/detect-cluster-metadata.injectable.ts index 3665f74f52..cfc492942f 100644 --- a/packages/core/src/main/cluster-detectors/detect-cluster-metadata.injectable.ts +++ b/packages/core/src/main/cluster-detectors/detect-cluster-metadata.injectable.ts @@ -22,7 +22,10 @@ const pickHighestAccuracy = (prev: ClusterDetectionResult, curr: ClusterDetectio const detectMetadataWithFor = (cluster: Cluster) => async (clusterMetadataDetector: ClusterMetadataDetector) => { try { - return await clusterMetadataDetector.detect(cluster); + return { + key: clusterMetadataDetector.key, + result: await clusterMetadataDetector.detect(cluster), + }; } catch { return null; } @@ -39,7 +42,12 @@ const detectClusterMetadataInjectable = getInjectable({ filter(isDefined), (arg) => groupBy(arg, "key"), (arg) => object.entries(arg), - map(([ key, results ]) => [key, reduce(results, pickHighestAccuracy)] as const), + map(([ key, detectionResults ]) => { + const results = detectionResults.map(({ result }) => result as ClusterDetectionResult); + const highestAccuracyResult = reduce(results, pickHighestAccuracy)?.value; + + return [key, highestAccuracyResult] as const; + }), filter(hasDefinedTupleValue), ); diff --git a/packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts b/packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts new file mode 100644 index 0000000000..387e365227 --- /dev/null +++ b/packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts @@ -0,0 +1,107 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { AppPaths } from "../../common/app-paths/app-path-injection-token"; +import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable"; +import directoryForKubeConfigsInjectable from "../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; +import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import { ClusterMetadataKey } from "../../common/cluster-types"; +import type { Cluster } from "../../common/cluster/cluster"; +import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token"; +import { getDiForUnitTesting } from "../getDiForUnitTesting"; +import clusterDistributionDetectorInjectable from "./cluster-distribution-detector.injectable"; +import clusterIdDetectorFactoryInjectable from "./cluster-id-detector.injectable"; +import clusterLastSeenDetectorInjectable from "./cluster-last-seen-detector.injectable"; +import clusterNodeCountDetectorInjectable from "./cluster-nodes-count-detector.injectable"; +import type { DetectClusterMetadata } from "./detect-cluster-metadata.injectable"; +import detectClusterMetadataInjectable from "./detect-cluster-metadata.injectable"; +import requestClusterVersionInjectable from "./request-cluster-version.injectable"; + +describe("detect-cluster-metadata", () => { + let detectClusterMetadata: DetectClusterMetadata; + + let cluster: Cluster; + + beforeEach(async () => { + const di = getDiForUnitTesting({ doGeneralOverrides: true }); + + const lastSeenDetectMock = jest.fn().mockReturnValue(Promise.resolve({ value: "some-time-stamp", accuracy: 100 })); + const nodeCountDetectMock = jest.fn().mockReturnValue(Promise.resolve({ value: 42, accuracy: 100 })); + const clusterIdDetectMock = jest.fn().mockReturnValue(Promise.resolve({ value: "some-cluster-id", accuracy: 100 })); + const distributionDetectMock = jest.fn().mockReturnValue(Promise.resolve({ value: "some-distribution", accuracy: 100 })); + + di.override(clusterLastSeenDetectorInjectable, () => { + return { + key: ClusterMetadataKey.LAST_SEEN, + detect: lastSeenDetectMock, + }; + }); + + di.override(requestClusterVersionInjectable, () => () => Promise.resolve("some-cluster-version")); + + di.override(clusterNodeCountDetectorInjectable, () => ({ + key: ClusterMetadataKey.NODES_COUNT, + detect: nodeCountDetectMock, + })); + + di.override(clusterIdDetectorFactoryInjectable, () => ({ + key: ClusterMetadataKey.CLUSTER_ID, + detect: clusterIdDetectMock, + })); + + di.override(clusterDistributionDetectorInjectable, () => ({ + key: ClusterMetadataKey.DISTRIBUTION, + detect: distributionDetectMock, + })); + + di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); + di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); + di.override(appPathsStateInjectable, () => ({ + get: () => ({} as AppPaths), + set: () => {}, + })); + + detectClusterMetadata = di.inject(detectClusterMetadataInjectable); + + const createCluster = di.inject(createClusterInjectionToken); + + cluster = createCluster({ + id: "some-id", + contextName: "some-context", + kubeConfigPath: "minikube-config.yml", + }, { + clusterServerUrl: "foo", + }); + }); + + it("given some cluster, last seen time stamp is added to the metadata", async () => { + const metadata = await detectClusterMetadata(cluster); + + expect(metadata.lastSeen).toEqual("some-time-stamp"); + }); + + it("given some cluster, cluster version is added to the metadata", async () => { + const metadata = await detectClusterMetadata(cluster); + + expect(metadata.version).toEqual("some-cluster-version"); + }); + + it("given some cluster, id is added to the metadata", async () => { + const metadata = await detectClusterMetadata(cluster); + + expect(metadata.id).toEqual("some-cluster-id"); + }); + + it("given some cluster, node count is added to the metadata", async () => { + const metadata = await detectClusterMetadata(cluster); + + expect(metadata.nodes).toEqual(42); + }); + + it("given some cluster, distribution is added to the metadata", async () => { + const metadata = await detectClusterMetadata(cluster); + + expect(metadata.distribution).toEqual("some-distribution"); + }); +});