1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Clean up versioning and packageJson uses

- Rename packageJsonInjectable -> applicationInformationInjectable

- Add injectables for all sub items

- Introduce storeMigrationVersionInjectable for BaseStores

- Introduce extensionApiVersionInjectable

- Use buildVersionInjectable in more places, such as in telemetry

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-09-07 09:37:55 -04:00
parent a784ca70b1
commit cedb0c4fa9
79 changed files with 684 additions and 491 deletions

View File

@ -208,7 +208,8 @@
"lens"
],
"role": "Viewer"
}
},
"publish": []
},
"resolutions": {
"@astronautlabs/jsonpath/underscore": "^1.12.1"

View File

@ -18,13 +18,13 @@ import { createClusterInjectionToken } from "../cluster/create-cluster-injection
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../vars/app-version.injectable";
import assert from "assert";
import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable";
import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable";
import normalizedPlatformInjectable from "../vars/normalized-platform.injectable";
import fsInjectable from "../fs/fs.injectable";
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
console = new Console(stdout, stderr);
@ -372,7 +372,7 @@ users:
mockFs(mockOpts);
mainDi.override(appVersionInjectable, () => "3.6.0");
mainDi.override(storeMigrationVersionInjectable, () => "3.6.0");
createCluster = mainDi.inject(createClusterInjectionToken);

View File

@ -8,7 +8,6 @@ import mockFs from "mock-fs";
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../vars/app-version.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import hotbarStoreInjectable from "../hotbars/store.injectable";
import type { HotbarStore } from "../hotbars/store";
@ -19,6 +18,7 @@ import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-
import loggerInjectable from "../logger.injectable";
import type { Logger } from "../logger";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
return {
@ -348,7 +348,7 @@ describe("HotbarStore", () => {
mockFs(configurationToBeMigrated);
di.override(appVersionInjectable, () => "5.0.0-beta.10");
di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10");
hotbarStore = di.inject(hotbarStoreInjectable);

View File

@ -23,8 +23,6 @@ jest.mock("electron", () => ({
import type { UserStore } from "../user-store";
import { Console } from "console";
import { SemVer } from "semver";
import electron from "electron";
import { stdout, stderr } from "process";
import userStoreInjectable from "../user-store/user-store.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
@ -34,7 +32,7 @@ import { defaultThemeId } from "../vars";
import writeFileInjectable from "../fs/write-file.injectable";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../vars/app-version.injectable";
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
console = new Console(stdout, stderr);
@ -86,13 +84,6 @@ describe("user store tests", () => {
userStore.resetTheme();
expect(userStore.colorTheme).toBe(defaultThemeId);
});
it("correctly calculates if the last seen version is an old release", () => {
expect(userStore.isNewVersion).toBe(true);
userStore.lastSeenAppVersion = (new SemVer(electron.app.getVersion())).inc("major").format();
expect(userStore.isNewVersion).toBe(false);
});
});
describe("migrations", () => {
@ -125,7 +116,7 @@ describe("user store tests", () => {
},
});
di.override(appVersionInjectable, () => "10.0.0");
di.override(storeMigrationVersionInjectable, () => "10.0.0");
userStore = di.inject(userStoreInjectable);
});

View File

@ -7,7 +7,6 @@ import { appPathsInjectionToken } from "./app-path-injection-token";
import getElectronAppPathInjectable from "../../main/app-paths/get-electron-app-path/get-electron-app-path.injectable";
import type { PathName } from "./app-path-names";
import setElectronAppPathInjectable from "../../main/app-paths/set-electron-app-path/set-electron-app-path.injectable";
import appNameInjectable from "../../main/app-paths/app-name/app-name.injectable";
import directoryForIntegrationTestingInjectable from "../../main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
@ -53,8 +52,6 @@ describe("app-paths", () => {
defaultAppPathsStub[key] = path;
},
);
mainDi.override(appNameInjectable, () => "some-app-name");
});
});

View File

@ -19,7 +19,7 @@ import { kebabCase } from "lodash";
import { getLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import directoryForUserDataInjectable from "./app-paths/directory-for-user-data/directory-for-user-data.injectable";
import getConfigurationFileModelInjectable from "./get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "./vars/app-version.injectable";
import storeMigrationVersionInjectable from "./vars/store-migration-version.injectable";
export interface BaseStoreParams<T> extends ConfOptions<T> {
syncOptions?: {
@ -60,7 +60,7 @@ export abstract class BaseStore<T extends object> extends Singleton {
this.storeConfig = getConfigurationFileModel({
projectName: "lens",
projectVersion: di.inject(appVersionInjectable),
projectVersion: di.inject(storeMigrationVersionInjectable),
cwd: this.cwd(),
...this.params,
});

View File

@ -3,9 +3,10 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getLegacyGlobalDiForExtensionApi } 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 { productName } from "../vars";
import productNameInjectable from "../vars/product-name.injectable";
import { WeblinkStore } from "../weblink-store";
export type WebLinkStatusPhase = "available" | "unavailable";
@ -30,6 +31,9 @@ export class WebLink extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus,
}
onContextMenuOpen(context: CatalogEntityContextMenuContext) {
const di = getLegacyGlobalDiForExtensionApi();
const productName = di.inject(productNameInjectable);
if (this.metadata.source === "local") {
context.menuItems.push({
title: "Delete",

View File

@ -0,0 +1,9 @@
/**
* 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 initializeSentryReportingWithInjectable from "./initialize-sentry-reporting.injectable";
export default getGlobalOverride(initializeSentryReportingWithInjectable, () => () => {});

View File

@ -0,0 +1,65 @@
/**
* 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 type { ElectronMainOptions } from "@sentry/electron/main";
import type { BrowserOptions } from "@sentry/electron/renderer";
import isProductionInjectable from "../vars/is-production.injectable";
import sentryDataSourceNameInjectable from "../vars/sentry-dsn-url.injectable";
import { Dedupe, Offline } from "@sentry/integrations";
import { inspect } from "util";
import userStoreInjectable from "../user-store/user-store.injectable";
export type InitializeSentryReportingWith = (initSentry: (opts: BrowserOptions | ElectronMainOptions) => void) => void;
const mapProcessName = (type: "browser" | "renderer" | "worker") => type === "browser" ? "main" : type;
const initializeSentryReportingWithInjectable = getInjectable({
id: "initialize-sentry-reporting-with",
instantiate: (di): InitializeSentryReportingWith => {
const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable);
const isProduction = di.inject(isProductionInjectable);
if (!sentryDataSourceName) {
return () => {};
}
return (initSentry) => initSentry({
beforeSend: (event) => {
// TODO: remove loading from userStoreInjectable so that this can be moved out
const userStore = di.inject(userStoreInjectable);
if (userStore.allowErrorReporting) {
return event;
}
/**
* Directly write to stdout so that no other integrations capture this and create an infinite loop
*/
process.stdout.write(`🔒 [SENTRY-BEFORE-SEND-HOOK]: Sentry event is caught but not sent to server.`);
process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === START OF SENTRY EVENT ===");
process.stdout.write(inspect(event, false, null, true));
process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === END OF SENTRY EVENT ===");
// if return null, the event won't be sent
// ref https://github.com/getsentry/sentry-javascript/issues/2039
return null;
},
dsn: sentryDataSourceName,
integrations: [
new Dedupe(),
new Offline(),
],
initialScope: {
tags: {
"process": mapProcessName(process.type),
},
},
environment: isProduction ? "production" : "development",
});
},
causesSideEffects: true,
});
export default initializeSentryReportingWithInjectable;

View File

@ -11,8 +11,9 @@ import logger from "../../main/logger";
import { app } from "electron";
import { ClusterStore } from "../cluster-store/cluster-store";
import yaml from "js-yaml";
import { productName } from "../vars";
import { requestKubectlApplyAll, requestKubectlDeleteAll } from "../../renderer/ipc";
import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import productNameInjectable from "../vars/product-name.injectable";
export class ResourceStack {
constructor(protected cluster: KubernetesCluster, protected name: string) {}
@ -97,6 +98,8 @@ export class ResourceStack {
protected async renderTemplates(folderPath: string, templateContext: any): Promise<string[]> {
const resources: string[] = [];
const di = getLegacyGlobalDiForExtensionApi();
const productName = di.inject(productNameInjectable);
logger.info(`[RESOURCE-STACK]: render templates from ${folderPath}`);
const files = await fse.readdir(folderPath);

View File

@ -1,67 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { Dedupe, Offline } from "@sentry/integrations";
import { sentryDsn, isProduction } from "./vars";
import { UserStore } from "./user-store";
import { inspect } from "util";
import type { BrowserOptions } from "@sentry/electron/renderer";
import type { ElectronMainOptions } from "@sentry/electron/main";
/**
* "Translate" 'browser' to 'main' as Lens developer more familiar with the term 'main'
*/
function mapProcessName(processType: string) {
if (processType === "browser") {
return "main";
}
return processType;
}
/**
* Initialize Sentry for the current process so to send errors for debugging.
*/
export function initializeSentryReporting(init: (opts: BrowserOptions | ElectronMainOptions) => void) {
const processName = mapProcessName(process.type);
if (!sentryDsn) {
return; // do nothing if not configured to avoid uncaught error in dev mode
}
init({
beforeSend: (event) => {
// default to false, in case instance of UserStore is not created (yet)
const allowErrorReporting = UserStore.getInstance(false)?.allowErrorReporting ?? false;
if (allowErrorReporting) {
return event;
}
/**
* Directly write to stdout so that no other integrations capture this and create an infinite loop
*/
process.stdout.write(`🔒 [SENTRY-BEFORE-SEND-HOOK]: allowErrorReporting: ${allowErrorReporting}. Sentry event is caught but not sent to server.`);
process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === START OF SENTRY EVENT ===");
process.stdout.write(inspect(event, false, null, true));
process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === END OF SENTRY EVENT ===");
// if return null, the event won't be sent
// ref https://github.com/getsentry/sentry-javascript/issues/2039
return null;
},
dsn: sentryDsn,
integrations: [
new Dedupe(),
new Offline(),
],
initialScope: {
tags: {
"process": processName,
},
},
environment: isProduction ? "production" : "development",
});
}

View File

@ -4,13 +4,10 @@
*/
import { app } from "electron";
import semver from "semver";
import { action, computed, observable, reaction, makeObservable, isObservableArray, isObservableSet, isObservableMap } from "mobx";
import { BaseStore } from "../base-store";
import migrations from "../../migrations/user-store";
import { getAppVersion } from "../utils/app-version";
import { kubeConfigDefaultPath } from "../kube-helpers";
import { appEventBus } from "../app-event-bus/event-bus";
import { getOrInsertSet, toggle, toJS, object } from "../../renderer/utils";
import { DESCRIPTORS } from "./preferences-helpers";
import type { UserPreferencesModel, StoreType } from "./preferences-helpers";
@ -24,7 +21,7 @@ export interface UserStoreModel {
}
interface Dependencies {
selectedUpdateChannel: SelectedUpdateChannel;
readonly selectedUpdateChannel: SelectedUpdateChannel;
}
export class UserStore extends BaseStore<UserStoreModel> /* implements UserStoreFlatModel (when strict null is enabled) */ {
@ -98,10 +95,6 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
*/
@observable syncKubeconfigEntries!: StoreType<typeof DESCRIPTORS["syncKubeconfigEntries"]>;
@computed get isNewVersion() {
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
}
@computed get resolvedShell(): string | undefined {
return this.shell || process.env.SHELL || process.env.PTYSHELL;
}
@ -151,12 +144,6 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
this.colorTheme = DESCRIPTORS.colorTheme.fromStore(undefined);
}
@action
saveLastSeenAppVersion() {
appEventBus.emit({ name: "app", action: "whats-new-seen" });
this.lastSeenAppVersion = getAppVersion();
}
@action
protected fromStore({ lastSeenAppVersion, preferences }: Partial<UserStoreModel> = {}) {
logger.debug("UserStore.fromStore()", { lastSeenAppVersion, preferences });

View File

@ -4,15 +4,6 @@
*/
import requestPromise from "request-promise-native";
import packageInfo from "../../../package.json";
export function getAppVersion(): string {
return packageInfo.version;
}
export function getBundledKubectlVersion(): string {
return packageInfo.config.bundledKubectlVersion;
}
export async function getAppVersionFromProxyServer(proxyPort: number): Promise<string> {
const response = await requestPromise({

View File

@ -5,7 +5,6 @@
// App's common configuration for any process (main, renderer, build pipeline, etc.)
import path from "path";
import packageInfo from "../../package.json";
import type { ThemeId } from "../renderer/themes/store";
import { lazyInitialized } from "./utils/lazy-initialized";
@ -25,7 +24,6 @@ export const isWindows = process.platform === "win32";
export const isLinux = process.platform === "linux";
export const isDebugging = ["true", "1", "yes", "y", "on"].includes((process.env.DEBUG ?? "").toLowerCase());
export const isSnap = !!process.env.SNAP;
/**
* @deprecated Switch to using isTestEnvInjectable
@ -42,13 +40,6 @@ export const isProduction = process.env.NODE_ENV === "production";
*/
export const isDevelopment = !isTestEnv && !isProduction;
export const productName = packageInfo.productName;
/**
* @deprecated Switch to using appNameInjectable
*/
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
export const publicPath = "/build/" as string;
export const defaultThemeId: ThemeId = "lens-dark";
export const defaultFontSize = 12;
@ -139,6 +130,3 @@ export const lensBlogWeblinkId = "lens-blog-link";
export const kubernetesDocumentationWeblinkId = "kubernetes-documentation-link";
export const docsUrl = "https://docs.k8slens.dev/main" as string;
export const sentryDsn = packageInfo.config?.sentryDsn ?? "";
export const contentSecurityPolicy = packageInfo.config?.contentSecurityPolicy ?? "";

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import isDevelopmentInjectable from "../../../common/vars/is-development.injectable";
import isDevelopmentInjectable from "./is-development.injectable";
import productNameInjectable from "./product-name.injectable";
const appNameInjectable = getInjectable({
@ -15,8 +15,6 @@ const appNameInjectable = getInjectable({
return `${productName}${isDevelopment ? "Dev" : ""}`;
},
causesSideEffects: true,
});
export default appNameInjectable;

View File

@ -1,14 +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 { SemVer } from "semver";
import appVersionInjectable from "./app-version.injectable";
const appSemanticVersionInjectable = getInjectable({
id: "app-semantic-version",
instantiate: (di) => new SemVer(di.inject(appVersionInjectable)),
});
export default appSemanticVersionInjectable;

View File

@ -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 packageJsonInjectable from "./package-json.injectable";
const appVersionInjectable = getInjectable({
id: "app-version",
instantiate: (di) => di.inject(packageJsonInjectable).version,
});
export default appVersionInjectable;

View File

@ -0,0 +1,13 @@
/**
* 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 applicationInformationInjectable from "./application-information.injectable";
const applicationCopyrightInjectable = getInjectable({
id: "application-copyright",
instantiate: (di) => di.inject(applicationInformationInjectable).copyright,
});
export default applicationCopyrightInjectable;

View File

@ -0,0 +1,13 @@
/**
* 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 applicationInformationInjectable from "./application-information.injectable";
const applicationDescriptionInjectable = getInjectable({
id: "application-description",
instantiate: (di) => di.inject(applicationInformationInjectable).description,
});
export default applicationDescriptionInjectable;

View File

@ -0,0 +1,22 @@
/**
* 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, () => ({
productName: "some-product-name",
version: "6.0.0",
build: {},
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:",
},
copyright: "some-copyright-information",
description: "some-descriptive-text",
}));

View File

@ -0,0 +1,22 @@
/**
* 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";
export type ApplicationInformation = Pick<typeof packageJson, "version" | "config" | "productName" | "copyright" | "description"> & {
build: Partial<typeof packageJson["build"]>;
};
const applicationInformationInjectable = getInjectable({
id: "application-information",
instantiate: (): ApplicationInformation => {
const { version, config, productName, build, copyright, description } = packageJson;
return { version, config, productName, build, copyright, description };
},
causesSideEffects: true,
});
export default applicationInformationInjectable;

View File

@ -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, getInjectable } from "@ogre-tools/injectable";
import { SemVer } from "semver";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
export const buildVersionInjectionToken = getInjectionToken<string>({
id: "build-version-token",
});
export const buildVersionChannel: RequestChannel<void, string> = {
id: "build-version",
};
const buildSemanticVersionInjectable = getInjectable({
id: "build-semantic-version",
instantiate: (di) => new SemVer(di.inject(buildVersionInjectionToken)),
});
export default buildSemanticVersionInjectable;

View File

@ -0,0 +1,13 @@
/**
* 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 applicationInformationInjectable from "./application-information.injectable";
const bundledKubectlVersionInjectable = getInjectable({
id: "bundled-kubectl-version",
instantiate: (di) => di.inject(applicationInformationInjectable).config.bundledKubectlVersion,
});
export default bundledKubectlVersionInjectable;

View File

@ -0,0 +1,13 @@
/**
* 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 applicationInformationInjectable from "./application-information.injectable";
const contentSecurityPolicyInjectable = getInjectable({
id: "content-security-policy",
instantiate: (di) => di.inject(applicationInformationInjectable).config.contentSecurityPolicy,
});
export default contentSecurityPolicyInjectable;

View File

@ -0,0 +1,18 @@
/**
* 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 { SemVer } from "semver";
import applicationInformationInjectable from "./application-information.injectable";
const extensionApiVersionInjectable = getInjectable({
id: "extension-api-version",
instantiate: (di) => {
const { major, minor, patch } = new SemVer(di.inject(applicationInformationInjectable).version);
return `${major}.${minor}.${patch}`;
},
});
export default extensionApiVersionInjectable;

View File

@ -0,0 +1,9 @@
/**
* 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 isSnapInjectable from "./is-snap.injectable";
export default getGlobalOverride(isSnapInjectable, () => false);

View File

@ -3,12 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import packageJson from "../../../package.json";
const packageJsonInjectable = getInjectable({
id: "package-json",
instantiate: () => packageJson,
const isSnapInjectable = getInjectable({
id: "is-snap",
instantiate: () => Boolean(process.env.SNAP),
causesSideEffects: true,
});
export default packageJsonInjectable;
export default isSnapInjectable;

View File

@ -3,12 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import packageInfo from "../../../../package.json";
import applicationInformationInjectable from "./application-information.injectable";
const productNameInjectable = getInjectable({
id: "product-name",
instantiate: () => packageInfo.productName,
causesSideEffects: true,
instantiate: (di) => di.inject(applicationInformationInjectable).productName,
});
export default productNameInjectable;

View File

@ -0,0 +1,13 @@
/**
* 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 applicationInformationInjectable from "./application-information.injectable";
const sentryDataSourceNameInjectable = getInjectable({
id: "sentry-data-source-name",
instantiate: (di) => di.inject(applicationInformationInjectable).config.sentryDsn,
});
export default sentryDataSourceNameInjectable;

View File

@ -0,0 +1,13 @@
/**
* 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 applicationInformationInjectable from "./application-information.injectable";
const storeMigrationVersionInjectable = getInjectable({
id: "store-migration-version",
instantiate: (di) => di.inject(applicationInformationInjectable).version,
});
export default storeMigrationVersionInjectable;

View File

@ -3,65 +3,53 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import assert from "assert";
import semver from "semver";
import { isCompatibleExtension } from "../extension-discovery/is-compatible-extension/is-compatible-extension";
import type { LensExtensionManifest } from "../lens-extension";
describe("Extension/App versions compatibility checks", () => {
it("is compatible with exact version matching", () => {
expect(isCompatible({ extLensEngineVersion: "5.5.0", appVersion: "5.5.0" })).toBeTruthy();
expect(isCompatible({ extLensEngineVersion: "5.5.0", extensionApiVersion: "5.5.0" })).toBeTruthy();
});
it("is compatible with upper %PATCH versions of base app", () => {
expect(isCompatible({ extLensEngineVersion: "5.5.0", appVersion: "5.5.5" })).toBeTruthy();
expect(isCompatible({ extLensEngineVersion: "5.5.0", extensionApiVersion: "5.5.5" })).toBeTruthy();
});
it("is compatible with higher %MINOR version of base app", () => {
expect(isCompatible({ extLensEngineVersion: "5.5.0", appVersion: "5.6.0" })).toBeTruthy();
expect(isCompatible({ extLensEngineVersion: "5.5.0", extensionApiVersion: "5.6.0" })).toBeTruthy();
});
it("is not compatible with higher %MAJOR version of base app", () => {
expect(isCompatible({ extLensEngineVersion: "5.6.0", appVersion: "6.0.0" })).toBeFalsy(); // extension for lens@5 not compatible with lens@6
expect(isCompatible({ extLensEngineVersion: "6.0.0", appVersion: "5.6.0" })).toBeFalsy();
});
it("is compatible with lensEngine with prerelease", () => {
expect(isCompatible({
extLensEngineVersion: "^5.4.0-alpha.0",
appVersion: "5.5.0-alpha.0",
})).toBeTruthy();
expect(isCompatible({ extLensEngineVersion: "5.6.0", extensionApiVersion: "6.0.0" })).toBeFalsy(); // extension for lens@5 not compatible with lens@6
expect(isCompatible({ extLensEngineVersion: "6.0.0", extensionApiVersion: "5.6.0" })).toBeFalsy();
});
it("supports short version format for manifest.engines.lens", () => {
expect(isCompatible({ extLensEngineVersion: "5.5", appVersion: "5.5.1" })).toBeTruthy();
expect(isCompatible({ extLensEngineVersion: "5.5", extensionApiVersion: "5.5.1" })).toBeTruthy();
});
it("throws for incorrect or not supported version format", () => {
expect(() => isCompatible({
extLensEngineVersion: ">=2.0",
appVersion: "2.0",
extensionApiVersion: "2.0",
})).toThrow(/Invalid format/i);
expect(() => isCompatible({
extLensEngineVersion: "~2.0",
appVersion: "2.0",
extensionApiVersion: "2.0",
})).toThrow(/Invalid format/i);
expect(() => isCompatible({
extLensEngineVersion: "*",
appVersion: "1.0",
extensionApiVersion: "1.0",
})).toThrow(/Invalid format/i);
});
});
function isCompatible({ extLensEngineVersion = "^1.0", appVersion = "1.0" } = {}): boolean {
const appSemVer = semver.coerce(appVersion);
function isCompatible({ extLensEngineVersion = "^1.0", extensionApiVersion = "1.0" } = {}): boolean {
const extensionManifestMock = getExtensionManifestMock(extLensEngineVersion);
assert(appSemVer);
return isCompatibleExtension({ appSemVer })(extensionManifestMock);
return isCompatibleExtension({ extensionApiVersion })(extensionManifestMock);
}
function getExtensionManifestMock(lensEngine = "1.0"): LensExtensionManifest {

View File

@ -3,14 +3,53 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getAppVersion } from "../../common/utils";
import appNameInjectable from "../../common/vars/app-name.injectable";
import isLinuxInjectable from "../../common/vars/is-linux.injectable";
import isMacInjectable from "../../common/vars/is-mac.injectable";
import isSnapInjectable from "../../common/vars/is-snap.injectable";
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 * as Preferences from "./user-preferences";
import { slackUrl, issuesTrackerUrl } from "../../common/vars";
import { buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable";
export const version = getAppVersion();
export { isSnap, isWindows, isMac, isLinux, appName, slackUrl, issuesTrackerUrl } from "../../common/vars";
const App = {
Preferences,
getEnabledExtensions: asLegacyGlobalFunctionForExtensionApi(getEnabledExtensionsInjectable),
get version() {
const di = getLegacyGlobalDiForExtensionApi();
export const getEnabledExtensions = asLegacyGlobalFunctionForExtensionApi(getEnabledExtensionsInjectable);
return di.inject(buildVersionInjectionToken);
},
get appName() {
const di = getLegacyGlobalDiForExtensionApi();
export { Preferences };
return di.inject(appNameInjectable);
},
get isSnap() {
const di = getLegacyGlobalDiForExtensionApi();
return di.inject(isSnapInjectable);
},
get isWindows() {
const di = getLegacyGlobalDiForExtensionApi();
return di.inject(isWindowsInjectable);
},
get isMac() {
const di = getLegacyGlobalDiForExtensionApi();
return di.inject(isMacInjectable);
},
get isLinux() {
const di = getLegacyGlobalDiForExtensionApi();
return di.inject(isLinuxInjectable);
},
slackUrl,
issuesTrackerUrl,
};
export default App;

View File

@ -4,7 +4,7 @@
*/
// APIs
import * as App from "./app";
import App from "./app";
import * as EventBus from "./event-bus";
import * as Store from "./stores";
import * as Util from "./utils";

View File

@ -4,9 +4,11 @@
*/
import openLinkInBrowserInjectable from "../../common/utils/open-link-in-browser.injectable";
import buildVersionInjectable from "../../main/vars/build-version.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";
export { Singleton, getAppVersion } from "../../common/utils";
export { Singleton } from "../../common/utils";
export { prevDefault, stopPropagation } from "../../renderer/utils/prevDefault";
export { cssNames } from "../../renderer/utils/cssNames";
@ -15,3 +17,12 @@ export { cssNames } from "../../renderer/utils/cssNames";
*/
export const openExternal = asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable);
export const openBrowser = asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable);
/**
* @deprecated use `Common.App.version` instead
*/
export function getAppVersion() {
const di = getLegacyGlobalDiForExtensionApi();
return di.inject(buildVersionInjectable);
}

View File

@ -20,21 +20,20 @@ import watchInjectable from "../../common/fs/watch/watch.injectable";
const extensionDiscoveryInjectable = getInjectable({
id: "extension-discovery",
instantiate: (di) =>
new ExtensionDiscovery({
extensionLoader: di.inject(extensionLoaderInjectable),
extensionsStore: di.inject(extensionsStoreInjectable),
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
isCompatibleExtension: di.inject(isCompatibleExtensionInjectable),
installExtension: di.inject(installExtensionInjectable),
installExtensions: di.inject(installExtensionsInjectable),
extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable),
staticFilesDirectory: di.inject(staticFilesDirectoryInjectable),
readJsonFile: di.inject(readJsonFileInjectable),
pathExists: di.inject(pathExistsInjectable),
watch: di.inject(watchInjectable),
logger: di.inject(loggerInjectable),
}),
instantiate: (di) => new ExtensionDiscovery({
extensionLoader: di.inject(extensionLoaderInjectable),
extensionsStore: di.inject(extensionsStoreInjectable),
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
isCompatibleExtension: di.inject(isCompatibleExtensionInjectable),
installExtension: di.inject(installExtensionInjectable),
installExtensions: di.inject(installExtensionsInjectable),
extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable),
staticFilesDirectory: di.inject(staticFilesDirectoryInjectable),
readJsonFile: di.inject(readJsonFileInjectable),
pathExists: di.inject(pathExistsInjectable),
watch: di.inject(watchInjectable),
logger: di.inject(loggerInjectable),
}),
});
export default extensionDiscoveryInjectable;

View File

@ -15,10 +15,10 @@ import directoryForUserDataInjectable from "../../common/app-paths/directory-for
import mockFs from "mock-fs";
import { delay } from "../../renderer/utils";
import { observable, when } from "mobx";
import appVersionInjectable from "../../common/vars/app-version.injectable";
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import watchInjectable from "../../common/fs/watch/watch.injectable";
import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable";
console = new Console(process.stdout, process.stderr); // fix mockFS
@ -33,7 +33,7 @@ describe("ExtensionDiscovery", () => {
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(installExtensionInjectable, () => () => Promise.resolve());
di.override(appVersionInjectable, () => "5.0.0");
di.override(extensionApiVersionInjectable, () => "5.0.0");
readJsonFileMock = jest.fn();
di.override(readJsonFileInjectable, () => readJsonFileMock);

View File

@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import appSemanticVersionInjectable from "../../../common/vars/app-semantic-version.injectable";
import extensionApiVersionInjectable from "../../../common/vars/extension-api-version.injectable";
import { isCompatibleExtension } from "./is-compatible-extension";
const isCompatibleExtensionInjectable = getInjectable({
id: "is-compatible-extension",
instantiate: (di) => isCompatibleExtension({
appSemVer: di.inject(appSemanticVersionInjectable),
extensionApiVersion: di.inject(extensionApiVersionInjectable),
}),
});

View File

@ -2,16 +2,15 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import semver, { type SemVer } from "semver";
import semver from "semver";
import type { LensExtensionManifest } from "../../lens-extension";
interface Dependencies {
appSemVer: SemVer;
extensionApiVersion: string;
}
export const isCompatibleExtension = ({ appSemVer }: Dependencies): ((manifest: LensExtensionManifest) => boolean) => {
export const isCompatibleExtension = ({ extensionApiVersion }: Dependencies): ((manifest: LensExtensionManifest) => boolean) => {
return (manifest: LensExtensionManifest): boolean => {
const appVersion = appSemVer.raw.split("-")[0]; // drop prerelease version if any, e.g. "-alpha.0"
const manifestLensEngine = manifest.engines.lens;
const validVersion = manifestLensEngine.match(/^[\^0-9]\d*\.\d+\b/); // must start from ^ or number
@ -30,7 +29,7 @@ export const isCompatibleExtension = ({ appSemVer }: Dependencies): ((manifest:
}) as semver.SemVer;
const supportedVersionsByExtension = semver.validRange(`^${extMajor}.${extMinor}`) as string;
return semver.satisfies(appVersion, supportedVersionsByExtension, {
return semver.satisfies(extensionApiVersion, supportedVersionsByExtension, {
loose: true,
includePrerelease: false,
});

View File

@ -16,10 +16,10 @@ import processCheckingForUpdatesInjectable from "../../main/application-update/c
import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import quitAndInstallUpdateInjectable from "../../main/application-update/quit-and-install-update.injectable";
import appVersionInjectable from "../../common/vars/app-version.injectable";
import periodicalCheckForUpdatesInjectable from "../../main/application-update/periodical-check-for-updates/periodical-check-for-updates.injectable";
import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time";
import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable";
import buildVersionInjectable from "../../main/vars/build-version.injectable";
describe("analytics for installing update", () => {
let builder: ApplicationBuilder;
@ -36,7 +36,7 @@ describe("analytics for installing update", () => {
analyticsListenerMock = jest.fn();
builder.beforeApplicationStart(mainDi => {
mainDi.override(appVersionInjectable, () => "42.0.0");
mainDi.override(buildVersionInjectable, () => "42.0.0");
checkForPlatformUpdatesMock = asyncFn();

View File

@ -13,8 +13,8 @@ import checkForPlatformUpdatesInjectable from "../../main/application-update/che
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
import selectedUpdateChannelInjectable from "../../common/application-update/selected-update-channel/selected-update-channel.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import appVersionInjectable from "../../common/vars/app-version.injectable";
import { updateChannels } from "../../common/application-update/update-channels";
import buildVersionInjectable from "../../main/vars/build-version.injectable";
describe("downgrading version update", () => {
let applicationBuilder: ApplicationBuilder;
@ -102,7 +102,7 @@ describe("downgrading version update", () => {
},
].forEach(({ appVersion, updateChannel, downgradeIsAllowed }) => {
it(`given application version "${appVersion}" and update channel "${updateChannel.id}", when checking for updates, can${downgradeIsAllowed ? "": "not"} downgrade`, async () => {
mainDi.override(appVersionInjectable, () => appVersion);
mainDi.override(buildVersionInjectable, () => appVersion);
await applicationBuilder.render();

View File

@ -21,8 +21,8 @@ import type { IComputedValue } from "mobx";
import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable";
import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable";
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
import appVersionInjectable from "../../common/vars/app-version.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import buildVersionInjectable from "../../main/vars/build-version.injectable";
describe("selection of update stability", () => {
let builder: ApplicationBuilder;
@ -268,7 +268,7 @@ describe("selection of update stability", () => {
it('given no update channel selection is stored and currently using stable release, when user checks for updates, checks for updates from "latest" update channel by default', async () => {
builder.beforeApplicationStart((mainDi) => {
mainDi.override(appVersionInjectable, () => "1.0.0");
mainDi.override(buildVersionInjectable, () => "1.0.0");
});
await builder.render();
@ -285,7 +285,7 @@ describe("selection of update stability", () => {
it('given no update channel selection is stored and currently using alpha release, when checking for updates, checks for updates from "alpha" channel', async () => {
builder.beforeApplicationStart((mainDi) => {
mainDi.override(appVersionInjectable, () => "1.0.0-alpha");
mainDi.override(buildVersionInjectable, () => "1.0.0-alpha");
});
await builder.render();
@ -299,7 +299,7 @@ describe("selection of update stability", () => {
it('given no update channel selection is stored and currently using beta release, when checking for updates, checks for updates from "beta" channel', async () => {
builder.beforeApplicationStart((mainDi) => {
mainDi.override(appVersionInjectable, () => "1.0.0-beta");
mainDi.override(buildVersionInjectable, () => "1.0.0-beta");
});
await builder.render();
@ -313,7 +313,7 @@ describe("selection of update stability", () => {
it("given update channel selection is stored and currently using prerelease, when checking for updates, checks for updates from stored channel", async () => {
builder.beforeApplicationStart((mainDi) => {
mainDi.override(appVersionInjectable, () => "1.0.0-alpha");
mainDi.override(buildVersionInjectable, () => "1.0.0-alpha");
// TODO: Switch to more natural way of setting initial value
// TODO: UserStore is currently responsible for getting and setting initial value

View File

@ -7,7 +7,7 @@ import React from "react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import navigateToTelemetryPreferencesInjectable from "../../common/front-end-routing/routes/preferences/telemetry/navigate-to-telemetry-preferences.injectable";
import sentryDnsUrlInjectable from "../../renderer/components/+preferences/sentry-dns-url.injectable";
import sentryDataSourceNameInjectable from "../../common/vars/sentry-dsn-url.injectable";
import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake";
describe("preferences - navigation to telemetry preferences", () => {
@ -114,7 +114,7 @@ describe("preferences - navigation to telemetry preferences", () => {
beforeEach(async () => {
builder.beforeWindowStart((windowDi) => {
windowDi.override(sentryDnsUrlInjectable, () => "some-sentry-dns-url");
windowDi.override(sentryDataSourceNameInjectable, () => "some-sentry-dns-url");
});
rendered = await builder.render();
@ -144,7 +144,7 @@ describe("preferences - navigation to telemetry preferences", () => {
beforeEach(async () => {
builder.beforeWindowStart((windowDi) => {
windowDi.override(sentryDnsUrlInjectable, () => null);
windowDi.override(sentryDataSourceNameInjectable, () => null);
});
rendered = await builder.render();

View File

@ -6,7 +6,6 @@ import { getInjectable } from "@ogre-tools/injectable";
import type { AppPaths } from "../../common/app-paths/app-path-injection-token";
import getElectronAppPathInjectable from "./get-electron-app-path/get-electron-app-path.injectable";
import setElectronAppPathInjectable from "./set-electron-app-path/set-electron-app-path.injectable";
import appNameInjectable from "./app-name/app-name.injectable";
import directoryForIntegrationTestingInjectable from "./directory-for-integration-testing/directory-for-integration-testing.injectable";
import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable";
import { pathNames } from "../../common/app-paths/app-path-names";
@ -14,6 +13,7 @@ import { fromPairs, map } from "lodash/fp";
import { pipeline } from "@ogre-tools/fp";
import joinPathsInjectable from "../../common/path/join-paths.injectable";
import { beforeElectronIsReadyInjectionToken } from "../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
import appNameInjectable from "../../common/vars/app-name.injectable";
const setupAppPathsInjectable = getInjectable({
id: "setup-app-paths",

View File

@ -6,14 +6,14 @@ import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsLoadedInjectionToken } from "../start-main-application/runnable-tokens/after-application-is-loaded-injection-token";
import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable";
import { getCurrentDateTime } from "../../common/utils/date/get-current-date-time";
import appVersionInjectable from "../../common/vars/app-version.injectable";
import buildVersionInjectable from "../vars/build-version.injectable";
const emitCurrentVersionToAnalyticsInjectable = getInjectable({
id: "emit-current-version-to-analytics",
instantiate: (di) => {
const emitEvent = di.inject(emitEventInjectable);
const appVersion = di.inject(appVersionInjectable);
const buildVersion = di.inject(buildVersionInjectable);
return {
run: () => {
@ -22,7 +22,7 @@ const emitCurrentVersionToAnalyticsInjectable = getInjectable({
action: "current-version",
params: {
version: appVersion,
version: buildVersion,
currentDateTime: getCurrentDateTime(),
},
});

View File

@ -3,18 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import packageJsonInjectable from "../../common/vars/package-json.injectable";
import { has } from "lodash/fp";
import applicationInformationInjectable from "../../common/vars/application-information.injectable";
// TOOO: Rename to something less technical
const publishIsConfiguredInjectable = getInjectable({
id: "publish-is-configured",
instantiate: (di) => {
const packageJson = di.inject(packageJsonInjectable);
return has("build.publish", packageJson);
},
instantiate: (di) => Boolean(di.inject(applicationInformationInjectable).build.publish?.length),
});
export default publishIsConfiguredInjectable;

View File

@ -0,0 +1,21 @@
/**
* 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 { requestChannelListenerInjectionToken } from "../../common/utils/channel/request-channel-listener-injection-token";
import { buildVersionChannel } from "../../common/vars/build-semantic-version.injectable";
import buildVersionInjectable from "../vars/build-version.injectable";
const setupBuildVersionRequestChannelInjectable = getInjectable({
id: "setup-build-version-request-channel",
instantiate: (di) => {
return {
channel: buildVersionChannel,
handler: () => di.inject(buildVersionInjectable),
};
},
injectionToken: requestChannelListenerInjectionToken,
});
export default setupBuildVersionRequestChannelInjectable;

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import appNameInjectable from "../../app-paths/app-name/app-name.injectable";
import appNameInjectable from "../../../common/vars/app-name.injectable";
import { beforeElectronIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
import electronAppInjectable from "../electron-app.injectable";

View File

@ -7,7 +7,6 @@ import { kebabCase, noop, chunk } from "lodash/fp";
import type { DiContainer, Injectable } from "@ogre-tools/injectable";
import { createContainer } from "@ogre-tools/injectable";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import appNameInjectable from "./app-paths/app-name/app-name.injectable";
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable";
import readJsonFileInjectable from "../common/fs/read-json-file.injectable";
import readFileInjectable from "../common/fs/read-file.injectable";
@ -33,7 +32,6 @@ import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.inject
import environmentVariablesInjectable from "../common/utils/environment-variables.injectable";
import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable";
import setupLensProxyInjectable from "./start-main-application/runnables/setup-lens-proxy.injectable";
import setupSentryInjectable from "./start-main-application/runnables/setup-sentry.injectable";
import setupShellInjectable from "./start-main-application/runnables/setup-shell.injectable";
import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable";
import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable";
@ -63,7 +61,6 @@ import broadcastMessageInjectable from "../common/ipc/broadcast-message.injectab
import getElectronThemeInjectable from "./electron-app/features/get-electron-theme.injectable";
import syncThemeFromOperatingSystemInjectable from "./electron-app/features/sync-theme-from-operating-system.injectable";
import platformInjectable from "../common/vars/platform.injectable";
import productNameInjectable from "./app-paths/app-name/product-name.injectable";
import electronQuitAndInstallUpdateInjectable from "./electron-app/features/electron-quit-and-install-update.injectable";
import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable";
import publishIsConfiguredInjectable from "./application-update/publish-is-configured.injectable";
@ -73,7 +70,6 @@ import setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-qui
import downloadPlatformUpdateInjectable from "./application-update/download-platform-update/download-platform-update.injectable";
import startCatalogSyncInjectable from "./catalog-sync-to-renderer/start-catalog-sync.injectable";
import startKubeConfigSyncInjectable from "./start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable";
import appVersionInjectable from "../common/vars/app-version.injectable";
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
import periodicalCheckForUpdatesInjectable from "./application-update/periodical-check-for-updates/periodical-check-for-updates.injectable";
import execFileInjectable from "../common/fs/exec-file.injectable";
@ -148,9 +144,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
di.override(environmentVariablesInjectable, () => ({}));
di.override(commandLineArgumentsInjectable, () => []);
di.override(productNameInjectable, () => "some-product-name");
di.override(appVersionInjectable, () => "1.0.0");
di.override(clusterFramesInjectable, () => observable.map<string, ClusterFrameInfo>());
di.override(stopServicesAndExitAppInjectable, () => () => {});
@ -179,7 +172,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
// TODO: Remove usages of globally exported appEventBus to get rid of this
di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>());
di.override(appNameInjectable, () => "some-app-name");
di.override(broadcastMessageInjectable, () => (channel) => {
throw new Error(`Tried to broadcast message to channel "${channel}" over IPC without explicit override.`);
});
@ -210,7 +202,6 @@ const overrideRunnablesHavingSideEffects = (di: DiContainer) => {
initializeExtensionsInjectable,
setupIpcMainHandlersInjectable,
setupLensProxyInjectable,
setupSentryInjectable,
setupShellInjectable,
setupSyncingOfWeblinksInjectable,
setupSystemCaInjectable,

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { getBundledKubectlVersion } from "../../common/utils";
import bundledKubectlVersionInjectable from "../../common/vars/bundled-kubectl-version.injectable";
import createKubectlInjectable from "./create-kubectl.injectable";
const bundledKubectlInjectable = getInjectable({
@ -11,8 +11,7 @@ const bundledKubectlInjectable = getInjectable({
instantiate: (di) => {
const createKubectl = di.inject(createKubectlInjectable);
const bundledKubectlVersion = getBundledKubectlVersion();
const bundledKubectlVersion = di.inject(bundledKubectlVersionInjectable);
return createKubectl(bundledKubectlVersion);
},

View File

@ -12,6 +12,8 @@ import normalizedPlatformInjectable from "../../common/vars/normalized-platform.
import kubectlBinaryNameInjectable from "./binary-name.injectable";
import bundledKubectlBinaryPathInjectable from "./bundled-binary-path.injectable";
import baseBundledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
import bundledKubectlVersionInjectable from "../../common/vars/bundled-kubectl-version.injectable";
import kubectlVersionMapInjectable from "./version-map.injectable";
const createKubectlInjectable = getInjectable({
id: "create-kubectl",
@ -25,6 +27,8 @@ const createKubectlInjectable = getInjectable({
kubectlBinaryName: di.inject(kubectlBinaryNameInjectable),
bundledKubectlBinaryPath: di.inject(bundledKubectlBinaryPathInjectable),
baseBundeledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable),
bundledKubectlVersion: di.inject(bundledKubectlVersionInjectable),
kubectlVersionMap: di.inject(kubectlVersionMapInjectable),
};
return (clusterVersion: string) => new Kubectl(dependencies, clusterVersion);

View File

@ -9,7 +9,6 @@ import { promiseExecFile } from "../../common/utils/promise-exec";
import logger from "../logger";
import { ensureDir, pathExists } from "fs-extra";
import * as lockFile from "proper-lockfile";
import { getBundledKubectlVersion } from "../../common/utils/app-version";
import { SemVer } from "semver";
import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers";
import got from "got/dist/source";
@ -17,26 +16,6 @@ import { promisify } from "util";
import stream from "stream";
import { noop } from "lodash/fp";
const bundledVersion = getBundledKubectlVersion();
const kubectlMap: Map<string, string> = new Map([
["1.7", "1.8.15"],
["1.8", "1.9.10"],
["1.9", "1.10.13"],
["1.10", "1.11.10"],
["1.11", "1.12.10"],
["1.12", "1.13.12"],
["1.13", "1.13.12"],
["1.14", "1.14.10"],
["1.15", "1.15.11"],
["1.16", "1.16.15"],
["1.17", "1.17.17"],
["1.18", "1.18.20"],
["1.19", "1.19.12"],
["1.20", "1.20.8"],
["1.21", "1.21.9"],
["1.22", "1.22.6"],
["1.23", bundledVersion],
]);
const initScriptVersionString = "# lens-initscript v3";
export interface KubectlDependencies {
@ -52,6 +31,8 @@ export interface KubectlDependencies {
readonly downloadKubectlBinaries: boolean;
readonly downloadMirror: string;
};
readonly bundledKubectlVersion: string;
readonly kubectlVersionMap: Map<string, string>;
}
export class Kubectl {
@ -60,7 +41,6 @@ export class Kubectl {
protected readonly path: string;
protected readonly dirname: string;
public static readonly bundledKubectlVersion = bundledVersion;
public static invalidBundle = false;
constructor(protected readonly dependencies: KubectlDependencies, clusterVersion: string) {
@ -69,10 +49,10 @@ export class Kubectl {
try {
version = new SemVer(clusterVersion);
} catch {
version = new SemVer(Kubectl.bundledKubectlVersion);
version = new SemVer(this.dependencies.bundledKubectlVersion);
}
const fromMajorMinor = kubectlMap.get(`${version.major}.${version.minor}`);
const fromMajorMinor = this.dependencies.kubectlVersionMap.get(`${version.major}.${version.minor}`);
/**
* minorVersion is the first two digits of kube server version if the version map includes that,
@ -189,7 +169,7 @@ export class Kubectl {
}
protected async checkBundled(): Promise<boolean> {
if (this.kubectlVersion === Kubectl.bundledKubectlVersion) {
if (this.kubectlVersion === this.dependencies.bundledKubectlVersion) {
try {
const exist = await pathExists(this.path);

View File

@ -0,0 +1,35 @@
/**
* 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 bundledKubectlVersionInjectable from "../../common/vars/bundled-kubectl-version.injectable";
const kubectlVersionMapInjectable = getInjectable({
id: "kubectl-version-map",
instantiate: (di) => {
const bundledKubectlVersion = di.inject(bundledKubectlVersionInjectable);
return new Map([
["1.7", "1.8.15"],
["1.8", "1.9.10"],
["1.9", "1.10.13"],
["1.10", "1.11.10"],
["1.11", "1.12.10"],
["1.12", "1.13.12"],
["1.13", "1.13.12"],
["1.14", "1.14.10"],
["1.15", "1.15.11"],
["1.16", "1.16.15"],
["1.17", "1.17.17"],
["1.18", "1.18.20"],
["1.19", "1.19.12"],
["1.20", "1.20.8"],
["1.21", "1.21.9"],
["1.22", "1.22.6"],
["1.23", bundledKubectlVersion],
]);
},
});
export default kubectlVersionMapInjectable;

View File

@ -10,26 +10,20 @@ import httpProxy from "http-proxy";
import clusterManagerInjectable from "../cluster-manager.injectable";
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
import lensProxyPortInjectable from "./lens-proxy-port.injectable";
import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable";
const lensProxyInjectable = getInjectable({
id: "lens-proxy",
instantiate: (di) => {
const clusterManager = di.inject(clusterManagerInjectable);
const router = di.inject(routerInjectable);
const shellApiRequest = di.inject(shellApiRequestInjectable);
const proxy = httpProxy.createProxy();
const lensProxyPort = di.inject(lensProxyPortInjectable);
return new LensProxy({
router,
proxy,
kubeApiUpgradeRequest,
shellApiRequest,
getClusterForRequest: clusterManager.getClusterForRequest,
lensProxyPort,
});
},
instantiate: (di) => new LensProxy({
router: di.inject(routerInjectable),
proxy: httpProxy.createProxy(),
kubeApiUpgradeRequest,
shellApiRequest: di.inject(shellApiRequestInjectable),
getClusterForRequest: di.inject(clusterManagerInjectable).getClusterForRequest,
lensProxyPort: di.inject(lensProxyPortInjectable),
contentSecurityPolicy: di.inject(contentSecurityPolicyInjectable),
}),
});
export default lensProxyInjectable;

View File

@ -7,7 +7,7 @@ import net from "net";
import type http from "http";
import spdy from "spdy";
import type httpProxy from "http-proxy";
import { apiPrefix, apiKubePrefix, contentSecurityPolicy } from "../../common/vars";
import { apiPrefix, apiKubePrefix } from "../../common/vars";
import type { Router } from "../router/router";
import type { ClusterContextHandler } from "../context-handler/context-handler";
import logger from "../logger";
@ -26,9 +26,10 @@ interface Dependencies {
getClusterForRequest: GetClusterForRequest;
shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
router: Router;
proxy: httpProxy;
lensProxyPort: { set: (portNumber: number) => void };
readonly router: Router;
readonly proxy: httpProxy;
readonly lensProxyPort: { set: (portNumber: number) => void };
readonly contentSecurityPolicy: string;
}
const watchParam = "watch";
@ -63,7 +64,7 @@ export class LensProxy {
protected closed = false;
protected retryCounters = new Map<string, number>();
constructor(private dependencies: Dependencies) {
constructor(private readonly dependencies: Dependencies) {
this.configureProxy(dependencies.proxy);
this.proxyServer = spdy.createServer({
@ -239,10 +240,7 @@ export class LensProxy {
}
}
if (contentSecurityPolicy) {
res.setHeader("Content-Security-Policy", contentSecurityPolicy);
}
res.setHeader("Content-Security-Policy", this.dependencies.contentSecurityPolicy);
this.dependencies.router.route(cluster, req, res);
}
}

View File

@ -3,12 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { docsUrl, productName, supportUrl } from "../../common/vars";
import { docsUrl, supportUrl } from "../../common/vars";
import { broadcastMessage } from "../../common/ipc";
import type { MenuItemConstructorOptions } from "electron";
import { webContents } from "electron";
import loggerInjectable from "../../common/logger.injectable";
import appNameInjectable from "../app-paths/app-name/app-name.injectable";
import electronMenuItemsInjectable from "./electron-menu-items.injectable";
import updatingIsEnabledInjectable from "../application-update/updating-is-enabled.injectable";
import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
@ -24,6 +23,8 @@ import reloadCurrentApplicationWindowInjectable from "../start-main-application/
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
import processCheckingForUpdatesInjectable from "../application-update/check-for-updates/process-checking-for-updates.injectable";
import openLinkInBrowserInjectable from "../../common/utils/open-link-in-browser.injectable";
import appNameInjectable from "../../common/vars/app-name.injectable";
import productNameInjectable from "../../common/vars/product-name.injectable";
function ignoreIf(check: boolean, menuItems: MenuItemOpts[]) {
return check ? [] : menuItems;
@ -39,6 +40,7 @@ const applicationMenuItemsInjectable = getInjectable({
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
const appName = di.inject(appNameInjectable);
const productName = di.inject(productNameInjectable);
const isMac = di.inject(isMacInjectable);
const updatingIsEnabled = di.inject(updatingIsEnabledInjectable);
const electronMenuItems = di.inject(electronMenuItemsInjectable);

View File

@ -3,36 +3,10 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { Menu } from "electron";
import { appName, isWindows, productName } from "../../common/vars";
import packageJson from "../../../package.json";
import type { MenuItemOpts } from "./application-menu-items.injectable";
import type { ShowMessagePopup } from "../electron-app/features/show-message-popup.injectable";
export type MenuTopId = "mac" | "file" | "edit" | "view" | "help";
interface Dependencies {
appVersion: string;
extensionApiVersion: string;
showMessagePopup: ShowMessagePopup;
}
export const showAbout = ({ showMessagePopup, extensionApiVersion, appVersion }: Dependencies) => async () => {
const appInfo = [
`${appName}: ${appVersion}`,
`Extension API: ${extensionApiVersion}`,
`Electron: ${process.versions.electron}`,
`Chrome: ${process.versions.chrome}`,
`Node: ${process.versions.node}`,
packageJson.copyright,
];
await showMessagePopup(
`${isWindows ? " ".repeat(2) : ""}${appName}`,
productName,
appInfo.join("\r\n"),
);
};
export function buildMenu(applicationMenuItems: MenuItemOpts[]) {
Menu.setApplicationMenu(
Menu.buildFromTemplate(applicationMenuItems),

View File

@ -3,20 +3,43 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { showAbout } from "./menu";
import showMessagePopupInjectable from "../electron-app/features/show-message-popup.injectable";
import appVersionInjectable from "../../common/vars/app-version.injectable";
import buildVersionInjectable from "./build-version.injectable";
import isWindowsInjectable from "../../common/vars/is-windows.injectable";
import appNameInjectable from "../../common/vars/app-name.injectable";
import productNameInjectable from "../../common/vars/product-name.injectable";
import buildVersionInjectable from "../vars/build-version.injectable";
import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable";
import applicationCopyrightInjectable from "../../common/vars/application-copyright.injectable";
const showAboutInjectable = getInjectable({
id: "show-about",
instantiate: (di) =>
showAbout({
appVersion: di.inject(buildVersionInjectable),
extensionApiVersion: di.inject(appVersionInjectable),
showMessagePopup: di.inject(showMessagePopupInjectable),
}),
instantiate: (di) => {
const appVersion = di.inject(buildVersionInjectable);
const extensionApiVersion = di.inject(extensionApiVersionInjectable);
const showMessagePopup = di.inject(showMessagePopupInjectable);
const isWindows = di.inject(isWindowsInjectable);
const appName = di.inject(appNameInjectable);
const productName = di.inject(productNameInjectable);
const applicationCopyright = di.inject(applicationCopyrightInjectable);
return () => {
const appInfo = [
`${appName}: ${appVersion}`,
`Extension API: ${extensionApiVersion}`,
`Electron: ${process.versions.electron}`,
`Chrome: ${process.versions.chrome}`,
`Node: ${process.versions.node}`,
applicationCopyright,
];
showMessagePopup(
`${isWindows ? " ".repeat(2) : ""}${appName}`,
productName,
appInfo.join("\r\n"),
);
};
},
});
export default showAboutInjectable;

View File

@ -6,7 +6,7 @@ import type { SupportedFileExtension } from "../router/router-content-types";
import { contentTypes } from "../router/router-content-types";
import logger from "../logger";
import { getRouteInjectable } from "../router/router.injectable";
import { appName, publicPath } from "../../common/vars";
import { publicPath } from "../../common/vars";
import path from "path";
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
import httpProxy from "http-proxy";
@ -19,66 +19,78 @@ import { webpackDevServerPort } from "../../../webpack/vars";
import type { LensApiRequest, RouteResponse } from "../router/route";
import { route } from "../router/route";
import staticFilesDirectoryInjectable from "../../common/vars/static-files-directory.injectable";
import appNameInjectable from "../../common/vars/app-name.injectable";
interface ProductionDependencies {
readFileBuffer: (path: string) => Promise<Buffer>;
getAbsolutePath: GetAbsolutePath;
joinPaths: JoinPaths;
staticFilesDirectory: string;
appName: string;
}
const handleStaticFileInProduction =
({ readFileBuffer, getAbsolutePath, joinPaths, staticFilesDirectory }: ProductionDependencies) =>
async ({ params }: LensApiRequest<"/{path*}">): Promise<RouteResponse<Buffer>> => {
let filePath = params.path;
const handleStaticFileInProduction = ({
readFileBuffer,
getAbsolutePath,
joinPaths,
staticFilesDirectory,
appName,
}: ProductionDependencies) => (
async ({ params }: LensApiRequest<"/{path*}">): Promise<RouteResponse<Buffer>> => {
let filePath = params.path;
for (let retryCount = 0; retryCount < 5; retryCount += 1) {
const asset = joinPaths(staticFilesDirectory, filePath);
const normalizedFilePath = getAbsolutePath(asset);
for (let retryCount = 0; retryCount < 5; retryCount += 1) {
const asset = joinPaths(staticFilesDirectory, filePath);
const normalizedFilePath = getAbsolutePath(asset);
if (!normalizedFilePath.startsWith(staticFilesDirectory)) {
return { statusCode: 404 };
}
try {
const fileExtension = path
.extname(asset)
.slice(1) as SupportedFileExtension;
const contentType = contentTypes[fileExtension] || contentTypes.txt;
return { response: await readFileBuffer(asset), contentType };
} catch (err) {
if (retryCount > 5) {
logger.error("handleStaticFile:", String(err));
if (!normalizedFilePath.startsWith(staticFilesDirectory)) {
return { statusCode: 404 };
}
try {
const fileExtension = path
.extname(asset)
.slice(1) as SupportedFileExtension;
const contentType = contentTypes[fileExtension] || contentTypes.txt;
return { response: await readFileBuffer(asset), contentType };
} catch (err) {
if (retryCount > 5) {
logger.error("handleStaticFile:", String(err));
return { statusCode: 404 };
}
filePath = `${publicPath}/${appName}.html`;
}
filePath = `${publicPath}/${appName}.html`;
}
}
return { statusCode: 404 };
};
return { statusCode: 404 };
}
);
interface DevelopmentDependencies {
proxy: httpProxy;
appName: string;
}
const handleStaticFileInDevelopment =
({ proxy }: DevelopmentDependencies) =>
({ raw: { req, res }}: LensApiRequest<"/{path*}">): RouteResponse<Buffer> => {
if (req.url === "/" || !req.url?.startsWith("/build/")) {
req.url = `${publicPath}/${appName}.html`;
}
const handleStaticFileInDevelopment = ({
proxy,
appName,
}: DevelopmentDependencies) => (
({ raw: { req, res }}: LensApiRequest<"/{path*}">): RouteResponse<Buffer> => {
if (req.url === "/" || !req.url?.startsWith("/build/")) {
req.url = `${publicPath}/${appName}.html`;
}
proxy.web(req, res, {
target: `http://127.0.0.1:${webpackDevServerPort}`,
});
proxy.web(req, res, {
target: `http://127.0.0.1:${webpackDevServerPort}`,
});
return { proxy };
};
return { proxy };
}
);
const staticFileRouteInjectable = getRouteInjectable({
id: "static-file-route",
@ -89,6 +101,7 @@ const staticFileRouteInjectable = getRouteInjectable({
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
const joinPaths = di.inject(joinPathsInjectable);
const staticFilesDirectory = di.inject(staticFilesDirectoryInjectable);
const appName = di.inject(appNameInjectable);
return route({
method: "get",
@ -97,12 +110,14 @@ const staticFileRouteInjectable = getRouteInjectable({
isDevelopment
? handleStaticFileInDevelopment({
proxy: httpProxy.createProxy(),
appName,
})
: handleStaticFileInProduction({
readFileBuffer,
getAbsolutePath,
joinPaths,
staticFilesDirectory,
appName,
}),
);
},

View File

@ -3,18 +3,18 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getRouteInjectable } from "../../router/router.injectable";
import { getAppVersion } from "../../../common/utils";
import { route } from "../../router/route";
import buildVersionInjectable from "../../vars/build-version.injectable";
const getVersionRouteInjectable = getRouteInjectable({
id: "get-version-route",
instantiate: () => route({
instantiate: (di) => route({
method: "get",
path: `/version`,
})(() => ({
response: {
version: getAppVersion(),
version: di.inject(buildVersionInjectable),
},
})),
});

View File

@ -1,40 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { shellEnv } from "./utils/shell-env";
import os from "os";
import { app } from "electron";
import logger from "./logger";
import { isSnap } from "../common/vars";
import { unionPATHs } from "../common/utils/union-env-path";
/**
* shellSync loads what would have been the environment if this application was
* run from the command line, into the process.env object. This is especially
* useful on macos where this always needs to be done.
*/
export async function shellSync() {
const env = await shellEnv(os.userInfo().shell);
if (!env.LANG) {
// the LANG env var expects an underscore instead of electron's dash
env.LANG = `${app.getLocale().replace("-", "_")}.UTF-8`;
} else if (!env.LANG.endsWith(".UTF-8")) {
env.LANG += ".UTF-8";
}
if (!isSnap) {
// Prefer the synced PATH over the initial one
process.env.PATH = unionPATHs(env.PATH ?? "", process.env.PATH ?? "");
}
// The spread operator allows joining of objects. The precedence is last to first.
process.env = {
...env,
...process.env,
};
logger.debug(`[SHELL-SYNC]: Synced shell env, and updating`, env, process.env);
}

View File

@ -6,11 +6,11 @@ import { getInjectable } from "@ogre-tools/injectable";
import createLensWindowInjectable from "./create-lens-window.injectable";
import lensProxyPortInjectable from "../../../lens-proxy/lens-proxy-port.injectable";
import isMacInjectable from "../../../../common/vars/is-mac.injectable";
import appNameInjectable from "../../../app-paths/app-name/app-name.injectable";
import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable";
import waitUntilBundledExtensionsAreLoadedInjectable from "./wait-until-bundled-extensions-are-loaded.injectable";
import { applicationWindowInjectionToken } from "./application-window-injection-token";
import { runInAction } from "mobx";
import appNameInjectable from "../../../../common/vars/app-name.injectable";
const createApplicationWindowInjectable = getInjectable({
id: "create-application-window",

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { getAppVersion, getAppVersionFromProxyServer } from "../../../common/utils";
import { getAppVersionFromProxyServer } from "../../../common/utils";
import exitAppInjectable from "../../electron-app/features/exit-app.injectable";
import lensProxyInjectable from "../../lens-proxy/lens-proxy.injectable";
import loggerInjectable from "../../../common/logger.injectable";
@ -11,6 +11,7 @@ import lensProxyPortInjectable from "../../lens-proxy/lens-proxy-port.injectable
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
import showErrorPopupInjectable from "../../electron-app/features/show-error-popup.injectable";
import { beforeApplicationIsLoadingInjectionToken } from "../runnable-tokens/before-application-is-loading-injection-token";
import buildVersionInjectable from "../../vars/build-version.injectable";
const setupLensProxyInjectable = getInjectable({
id: "setup-lens-proxy",
@ -22,6 +23,7 @@ const setupLensProxyInjectable = getInjectable({
const lensProxyPort = di.inject(lensProxyPortInjectable);
const isWindows = di.inject(isWindowsInjectable);
const showErrorPopup = di.inject(showErrorPopupInjectable);
const buildVersion = di.inject(buildVersionInjectable);
return {
run: async () => {
@ -41,7 +43,7 @@ const setupLensProxyInjectable = getInjectable({
lensProxyPort.get(),
);
if (getAppVersion() !== versionFromProxy) {
if (buildVersion !== versionFromProxy) {
logger.error("Proxy server responded with invalid response");
return exitApp();

View File

@ -3,21 +3,19 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { initializeSentryReporting } from "../../../common/sentry";
import { init } from "@sentry/electron/main";
import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token";
import initializeSentryReportingWithInjectable from "../../../common/error-reporting/initialize-sentry-reporting.injectable";
const setupSentryInjectable = getInjectable({
id: "setup-sentry",
instantiate: (di) => {
const initializeSentryReportingWith = di.inject(initializeSentryReportingWithInjectable);
instantiate: () => ({
run: () => {
initializeSentryReporting(init);
},
}),
causesSideEffects: true,
return {
run: () => initializeSentryReportingWith(init),
};
},
injectionToken: beforeElectronIsReadyInjectionToken,
});

View File

@ -3,21 +3,47 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { shellSync } from "../../shell-sync";
import loggerInjectable from "../../../common/logger.injectable";
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token";
import { shellEnv } from "../../utils/shell-env";
import os from "os";
import { unionPATHs } from "../../../common/utils/union-env-path";
import isSnapInjectable from "../../../common/vars/is-snap.injectable";
import electronAppInjectable from "../../electron-app/electron-app.injectable";
const setupShellInjectable = getInjectable({
id: "setup-shell",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
const isSnap = di.inject(isSnapInjectable);
const electronApp = di.inject(electronAppInjectable);
return {
run: async () => {
logger.info("🐚 Syncing shell environment");
await shellSync();
const env = await shellEnv(os.userInfo().shell);
if (!env.LANG) {
// the LANG env var expects an underscore instead of electron's dash
env.LANG = `${electronApp.getLocale().replace("-", "_")}.UTF-8`;
} else if (!env.LANG.endsWith(".UTF-8")) {
env.LANG += ".UTF-8";
}
if (!isSnap) {
// Prefer the synced PATH over the initial one
process.env.PATH = unionPATHs(env.PATH ?? "", process.env.PATH ?? "");
}
// The spread operator allows joining of objects. The precedence is last to first.
process.env = {
...env,
...process.env,
};
logger.debug(`[SHELL-SYNC]: Synced shell env, and updating`, env, process.env);
},
};
},

View File

@ -4,12 +4,12 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { Menu, Tray } from "electron";
import packageJsonInjectable from "../../../common/vars/package-json.injectable";
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import { convertToElectronMenuTemplate } from "../reactive-tray-menu-items/converters";
import trayIconInjectable from "../menu-icon/tray-icon.injectable";
import applicationDescriptionInjectable from "../../../common/vars/application-description.injectable";
const TRAY_LOG_PREFIX = "[TRAY]";
@ -34,7 +34,7 @@ const electronTrayInjectable = getInjectable({
id: "electron-tray",
instantiate: (di): ElectronTray => {
const packageJson = di.inject(packageJsonInjectable);
const applicationDescription = di.inject(applicationDescriptionInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const isWindows = di.inject(isWindowsInjectable);
const logger = di.inject(loggerInjectable);
@ -46,7 +46,7 @@ const electronTrayInjectable = getInjectable({
start: () => {
tray = new Tray(trayIcon.get().iconPath);
tray.setToolTip(packageJson.description);
tray.setToolTip(applicationDescription);
tray.setIgnoreDoubleClickEvents(true);
if (isWindows) {

View File

@ -3,7 +3,6 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import productNameInjectable from "../../../app-paths/app-name/product-name.injectable";
import showApplicationWindowInjectable from "../../../start-main-application/lens-window/show-application-window.injectable";
import showAboutInjectable from "../../../menu/show-about.injectable";
import { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
@ -11,6 +10,7 @@ import { computed } from "mobx";
import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable";
import { withErrorSuppression } from "../../../../common/utils/with-error-suppression/with-error-suppression";
import { pipeline } from "@ogre-tools/fp";
import productNameInjectable from "../../../../common/vars/product-name.injectable";
const aboutAppTrayItemInjectable = getInjectable({
id: "about-app-tray-item",
@ -32,15 +32,10 @@ const aboutAppTrayItemInjectable = getInjectable({
click: pipeline(
async () => {
await showApplicationWindow();
await showAbout();
showAbout();
},
withErrorLoggingFor(() => "[TRAY]: Opening of show about failed."),
// TODO: Find out how to improve typing so that instead of
// x => withErrorSuppression(x) there could only be withErrorSuppression
(x) => withErrorSuppression(x),
withErrorSuppression,
),
};
},

View File

@ -4,12 +4,12 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
import productNameInjectable from "../../../app-paths/app-name/product-name.injectable";
import showApplicationWindowInjectable from "../../../start-main-application/lens-window/show-application-window.injectable";
import { computed } from "mobx";
import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable";
import { withErrorSuppression } from "../../../../common/utils/with-error-suppression/with-error-suppression";
import { pipeline } from "@ogre-tools/fp";
import productNameInjectable from "../../../../common/vars/product-name.injectable";
const openAppTrayItemInjectable = getInjectable({
id: "open-app-tray-item",
@ -28,15 +28,9 @@ const openAppTrayItemInjectable = getInjectable({
visible: computed(() => true),
click: pipeline(
async () => {
await showApplicationWindow();
},
showApplicationWindow,
withErrorLoggingFor(() => "[TRAY]: Opening of application window failed."),
// TODO: Find out how to improve typing so that instead of
// x => withErrorSuppression(x) there could only be withErrorSuppression
(x) => withErrorSuppression(x),
withErrorSuppression,
),
};
},

View File

@ -3,11 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable";
import electronAppInjectable from "../electron-app/electron-app.injectable";
const buildVersionInjectable = getInjectable({
id: "build-version",
instantiate: (di) => di.inject(electronAppInjectable).getVersion(),
injectionToken: buildVersionInjectionToken,
});
export default buildVersionInjectable;

View File

@ -6,13 +6,14 @@
// Fix embedded kubeconfig paths under snap config
import type { ClusterModel } from "../../common/cluster-types";
import { getAppVersion } from "../../common/utils/app-version";
import fs from "fs";
import type { MigrationDeclaration } from "../helpers";
import { migrationLog } from "../helpers";
import packageJson from "../../../package.json";
export default {
version: getAppVersion(), // Run always after upgrade
// TODO: replace with injection once migrations are made as injectables
version: packageJson.version, // Run always after upgrade
run(store) {
if (!process.env["SNAP"]) return;

View File

@ -3,13 +3,14 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getAppVersion } from "../../common/utils";
import { lensSlackWeblinkId, slackUrl } from "../../common/vars";
import type { WeblinkData } from "../../common/weblink-store";
import type { MigrationDeclaration } from "../helpers";
import packageJson from "../../../package.json";
export default {
version: getAppVersion(), // Run always after upgrade
// TODO: replace with injection once migrations are made as injectables
version: packageJson.version, // Run always after upgrade
run(store) {
const weblinksRaw: any = store.get("weblinks");
const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[];

View File

@ -20,7 +20,6 @@ import configurePackages from "../common/configure-packages";
import * as initializers from "./initializers";
import logger from "../common/logger";
import { WeblinkStore } from "../common/weblink-store";
import { initializeSentryReporting } from "../common/sentry";
import { registerCustomThemes } from "./components/monaco-editor";
import { getDi } from "./getDi";
import { DiContextProvider } from "@ogre-tools/injectable-react";
@ -46,6 +45,7 @@ import kubernetesClusterCategoryInjectable from "../common/catalog/categories/ku
import autoRegistrationInjectable from "../common/k8s-api/api-manager/auto-registration.injectable";
import assert from "assert";
import startFrameInjectable from "./start-frame/start-frame.injectable";
import initializeSentryReportingWithInjectable from "../common/error-reporting/initialize-sentry-reporting.injectable";
configurePackages(); // global packages
registerCustomThemes(); // monaco editor themes
@ -62,8 +62,10 @@ async function attachChromeDebugger() {
}
export async function bootstrap(di: DiContainer) {
const initializeSentryReportingWith = di.inject(initializeSentryReportingWithInjectable);
if (process.isMainFrame) {
initializeSentryReporting(init);
initializeSentryReportingWith(init);
}
const startFrame = di.inject(startFrameInjectable);

View File

@ -8,14 +8,14 @@ import routeIsActiveInjectable from "../../../routes/route-is-active.injectable"
import { computed } from "mobx";
import telemetryPreferenceItemsInjectable from "../telemetry-preference-items.injectable";
import telemetryPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/telemetry/telemetry-preferences-route.injectable";
import sentryDnsUrlInjectable from "../sentry-dns-url.injectable";
import sentryDataSourceNameInjectable from "../../../../common/vars/sentry-dsn-url.injectable";
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
const terminalPreferencesNavigationItemInjectable = getInjectable({
id: "telemetry-preferences-navigation-item",
instantiate: (di) => {
const sentryDnsUrl = di.inject(sentryDnsUrlInjectable);
const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable);
const telemetryPreferenceItems = di.inject(
telemetryPreferenceItemsInjectable,
@ -37,7 +37,7 @@ const terminalPreferencesNavigationItemInjectable = getInjectable({
isActive: routeIsActive,
isVisible: computed(
() => !!sentryDnsUrl || telemetryPreferenceItems.get().length > 0,
() => !!sentryDataSourceName || telemetryPreferenceItems.get().length > 0,
),
orderNumber: 60,

View File

@ -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 { sentryDsn } from "../../../common/vars";
const sentryDnsUrlInjectable = getInjectable({
id: "sentry-dns-url",
instantiate: () => sentryDsn,
});
export default sentryDnsUrlInjectable;

View File

@ -12,7 +12,7 @@ import type { IComputedValue } from "mobx";
import { withInjectables } from "@ogre-tools/injectable-react";
import { Preferences } from "./preferences";
import telemetryPreferenceItemsInjectable from "./telemetry-preference-items.injectable";
import sentryDnsUrlInjectable from "./sentry-dns-url.injectable";
import sentryDataSourceNameInjectable from "../../../common/vars/sentry-dsn-url.injectable";
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
@ -74,7 +74,7 @@ export const Telemetry = withInjectables<Dependencies>(
{
getProps: (di) => ({
telemetryPreferenceItems: di.inject(telemetryPreferenceItemsInjectable),
sentryDnsUrl: di.inject(sentryDnsUrlInjectable),
sentryDnsUrl: di.inject(sentryDataSourceNameInjectable),
userStore: di.inject(userStoreInjectable),
}),
},

View File

@ -10,12 +10,13 @@ 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 { productName, slackUrl } from "../../../common/vars";
import { slackUrl } 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";
import welcomeBannerItemsInjectable from "./welcome-banner-items/welcome-banner-items.injectable";
import type { WelcomeBannerRegistration } from "./welcome-banner-items/welcome-banner-registration";
import productNameInjectable from "../../../common/vars/product-name.injectable";
export const defaultWidth = 320;
@ -25,9 +26,14 @@ const Carousel = LegacyCarousel as React.ComponentType<CarouselProps>;
interface Dependencies {
welcomeMenuItems: IComputedValue<WelcomeMenuRegistration[]>;
welcomeBannerItems: IComputedValue<WelcomeBannerRegistration[]>;
productName: string;
}
const NonInjectedWelcome = observer(({ welcomeMenuItems, welcomeBannerItems }: Dependencies) => {
const NonInjectedWelcome = observer(({
welcomeMenuItems,
welcomeBannerItems,
productName,
}: Dependencies) => {
const welcomeBanners = welcomeBannerItems.get();
// if there is banner with specified width, use it to calculate the width of the container
@ -129,5 +135,6 @@ export const Welcome = withInjectables<Dependencies>(NonInjectedWelcome, {
getProps: (di) => ({
welcomeMenuItems: di.inject(welcomeMenuItemsInjectable),
welcomeBannerItems: di.inject(welcomeBannerItemsInjectable),
productName: di.inject(productNameInjectable),
}),
});

View File

@ -11,10 +11,11 @@ import type { SelectOption } from "../../select";
import { Select } from "../../select";
import { Input } from "../../input";
import { observable, computed, autorun, makeObservable } from "mobx";
import { productName } from "../../../../common/vars";
import type { MetricProviderInfo } from "../../../../common/k8s-api/endpoints/metrics.api";
import { metricsApi } from "../../../../common/k8s-api/endpoints/metrics.api";
import { Spinner } from "../../spinner";
import { withInjectables } from "@ogre-tools/injectable-react";
import productNameInjectable from "../../../../common/vars/product-name.injectable";
export interface ClusterPrometheusSettingProps {
cluster: Cluster;
@ -24,8 +25,12 @@ const autoDetectPrometheus = Symbol("auto-detect-prometheus");
type ProviderValue = typeof autoDetectPrometheus | string;
interface Dependencies {
productName: string;
}
@observer
export class ClusterPrometheusSetting extends React.Component<ClusterPrometheusSettingProps> {
class NonInjectedClusterPrometheusSetting extends React.Component<ClusterPrometheusSettingProps & Dependencies> {
@observable path = "";
@observable selectedOption: ProviderValue = autoDetectPrometheus;
@observable loading = true;
@ -46,7 +51,7 @@ export class ClusterPrometheusSetting extends React.Component<ClusterPrometheusS
];
}
constructor(props: ClusterPrometheusSettingProps) {
constructor(props: ClusterPrometheusSettingProps & Dependencies) {
super(props);
makeObservable(this);
}
@ -155,7 +160,7 @@ export class ClusterPrometheusSetting extends React.Component<ClusterPrometheusS
placeholder="<namespace>/<service>:<port>"
/>
<small className="hint">
{`An address to an existing Prometheus installation (<namespace>/<service>:<port>). ${productName} tries to auto-detect address if left empty.`}
{`An address to an existing Prometheus installation (<namespace>/<service>:<port>). ${this.props.productName} tries to auto-detect address if left empty.`}
</small>
</section>
</>
@ -164,3 +169,10 @@ export class ClusterPrometheusSetting extends React.Component<ClusterPrometheusS
);
}
}
export const ClusterPrometheusSetting = withInjectables<Dependencies, ClusterPrometheusSettingProps>(NonInjectedClusterPrometheusSetting, {
getProps: (di, props) => ({
...props,
productName: di.inject(productNameInjectable),
}),
});

View File

@ -35,7 +35,6 @@ import apiManagerInjectable from "../common/k8s-api/api-manager/manager.injectab
import setupOnApiErrorListenersInjectable from "./api/setup-on-api-errors.injectable";
import { observable, computed, runInAction } from "mobx";
import defaultShellInjectable from "./components/+preferences/default-shell.injectable";
import appVersionInjectable from "../common/vars/app-version.injectable";
import requestAnimationFrameInjectable from "./components/animate/request-animation-frame.injectable";
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
import getFilePathsInjectable from "./components/+preferences/kubernetes/helm-charts/adding-of-custom-helm-repository/helm-file-input/get-file-paths.injectable";
@ -114,8 +113,6 @@ export const getDiForUnitTesting = (
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
di.override(joinPathsInjectable, () => joinPathsFake);
di.override(appVersionInjectable, () => "1.0.0");
di.override(historyInjectable, () => createMemoryHistory());
di.override(legacyOnChannelListenInjectable, () => () => noop);

View File

@ -0,0 +1,33 @@
/**
* 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 { runInAction } from "mobx";
import { requestFromChannelInjectionToken } from "../../common/utils/channel/request-from-channel-injection-token";
import { buildVersionChannel, buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
const setupBuildVersionInjectable = getInjectable({
id: "setup-build-version",
instantiate: (di) => {
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
return {
run: async () => {
const buildVersion = await requestFromChannel(buildVersionChannel);
runInAction(() => {
di.register(getInjectable({
id: "build-version",
instantiate: () => buildVersion,
injectionToken: buildVersionInjectionToken,
}));
});
},
};
},
injectionToken: beforeFrameStartsInjectionToken,
});
export default setupBuildVersionInjectable;