From f0f06f53d29480e1b7a4ccb4db4ae365fa6fd51b Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 7 Jul 2022 14:30:20 -0400 Subject: [PATCH] Remove isTestEnv - Make all migration declarations injectable using a token - Make all migrations injectable using a token for the renderer side (instead of clearing them based on the truthiness of ipcRenderer) Signed-off-by: Sebastian Malton --- src/common/base-store.ts | 15 +- src/common/catalog-entities/web-link.ts | 2 +- .../cluster-store/cluster-store.injectable.ts | 2 + src/common/cluster-store/cluster-store.ts | 7 +- src/common/cluster-store/migrations.ts | 12 ++ src/common/fs/read-file-sync.injectable.ts | 23 ++- src/common/hotbars/migrations.ts | 12 ++ src/common/hotbars/store.ts | 5 +- src/common/kube-helpers.ts | 6 - src/common/logger.ts | 14 +- src/common/user-store/migrations.ts | 16 ++ .../user-store/user-store.injectable.ts | 10 +- src/common/user-store/user-store.ts | 7 +- .../utils/environment-variables.injectable.ts | 5 +- src/common/vars.ts | 7 +- src/common/vars/is-development.injectable.ts | 8 +- src/common/vars/is-test-env.injectable.ts | 18 -- src/common/weblinks/migrations.ts | 12 ++ .../store.injectable.ts} | 9 +- .../{weblink-store.ts => weblinks/store.ts} | 14 +- .../sync-weblinks.injectable.ts | 2 +- src/main/catalog-sources/weblinks.ts | 2 +- src/main/getDiForUnitTesting.ts | 3 +- .../cluster-store/3.6.0-beta.1.injectable.ts | 102 ++++++++++ .../cluster-store/5.0.0-beta.10.injectable.ts | 65 +++++++ .../cluster-store/5.0.0-beta.13.injectable.ts | 137 ++++++++++++++ .../migrations/cluster-store/migration.ts | 11 ++ .../cluster-store/migrations.injectable.ts | 23 +++ .../cluster-store/snap.injectable.ts | 55 ++++++ src/main/migrations/declaration.ts | 10 + .../hotbar-store/5.0.0-alpha.0.injectable.ts | 32 ++++ .../hotbar-store/5.0.0-alpha.2.injectable.ts | 29 +++ .../hotbar-store/5.0.0-beta.10.injectable.ts | 174 ++++++++++++++++++ .../hotbar-store/5.0.0-beta.5.injectable.ts | 54 ++++++ src/main/migrations/hotbar-store/migration.ts | 11 ++ .../hotbar-store/migrations.injectable.ts | 22 +++ src/main/migrations/join.injectable.ts | 41 +++++ src/main/migrations/log.injectable.ts | 19 ++ .../user-store/2.1.0-beta.4.migration.ts | 21 +++ .../user-store/5.0.0-alpha.3.injectable.ts | 30 +++ .../user-store/5.0.3-beta.1.injectable.ts | 101 ++++++++++ .../file-name-migration.injectable.ts | 8 +- src/main/migrations/user-store/migration.ts | 11 ++ .../user-store/migrations.injectable.ts | 23 +++ src/{ => main}/migrations/utils.ts | 0 .../weblinks-store/5.1.4.injectable.ts | 42 +++++ .../weblinks-store/5.4.5-beta.1.injectable.ts | 64 +++++++ .../currentVersion.injectable.ts | 32 ++++ .../migrations/weblinks-store/migration.ts | 11 ++ .../weblinks-store/migrations.injectable.ts | 23 +++ src/migrations/cluster-store/3.6.0-beta.1.ts | 90 --------- src/migrations/cluster-store/5.0.0-beta.10.ts | 56 ------ src/migrations/cluster-store/5.0.0-beta.13.ts | 128 ------------- src/migrations/cluster-store/index.ts | 20 -- src/migrations/cluster-store/snap.ts | 42 ----- src/migrations/helpers.ts | 41 ----- src/migrations/hotbar-store/5.0.0-alpha.0.ts | 25 --- src/migrations/hotbar-store/5.0.0-alpha.2.ts | 22 --- src/migrations/hotbar-store/5.0.0-beta.10.ts | 165 ----------------- src/migrations/hotbar-store/5.0.0-beta.5.ts | 45 ----- src/migrations/hotbar-store/index.ts | 20 -- src/migrations/user-store/2.1.0-beta.4.ts | 14 -- src/migrations/user-store/5.0.0-alpha.3.ts | 23 --- src/migrations/user-store/5.0.3-beta.1.ts | 78 -------- src/migrations/user-store/index.ts | 18 -- src/migrations/weblinks-store/5.1.4.ts | 34 ---- src/migrations/weblinks-store/5.4.5-beta.1.ts | 55 ------ .../weblinks-store/currentVersion.ts | 24 --- src/migrations/weblinks-store/index.ts | 16 -- src/renderer/bootstrap.tsx | 4 +- .../catalog-entities/weblink-add-command.tsx | 4 +- src/renderer/getDiForUnitTesting.tsx | 1 - .../migrations/cluster-store.injectable.ts | 14 ++ .../migrations/hotbar-store.injectable.ts | 14 ++ .../migrations/user-store.injectable.ts | 14 ++ .../migrations/weblinks-store.injectable.ts | 14 ++ .../utils/create-storage/create-storage.ts | 6 +- 77 files changed, 1330 insertions(+), 1019 deletions(-) create mode 100644 src/common/cluster-store/migrations.ts create mode 100644 src/common/hotbars/migrations.ts create mode 100644 src/common/user-store/migrations.ts delete mode 100644 src/common/vars/is-test-env.injectable.ts create mode 100644 src/common/weblinks/migrations.ts rename src/common/{weblink-store.injectable.ts => weblinks/store.injectable.ts} (58%) rename src/common/{weblink-store.ts => weblinks/store.ts} (80%) create mode 100644 src/main/migrations/cluster-store/3.6.0-beta.1.injectable.ts create mode 100644 src/main/migrations/cluster-store/5.0.0-beta.10.injectable.ts create mode 100644 src/main/migrations/cluster-store/5.0.0-beta.13.injectable.ts create mode 100644 src/main/migrations/cluster-store/migration.ts create mode 100644 src/main/migrations/cluster-store/migrations.injectable.ts create mode 100644 src/main/migrations/cluster-store/snap.injectable.ts create mode 100644 src/main/migrations/declaration.ts create mode 100644 src/main/migrations/hotbar-store/5.0.0-alpha.0.injectable.ts create mode 100644 src/main/migrations/hotbar-store/5.0.0-alpha.2.injectable.ts create mode 100644 src/main/migrations/hotbar-store/5.0.0-beta.10.injectable.ts create mode 100644 src/main/migrations/hotbar-store/5.0.0-beta.5.injectable.ts create mode 100644 src/main/migrations/hotbar-store/migration.ts create mode 100644 src/main/migrations/hotbar-store/migrations.injectable.ts create mode 100644 src/main/migrations/join.injectable.ts create mode 100644 src/main/migrations/log.injectable.ts create mode 100644 src/main/migrations/user-store/2.1.0-beta.4.migration.ts create mode 100644 src/main/migrations/user-store/5.0.0-alpha.3.injectable.ts create mode 100644 src/main/migrations/user-store/5.0.3-beta.1.injectable.ts rename src/{common => main/migrations}/user-store/file-name-migration.injectable.ts (75%) create mode 100644 src/main/migrations/user-store/migration.ts create mode 100644 src/main/migrations/user-store/migrations.injectable.ts rename src/{ => main}/migrations/utils.ts (100%) create mode 100644 src/main/migrations/weblinks-store/5.1.4.injectable.ts create mode 100644 src/main/migrations/weblinks-store/5.4.5-beta.1.injectable.ts create mode 100644 src/main/migrations/weblinks-store/currentVersion.injectable.ts create mode 100644 src/main/migrations/weblinks-store/migration.ts create mode 100644 src/main/migrations/weblinks-store/migrations.injectable.ts delete mode 100644 src/migrations/cluster-store/3.6.0-beta.1.ts delete mode 100644 src/migrations/cluster-store/5.0.0-beta.10.ts delete mode 100644 src/migrations/cluster-store/5.0.0-beta.13.ts delete mode 100644 src/migrations/cluster-store/index.ts delete mode 100644 src/migrations/cluster-store/snap.ts delete mode 100644 src/migrations/helpers.ts delete mode 100644 src/migrations/hotbar-store/5.0.0-alpha.0.ts delete mode 100644 src/migrations/hotbar-store/5.0.0-alpha.2.ts delete mode 100644 src/migrations/hotbar-store/5.0.0-beta.10.ts delete mode 100644 src/migrations/hotbar-store/5.0.0-beta.5.ts delete mode 100644 src/migrations/hotbar-store/index.ts delete mode 100644 src/migrations/user-store/2.1.0-beta.4.ts delete mode 100644 src/migrations/user-store/5.0.0-alpha.3.ts delete mode 100644 src/migrations/user-store/5.0.3-beta.1.ts delete mode 100644 src/migrations/user-store/index.ts delete mode 100644 src/migrations/weblinks-store/5.1.4.ts delete mode 100644 src/migrations/weblinks-store/5.4.5-beta.1.ts delete mode 100644 src/migrations/weblinks-store/currentVersion.ts delete mode 100644 src/migrations/weblinks-store/index.ts create mode 100644 src/renderer/migrations/cluster-store.injectable.ts create mode 100644 src/renderer/migrations/hotbar-store.injectable.ts create mode 100644 src/renderer/migrations/user-store.injectable.ts create mode 100644 src/renderer/migrations/weblinks-store.injectable.ts diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 9cc4e27b92..e61915d5c2 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -14,7 +14,6 @@ import { Singleton, toJS } from "./utils"; import logger from "../main/logger"; import { broadcastMessage, ipcMainOn, ipcRendererOn } from "./ipc"; import isEqual from "lodash/isEqual"; -import { isTestEnv } from "./vars"; 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"; @@ -40,22 +39,15 @@ export abstract class BaseStore extends Singleton { protected constructor(protected params: BaseStoreParams) { super(); makeObservable(this); - - if (ipcRenderer) { - params.migrations = undefined; // don't run migrations on renderer - } } /** * This must be called after the last child's constructor is finished (or just before it finishes) */ load() { - if (!isTestEnv) { - logger.info(`[${kebabCase(this.displayName).toUpperCase()}]: LOADING from ${this.path} ...`); - } + logger.debug(`[${kebabCase(this.displayName).toUpperCase()}]: LOADING from ${this.path} ...`); const di = getLegacyGlobalDiForExtensionApi(); - const getConfigurationFileModel = di.inject(getConfigurationFileModelInjectable); this.storeConfig = getConfigurationFileModel({ @@ -72,10 +64,7 @@ export abstract class BaseStore extends Singleton { } this.enableSync(); - - if (!isTestEnv) { - logger.info(`[${kebabCase(this.displayName).toUpperCase()}]: LOADED from ${this.path}`); - } + logger.debug(`[${kebabCase(this.displayName).toUpperCase()}]: LOADED from ${this.path}`); } get name() { diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts index 35dc86be57..6ab75f37b2 100644 --- a/src/common/catalog-entities/web-link.ts +++ b/src/common/catalog-entities/web-link.ts @@ -6,7 +6,7 @@ import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog"; import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; import { productName } from "../vars"; -import { WeblinkStore } from "../weblink-store"; +import { WeblinkStore } from "../weblinks/store"; export type WebLinkStatusPhase = "available" | "unavailable"; diff --git a/src/common/cluster-store/cluster-store.injectable.ts b/src/common/cluster-store/cluster-store.injectable.ts index 5a601e1866..6d759387e2 100644 --- a/src/common/cluster-store/cluster-store.injectable.ts +++ b/src/common/cluster-store/cluster-store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { ClusterStore } from "./cluster-store"; import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token"; import readClusterConfigSyncInjectable from "./read-cluster-config.injectable"; +import { clusterStoreMigrationsInjectionToken } from "./migrations"; const clusterStoreInjectable = getInjectable({ id: "cluster-store", @@ -16,6 +17,7 @@ const clusterStoreInjectable = getInjectable({ return ClusterStore.createInstance({ createCluster: di.inject(createClusterInjectionToken), readClusterConfigSync: di.inject(readClusterConfigSyncInjectable), + migrations: di.inject(clusterStoreMigrationsInjectionToken), }); }, diff --git a/src/common/cluster-store/cluster-store.ts b/src/common/cluster-store/cluster-store.ts index 62b8cda973..c887094b62 100644 --- a/src/common/cluster-store/cluster-store.ts +++ b/src/common/cluster-store/cluster-store.ts @@ -8,7 +8,6 @@ import { ipcMain, ipcRenderer, webFrame } from "electron"; import { action, comparer, computed, makeObservable, observable, reaction } from "mobx"; import { BaseStore } from "../base-store"; import { Cluster } from "../cluster/cluster"; -import migrations from "../../migrations/cluster-store"; import logger from "../../main/logger"; import { appEventBus } from "../app-event-bus/event-bus"; import { ipcMainHandle } from "../ipc"; @@ -18,6 +17,7 @@ import { requestInitialClusterStates } from "../../renderer/ipc"; import { clusterStates } from "../ipc/cluster"; import type { CreateCluster } from "../cluster/create-cluster-injection-token"; import type { ReadClusterConfigSync } from "./read-cluster-config.injectable"; +import type { Migrations } from "conf/dist/source/types"; export interface ClusterStoreModel { clusters?: ClusterModel[]; @@ -26,6 +26,7 @@ export interface ClusterStoreModel { interface Dependencies { createCluster: CreateCluster; readClusterConfigSync: ReadClusterConfigSync; + readonly migrations: Migrations | undefined; } export class ClusterStore extends BaseStore { @@ -34,14 +35,14 @@ export class ClusterStore extends BaseStore { protected disposer = disposer(); - constructor(private dependencies: Dependencies) { + constructor(protected readonly dependencies: Dependencies) { super({ configName: "lens-cluster-store", accessPropertiesByDotNotation: false, // To make dots safe in cluster context names syncOptions: { equals: comparer.structural, }, - migrations, + migrations: dependencies.migrations, }); makeObservable(this); diff --git a/src/common/cluster-store/migrations.ts b/src/common/cluster-store/migrations.ts new file mode 100644 index 0000000000..bf9ee1655d --- /dev/null +++ b/src/common/cluster-store/migrations.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { Migrations } from "conf/dist/source/types"; +import type { ClusterStoreModel } from "./cluster-store"; + +export const clusterStoreMigrationsInjectionToken = getInjectionToken | undefined>({ + id: "cluster-store-migrations-token", +}); diff --git a/src/common/fs/read-file-sync.injectable.ts b/src/common/fs/read-file-sync.injectable.ts index cdb2e4b4d2..be0831ddd1 100644 --- a/src/common/fs/read-file-sync.injectable.ts +++ b/src/common/fs/read-file-sync.injectable.ts @@ -5,14 +5,31 @@ import { getInjectable } from "@ogre-tools/injectable"; import fsInjectable from "./fs.injectable"; -export type ReadFileSync = (filePath: string) => string; +export interface ReadFileAsBufferOptions { + asBuffer: true; +} + +export interface ReadFileAsStringOptions { + asBuffer: false; +} + +export interface ReadFileSync { + (filePath: string, options?: ReadFileAsStringOptions): string; + (filePath: string, options: ReadFileAsBufferOptions): Buffer; +} const readFileSyncInjectable = getInjectable({ id: "read-file-sync", - instantiate: (di): ReadFileSync => { + instantiate: (di) => { const { readFileSync } = di.inject(fsInjectable); - return (filePath) => readFileSync(filePath, "utf-8"); + return ((filePath, options = { asBuffer: false }) => { + if (options.asBuffer) { + return readFileSync(filePath); + } else { + return readFileSync(filePath, "utf-8"); + } + }) as ReadFileSync; }, }); diff --git a/src/common/hotbars/migrations.ts b/src/common/hotbars/migrations.ts new file mode 100644 index 0000000000..75e7355f33 --- /dev/null +++ b/src/common/hotbars/migrations.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { Migrations } from "conf/dist/source/types"; +import type { HotbarStoreModel } from "./store"; + +export const hotbarStoreMigrationsInjectionToken = getInjectionToken | undefined>({ + id: "hotbar-store-migrations-token", +}); diff --git a/src/common/hotbars/store.ts b/src/common/hotbars/store.ts index a75182b23b..520bcc199d 100644 --- a/src/common/hotbars/store.ts +++ b/src/common/hotbars/store.ts @@ -5,7 +5,6 @@ import { action, comparer, observable, makeObservable, computed } from "mobx"; import { BaseStore } from "../base-store"; -import migrations from "../../migrations/hotbar-store"; import { toJS } from "../utils"; import type { CatalogEntity } from "../catalog"; import { broadcastMessage } from "../ipc"; @@ -15,6 +14,7 @@ import { hotbarTooManyItemsChannel } from "../ipc/hotbar"; import type { GeneralEntity } from "../catalog-entities"; import type { Logger } from "../logger"; import assert from "assert"; +import type { Migrations } from "conf/dist/source/types"; export interface HotbarStoreModel { hotbars: Hotbar[]; @@ -24,6 +24,7 @@ export interface HotbarStoreModel { interface Dependencies { readonly catalogCatalogEntity: GeneralEntity; readonly logger: Logger; + readonly migrations: Migrations | undefined; } export class HotbarStore extends BaseStore { @@ -38,7 +39,7 @@ export class HotbarStore extends BaseStore { syncOptions: { equals: comparer.structural, }, - migrations, + migrations: dependencies.migrations, }); makeObservable(this); diff --git a/src/common/kube-helpers.ts b/src/common/kube-helpers.ts index f24d4dbe2d..3a90bc79ff 100644 --- a/src/common/kube-helpers.ts +++ b/src/common/kube-helpers.ts @@ -17,12 +17,6 @@ import type { PartialDeep } from "type-fest"; export const kubeConfigDefaultPath = path.join(os.homedir(), ".kube", "config"); -export function loadConfigFromFileSync(filePath: string): ConfigResult { - const content = fse.readFileSync(resolvePath(filePath), "utf-8"); - - return loadConfigFromString(content); -} - export async function loadConfigFromFile(filePath: string): Promise { const content = await fse.readFile(resolvePath(filePath), "utf-8"); diff --git a/src/common/logger.ts b/src/common/logger.ts index fd9345d61e..3dad5b13bc 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -7,7 +7,7 @@ import { app, ipcMain } from "electron"; import winston, { format } from "winston"; import type Transport from "winston-transport"; import { consoleFormat } from "winston-console-format"; -import { isDebugging, isTestEnv } from "./vars"; +import { isDebugging, isProduction } from "./vars"; import BrowserConsole from "winston-transport-browserconsole"; export interface Logger { @@ -18,13 +18,8 @@ export interface Logger { silly: (message: string, ...args: any) => void; } -const logLevel = process.env.LOG_LEVEL - ? process.env.LOG_LEVEL - : isDebugging - ? "debug" - : isTestEnv - ? "error" - : "info"; +const defaultLogLevel = isDebugging ? "debug" : "info"; +const logLevel = process.env.LOG_LEVEL || defaultLogLevel; const transports: Transport[] = []; @@ -51,7 +46,8 @@ if (ipcMain) { }), ); - if (!isTestEnv) { + // TODO: replace logger with injectable + if (isProduction) { transports.push( new winston.transports.File({ handleExceptions: false, diff --git a/src/common/user-store/migrations.ts b/src/common/user-store/migrations.ts new file mode 100644 index 0000000000..6550033755 --- /dev/null +++ b/src/common/user-store/migrations.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { Migrations } from "conf/dist/source/types"; +import type { UserStoreModel } from "./user-store"; + +export const userStoreMigrationsInjectionToken = getInjectionToken | undefined>({ + id: "user-store-migrations-token", +}); + +export const userStorePreMigrationsInjectionToken = getInjectionToken<() => void>({ + id: "user-store-pre-migrations-token", +}); diff --git a/src/common/user-store/user-store.injectable.ts b/src/common/user-store/user-store.injectable.ts index 3b4aba0b56..87c216ae5c 100644 --- a/src/common/user-store/user-store.injectable.ts +++ b/src/common/user-store/user-store.injectable.ts @@ -3,23 +3,25 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { ipcMain } from "electron"; -import userStoreFileNameMigrationInjectable from "./file-name-migration.injectable"; import { UserStore } from "./user-store"; import selectedUpdateChannelInjectable from "../application-update/selected-update-channel/selected-update-channel.injectable"; +import { userStoreMigrationsInjectionToken, userStorePreMigrationsInjectionToken } from "./migrations"; const userStoreInjectable = getInjectable({ id: "user-store", instantiate: (di) => { + const preMigrations = di.injectMany(userStorePreMigrationsInjectionToken); + UserStore.resetInstance(); - if (ipcMain) { - di.inject(userStoreFileNameMigrationInjectable); + for (const preMigration of preMigrations) { + preMigration(); } return UserStore.createInstance({ selectedUpdateChannel: di.inject(selectedUpdateChannelInjectable), + migrations: di.inject(userStoreMigrationsInjectionToken), }); }, diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index b806732735..70de68c869 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -7,7 +7,6 @@ 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"; @@ -17,6 +16,7 @@ import type { UserPreferencesModel, StoreType } from "./preferences-helpers"; import logger from "../../main/logger"; import type { SelectedUpdateChannel } from "../application-update/selected-update-channel/selected-update-channel.injectable"; import type { UpdateChannelId } from "../application-update/update-channels"; +import type { Migrations } from "conf/dist/source/types"; export interface UserStoreModel { lastSeenAppVersion: string; @@ -24,7 +24,8 @@ export interface UserStoreModel { } interface Dependencies { - selectedUpdateChannel: SelectedUpdateChannel; + readonly selectedUpdateChannel: SelectedUpdateChannel; + readonly migrations: Migrations | undefined; } export class UserStore extends BaseStore /* implements UserStoreFlatModel (when strict null is enabled) */ { @@ -33,7 +34,7 @@ export class UserStore extends BaseStore /* implements UserStore constructor(private readonly dependencies: Dependencies) { super({ configName: "lens-user-store", - migrations, + migrations: dependencies.migrations, }); makeObservable(this); diff --git a/src/common/utils/environment-variables.injectable.ts b/src/common/utils/environment-variables.injectable.ts index 897b349d56..8df689cc3f 100644 --- a/src/common/utils/environment-variables.injectable.ts +++ b/src/common/utils/environment-variables.injectable.ts @@ -11,16 +11,13 @@ const environmentVariablesInjectable = getInjectable({ // IMPORTANT: The syntax needs to be exactly this in order to make environment variable values // hard-coded at compile-time by Webpack. const NODE_ENV = process.env.NODE_ENV; - const JEST_WORKER_ID = process.env.JEST_WORKER_ID; - const CICD = process.env.CICD; return { // Compile-time environment variables NODE_ENV, - JEST_WORKER_ID, - CICD, // Runtime environment variables + CICD: process.env.CICD, LENS_DISABLE_GPU: process.env.LENS_DISABLE_GPU, }; }, diff --git a/src/common/vars.ts b/src/common/vars.ts index fb9156725c..663703d7d0 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -28,11 +28,6 @@ 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 - */ -export const isTestEnv = !!process.env.JEST_WORKER_ID; - /** * @deprecated Switch to using isProductionInjectable */ @@ -41,7 +36,7 @@ export const isProduction = process.env.NODE_ENV === "production"; /** * @deprecated Switch to using isDevelopmentInjectable */ -export const isDevelopment = !isTestEnv && !isProduction; +export const isDevelopment = !isProduction; export const productName = packageInfo.productName; diff --git a/src/common/vars/is-development.injectable.ts b/src/common/vars/is-development.injectable.ts index 190d754d8d..697d7b5ce6 100644 --- a/src/common/vars/is-development.injectable.ts +++ b/src/common/vars/is-development.injectable.ts @@ -4,17 +4,11 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import isProductionInjectable from "./is-production.injectable"; -import isTestEnvInjectable from "./is-test-env.injectable"; const isDevelopmentInjectable = getInjectable({ id: "is-development", - instantiate: (di) => { - const isProduction = di.inject(isProductionInjectable); - const isTestEnv = di.inject(isTestEnvInjectable); - - return !isTestEnv && !isProduction; - }, + instantiate: (di) => !di.inject(isProductionInjectable), }); export default isDevelopmentInjectable; diff --git a/src/common/vars/is-test-env.injectable.ts b/src/common/vars/is-test-env.injectable.ts deleted file mode 100644 index 85965d0098..0000000000 --- a/src/common/vars/is-test-env.injectable.ts +++ /dev/null @@ -1,18 +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 environmentVariablesInjectable from "../utils/environment-variables.injectable"; - -const isTestEnvInjectable = getInjectable({ - id: "is-test-env", - - instantiate: (di) => { - const { JEST_WORKER_ID: jestWorkerId } = di.inject(environmentVariablesInjectable); - - return !!jestWorkerId; - }, -}); - -export default isTestEnvInjectable; diff --git a/src/common/weblinks/migrations.ts b/src/common/weblinks/migrations.ts new file mode 100644 index 0000000000..6c33291b56 --- /dev/null +++ b/src/common/weblinks/migrations.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { Migrations } from "conf/dist/source/types"; +import type { WeblinkStoreModel } from "./store"; + +export const weblinksStoreMigrationsInjectionToken = getInjectionToken | undefined>({ + id: "weblinks-store-migrations-token", +}); diff --git a/src/common/weblink-store.injectable.ts b/src/common/weblinks/store.injectable.ts similarity index 58% rename from src/common/weblink-store.injectable.ts rename to src/common/weblinks/store.injectable.ts index 4aca7dce2a..0a00d32056 100644 --- a/src/common/weblink-store.injectable.ts +++ b/src/common/weblinks/store.injectable.ts @@ -3,15 +3,18 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { WeblinkStore } from "./weblink-store"; +import { weblinksStoreMigrationsInjectionToken } from "./migrations"; +import { WeblinkStore } from "./store"; const weblinkStoreInjectable = getInjectable({ id: "weblink-store", - instantiate: () => { + instantiate: (di) => { WeblinkStore.resetInstance(); - return WeblinkStore.createInstance(); + return WeblinkStore.createInstance({ + migrations: di.inject(weblinksStoreMigrationsInjectionToken), + }); }, }); diff --git a/src/common/weblink-store.ts b/src/common/weblinks/store.ts similarity index 80% rename from src/common/weblink-store.ts rename to src/common/weblinks/store.ts index a8430666e5..5510ae1ebd 100644 --- a/src/common/weblink-store.ts +++ b/src/common/weblinks/store.ts @@ -4,10 +4,10 @@ */ import { action, comparer, observable, makeObservable } from "mobx"; -import { BaseStore } from "./base-store"; -import migrations from "../migrations/weblinks-store"; +import { BaseStore } from "../base-store"; import * as uuid from "uuid"; -import { toJS } from "./utils"; +import { toJS } from "../utils"; +import type { Migrations } from "conf/dist/source/types"; export interface WeblinkData { id: string; @@ -26,18 +26,22 @@ export interface WeblinkStoreModel { weblinks: WeblinkData[]; } +export interface WeblinkStoreDependencies { + readonly migrations: Migrations | undefined; +} + export class WeblinkStore extends BaseStore { readonly displayName = "WeblinkStore"; @observable weblinks: WeblinkData[] = []; - constructor() { + constructor(protected readonly dependencies: WeblinkStoreDependencies) { super({ configName: "lens-weblink-store", accessPropertiesByDotNotation: false, // To make dots safe in cluster context names syncOptions: { equals: comparer.structural, }, - migrations, + migrations: dependencies.migrations, }); makeObservable(this); this.load(); diff --git a/src/main/catalog-sources/sync-weblinks.injectable.ts b/src/main/catalog-sources/sync-weblinks.injectable.ts index 836418781d..36e900e629 100644 --- a/src/main/catalog-sources/sync-weblinks.injectable.ts +++ b/src/main/catalog-sources/sync-weblinks.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { syncWeblinks } from "./weblinks"; -import weblinkStoreInjectable from "../../common/weblink-store.injectable"; +import weblinkStoreInjectable from "../../common/weblinks/store.injectable"; import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; const syncWeblinksInjectable = getInjectable({ diff --git a/src/main/catalog-sources/weblinks.ts b/src/main/catalog-sources/weblinks.ts index 353f5f91bb..80ccb582c1 100644 --- a/src/main/catalog-sources/weblinks.ts +++ b/src/main/catalog-sources/weblinks.ts @@ -4,7 +4,7 @@ */ import { computed, observable, reaction } from "mobx"; -import type { WeblinkStore } from "../../common/weblink-store"; +import type { WeblinkStore } from "../../common/weblinks/store"; import { WebLink } from "../../common/catalog-entities"; import type { CatalogEntityRegistry } from "../catalog"; import got from "got"; diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index 51a677ad07..0d5f356d1f 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -101,6 +101,7 @@ import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import electronInjectable from "./utils/resolve-system-proxy/electron.injectable"; import type { HotbarStore } from "../common/hotbars/store"; import focusApplicationInjectable from "./electron-app/features/focus-application.injectable"; +import migrationLogInjectable from "./migrations/log.injectable"; export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) { const { @@ -127,7 +128,7 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) di.override(electronInjectable, () => ({})); di.override(waitUntilBundledExtensionsAreLoadedInjectable, () => async () => {}); di.override(getRandomIdInjectable, () => () => "some-irrelevant-random-id"); - + di.override(migrationLogInjectable, () => noop); di.override(hotbarStoreInjectable, () => ({ load: () => {}, getActive: () => ({ name: "some-hotbar", items: [] }), diff --git a/src/main/migrations/cluster-store/3.6.0-beta.1.injectable.ts b/src/main/migrations/cluster-store/3.6.0-beta.1.injectable.ts new file mode 100644 index 0000000000..ddb7f376be --- /dev/null +++ b/src/main/migrations/cluster-store/3.6.0-beta.1.injectable.ts @@ -0,0 +1,102 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +// Move embedded kubeconfig into separate file and add reference to it to cluster settings +// convert file path cluster icons to their base64 encoded versions + +import { loadConfigFromString } from "../../../common/kube-helpers"; +import type { ClusterModel } from "../../../common/cluster-types"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; +import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { clusterStoreMigrationDeclarationInjectionToken } from "./migration"; +import migrationLogInjectable from "../log.injectable"; +import fsInjectable from "../../../common/fs/fs.injectable"; +import readFileSyncInjectable from "../../../common/fs/read-file-sync.injectable"; +import joinPathsInjectable from "../../../common/path/join-paths.injectable"; + +interface Pre360ClusterModel extends ClusterModel { + kubeConfig?: string; +} + + +const clusterStoreV360Beta1MigrationInjectable = getInjectable({ + id: "cluster-store-v3.6.0-beta.1-migration", + instantiate: (di) => { + const migrationLog = di.inject(migrationLogInjectable); + const userDataPath = di.inject(directoryForUserDataInjectable); + const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable); + const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable); + const { ensureDirSync, writeFileSync } = di.inject(fsInjectable); + const readFileSync = di.inject(readFileSyncInjectable); + const joinPaths = di.inject(joinPathsInjectable); + + return { + version: "3.6.0-beta.1", + run(store) { + const storedClusters = (store.get("clusters") ?? []) as Pre360ClusterModel[]; + const migratedClusters: ClusterModel[] = []; + + ensureDirSync(kubeConfigsPath); + migrationLog("Number of clusters to migrate: ", storedClusters.length); + + for (const clusterModel of storedClusters) { + /** + * migrate kubeconfig + */ + try { + const absPath = getCustomKubeConfigDirectory(clusterModel.id); + + if (!clusterModel.kubeConfig) { + continue; + } + + // take the embedded kubeconfig and dump it into a file + writeFileSync(absPath, clusterModel.kubeConfig, { encoding: "utf-8", mode: 0o600 }); + + clusterModel.kubeConfigPath = absPath; + delete clusterModel.kubeConfig; + + const clusterConfigData = readFileSync(clusterModel.kubeConfigPath); + const clusterConfig = loadConfigFromString(clusterConfigData); + + clusterModel.contextName = clusterConfig.config.getCurrentContext(); + } catch (error) { + migrationLog(`Failed to migrate Kubeconfig for cluster "${clusterModel.id}", removing clusterModel...`, error); + + continue; + } + + /** + * migrate cluster icon + */ + try { + if (clusterModel.preferences?.icon) { + migrationLog(`migrating ${clusterModel.preferences.icon} for ${clusterModel.preferences.clusterName}`); + const iconPath = clusterModel.preferences.icon.replace("store://", ""); + const fileData = readFileSync(joinPaths(userDataPath, iconPath), { asBuffer: true }); + + clusterModel.preferences.icon = `data:;base64,${fileData.toString("base64")}`; + } else { + delete clusterModel.preferences?.icon; + } + } catch (error) { + migrationLog(`Failed to migrate cluster icon for cluster "${clusterModel.id}"`, error); + delete clusterModel.preferences?.icon; + } + + migratedClusters.push(clusterModel); + } + + store.set("clusters", migratedClusters); + }, + }; + }, + injectionToken: clusterStoreMigrationDeclarationInjectionToken, +}); + +export default clusterStoreV360Beta1MigrationInjectable; + diff --git a/src/main/migrations/cluster-store/5.0.0-beta.10.injectable.ts b/src/main/migrations/cluster-store/5.0.0-beta.10.injectable.ts new file mode 100644 index 0000000000..3570f0dd41 --- /dev/null +++ b/src/main/migrations/cluster-store/5.0.0-beta.10.injectable.ts @@ -0,0 +1,65 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { ClusterModel } from "../../../common/cluster-types"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import { isErrnoException } from "../../../common/utils"; +import { getInjectable } from "@ogre-tools/injectable"; +import { clusterStoreMigrationDeclarationInjectionToken } from "./migration"; +import fsInjectable from "../../../common/fs/fs.injectable"; +import joinPathsInjectable from "../../../common/path/join-paths.injectable"; + +interface Pre500WorkspaceStoreModel { + workspaces: { + id: string; + name: string; + }[]; +} + +const clusterStoreV500Beta10MigrationInjectable = getInjectable({ + id: "clutster-store-v5.0.0-beta.10-migration", + instantiate: (di) => { + const userDataPath = di.inject(directoryForUserDataInjectable); + const { readJsonSync } = di.inject(fsInjectable); + const joinPaths = di.inject(joinPathsInjectable); + + return { + version: "5.0.0-beta.10", + run(store) { + try { + const workspaceData: Pre500WorkspaceStoreModel = readJsonSync(joinPaths(userDataPath, "lens-workspace-store.json")); + const workspaces = new Map(); // mapping from WorkspaceId to name + + for (const { id, name } of workspaceData.workspaces) { + workspaces.set(id, name); + } + + const clusters = (store.get("clusters") ?? []) as ClusterModel[]; + + for (const cluster of clusters) { + if (cluster.workspace) { + const workspace = workspaces.get(cluster.workspace); + + if (workspace) { + (cluster.labels ??= {}).workspace = workspace; + } + } + } + + store.set("clusters", clusters); + } catch (error) { + if (isErrnoException(error) && !(error.code === "ENOENT" && error.path?.endsWith("lens-workspace-store.json"))) { + // ignore lens-workspace-store.json being missing + throw error; + } + } + }, + }; + }, + injectionToken: clusterStoreMigrationDeclarationInjectionToken, +}); + +export default clusterStoreV500Beta10MigrationInjectable; + diff --git a/src/main/migrations/cluster-store/5.0.0-beta.13.injectable.ts b/src/main/migrations/cluster-store/5.0.0-beta.13.injectable.ts new file mode 100644 index 0000000000..10faa049e5 --- /dev/null +++ b/src/main/migrations/cluster-store/5.0.0-beta.13.injectable.ts @@ -0,0 +1,137 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../../../common/cluster-types"; +import { generateNewIdFor } from "../utils"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import { isDefined } from "../../../common/utils"; +import { getInjectable } from "@ogre-tools/injectable"; +import { clusterStoreMigrationDeclarationInjectionToken } from "./migration"; +import migrationLogInjectable from "../log.injectable"; +import fsInjectable from "../../../common/fs/fs.injectable"; +import joinPathsInjectable from "../../../common/path/join-paths.injectable"; + +function mergePrometheusPreferences(left: ClusterPrometheusPreferences, right: ClusterPrometheusPreferences): ClusterPrometheusPreferences { + if (left.prometheus && left.prometheusProvider) { + return { + prometheus: left.prometheus, + prometheusProvider: left.prometheusProvider, + }; + } + + if (right.prometheus && right.prometheusProvider) { + return { + prometheus: right.prometheus, + prometheusProvider: right.prometheusProvider, + }; + } + + return {}; +} + +function mergePreferences(left: ClusterPreferences, right: ClusterPreferences): ClusterPreferences { + return { + terminalCWD: left.terminalCWD || right.terminalCWD || undefined, + clusterName: left.clusterName || right.clusterName || undefined, + iconOrder: left.iconOrder || right.iconOrder || undefined, + icon: left.icon || right.icon || undefined, + httpsProxy: left.httpsProxy || right.httpsProxy || undefined, + hiddenMetrics: mergeSet(left.hiddenMetrics ?? [], right.hiddenMetrics ?? []), + ...mergePrometheusPreferences(left, right), + }; +} + +function mergeLabels(left: Record, right: Record): Record { + return { + ...right, + ...left, + }; +} + +function mergeSet(...iterables: Iterable[]): string[] { + const res = new Set(); + + for (const iterable of iterables) { + for (const val of iterable) { + if (val) { + res.add(val); + } + } + } + + return [...res]; +} + +function mergeClusterModel(prev: ClusterModel, right: Omit): ClusterModel { + return { + id: prev.id, + kubeConfigPath: prev.kubeConfigPath, + contextName: prev.contextName, + preferences: mergePreferences(prev.preferences ?? {}, right.preferences ?? {}), + metadata: prev.metadata, + labels: mergeLabels(prev.labels ?? {}, right.labels ?? {}), + accessibleNamespaces: mergeSet(prev.accessibleNamespaces ?? [], right.accessibleNamespaces ?? []), + workspace: prev.workspace || right.workspace, + workspaces: mergeSet([prev.workspace, right.workspace], prev.workspaces ?? [], right.workspaces ?? []), + }; +} + +const clusterStoreV500Beta13MigrationInjectable = getInjectable({ + id: "cluster-store-v5.0.0-beta.13-migration", + instantiate: (di) => { + const migrationLog = di.inject(migrationLogInjectable); + const userDataPath = di.inject(directoryForUserDataInjectable); + const joinPaths = di.inject(joinPathsInjectable); + const { moveSync, removeSync } = di.inject(fsInjectable); + + const moveStorageFolder = ({ folder, newId, oldId }: { folder: string; newId: string; oldId: string }) => { + const oldPath = joinPaths(folder, `${oldId}.json`); + const newPath = joinPaths(folder, `${newId}.json`); + + try { + moveSync(oldPath, newPath); + } catch (error) { + if (String(error).includes("dest already exists")) { + migrationLog(`Multiple old lens-local-storage files for newId=${newId}. Removing ${oldId}.json`); + removeSync(oldPath); + } + } + }; + + return { + version: "5.0.0-beta.13", + run(store) { + const folder = joinPaths(userDataPath, "lens-local-storage"); + + const oldClusters = (store.get("clusters") ?? []) as ClusterModel[]; + const clusters = new Map(); + + for (const { id: oldId, ...cluster } of oldClusters) { + const newId = generateNewIdFor(cluster); + const newCluster = clusters.get(newId); + + if (newCluster) { + migrationLog(`Duplicate entries for ${newId}`, { oldId }); + clusters.set(newId, mergeClusterModel(newCluster, cluster)); + } else { + migrationLog(`First entry for ${newId}`, { oldId }); + clusters.set(newId, { + ...cluster, + id: newId, + workspaces: [cluster.workspace].filter(isDefined), + }); + moveStorageFolder({ folder, newId, oldId }); + } + } + + store.set("clusters", [...clusters.values()]); + }, + }; + }, + injectionToken: clusterStoreMigrationDeclarationInjectionToken, +}); + +export default clusterStoreV500Beta13MigrationInjectable; + diff --git a/src/main/migrations/cluster-store/migration.ts b/src/main/migrations/cluster-store/migration.ts new file mode 100644 index 0000000000..7b3105805a --- /dev/null +++ b/src/main/migrations/cluster-store/migration.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { MigrationDeclaration } from "../declaration"; + +export const clusterStoreMigrationDeclarationInjectionToken = getInjectionToken({ + id: "cluster-store-migration-declaration-token", +}); diff --git a/src/main/migrations/cluster-store/migrations.injectable.ts b/src/main/migrations/cluster-store/migrations.injectable.ts new file mode 100644 index 0000000000..54b0be68d8 --- /dev/null +++ b/src/main/migrations/cluster-store/migrations.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +// Cluster store migrations + +import joinMigrationsInjectable from "../join.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { clusterStoreMigrationDeclarationInjectionToken } from "./migration"; + +const clusterStoreMigrationsInjectable = getInjectable({ + id: "cluster-store-migrations", + instantiate: (di) => { + const joinMigrations = di.inject(joinMigrationsInjectable); + const migrationDeclarations = di.injectMany(clusterStoreMigrationDeclarationInjectionToken); + + return joinMigrations(migrationDeclarations); + }, +}); + +export default clusterStoreMigrationsInjectable; + diff --git a/src/main/migrations/cluster-store/snap.injectable.ts b/src/main/migrations/cluster-store/snap.injectable.ts new file mode 100644 index 0000000000..a7723ae48e --- /dev/null +++ b/src/main/migrations/cluster-store/snap.injectable.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +// Fix embedded kubeconfig paths under snap config + +import type { ClusterModel } from "../../../common/cluster-types"; +import { getInjectable } from "@ogre-tools/injectable"; +import { clusterStoreMigrationDeclarationInjectionToken } from "./migration"; +import migrationLogInjectable from "../log.injectable"; +import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable"; +import fsInjectable from "../../../common/fs/fs.injectable"; + +const clusterStoreSnapMigrationInjectable = getInjectable({ + id: "cluster-store-snap-migration", + instantiate: (di) => { + const migrationLog = di.inject(migrationLogInjectable); + const { existsSync } = di.inject(fsInjectable); + + return { + version: di.inject(appVersionInjectable), // Run always after upgrade + run(store) { + if (!process.env.SNAP) return; + + migrationLog("Migrating embedded kubeconfig paths"); + const storedClusters = (store.get("clusters") || []) as ClusterModel[]; + + if (!storedClusters.length) return; + + migrationLog("Number of clusters to migrate: ", storedClusters.length); + const migratedClusters = storedClusters + .map(cluster => { + /** + * replace snap version with 'current' in kubeconfig path + */ + if (!existsSync(cluster.kubeConfigPath)) { + const kubeconfigPath = cluster.kubeConfigPath.replace(/\/snap\/kontena-lens\/[0-9]*\//, "/snap/kontena-lens/current/"); + + cluster.kubeConfigPath = kubeconfigPath; + } + + return cluster; + }); + + + store.set("clusters", migratedClusters); + }, + }; + }, + injectionToken: clusterStoreMigrationDeclarationInjectionToken, +}); + +export default clusterStoreSnapMigrationInjectable; + diff --git a/src/main/migrations/declaration.ts b/src/main/migrations/declaration.ts new file mode 100644 index 0000000000..6a9425f81a --- /dev/null +++ b/src/main/migrations/declaration.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type Conf from "conf"; + +export interface MigrationDeclaration { + version: string; + run(store: Conf>): void; +} diff --git a/src/main/migrations/hotbar-store/5.0.0-alpha.0.injectable.ts b/src/main/migrations/hotbar-store/5.0.0-alpha.0.injectable.ts new file mode 100644 index 0000000000..88704f8528 --- /dev/null +++ b/src/main/migrations/hotbar-store/5.0.0-alpha.0.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getEmptyHotbar } from "../../../common/hotbars/types"; +import catalogCatalogEntityInjectable from "../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { hotbarStoreMigrationDeclarationInjectionToken } from "./migration"; + +const hotbarStoreV500Alpha0MigrationInjectable = getInjectable({ + id: "hotbar-store-v5.0.0-alpha.0-migration", + instantiate: (di) => { + const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); + + return { + version: "5.0.0-alpha.0", + run(store) { + const hotbar = getEmptyHotbar("default"); + const { metadata: { uid, name, source }} = catalogCatalogEntity; + + hotbar.items[0] = { entity: { uid, name, source }}; + + store.set("hotbars", [hotbar]); + }, + }; + }, + injectionToken: hotbarStoreMigrationDeclarationInjectionToken, +}); + +export default hotbarStoreV500Alpha0MigrationInjectable; + diff --git a/src/main/migrations/hotbar-store/5.0.0-alpha.2.injectable.ts b/src/main/migrations/hotbar-store/5.0.0-alpha.2.injectable.ts new file mode 100644 index 0000000000..c7c0a17cf0 --- /dev/null +++ b/src/main/migrations/hotbar-store/5.0.0-alpha.2.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { Hotbar } from "../../../common/hotbars/types"; +import * as uuid from "uuid"; +import { getInjectable } from "@ogre-tools/injectable"; +import { hotbarStoreMigrationDeclarationInjectionToken } from "./migration"; + +const hotbarStoreV500Alpha2MigrationInjectable = getInjectable({ + id: "hotbar-store-v5.0.0-alpha.2-migration", + instantiate: () => ( { + version: "5.0.0-alpha.2", + run(store) { + const rawHotbars = store.get("hotbars"); + const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : []; + + store.set("hotbars", hotbars.map(({ id, ...rest }) => ({ + id: id || uuid.v4(), + ...rest, + }))); + }, + }), + injectionToken: hotbarStoreMigrationDeclarationInjectionToken, +}); + +export default hotbarStoreV500Alpha2MigrationInjectable; + diff --git a/src/main/migrations/hotbar-store/5.0.0-beta.10.injectable.ts b/src/main/migrations/hotbar-store/5.0.0-beta.10.injectable.ts new file mode 100644 index 0000000000..ebe9240015 --- /dev/null +++ b/src/main/migrations/hotbar-store/5.0.0-beta.10.injectable.ts @@ -0,0 +1,174 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import fse from "fs-extra"; +import { isNull } from "lodash"; +import path from "path"; +import * as uuid from "uuid"; +import type { ClusterStoreModel } from "../../../common/cluster-store/cluster-store"; +import type { Hotbar, HotbarItem } from "../../../common/hotbars/types"; +import { defaultHotbarCells, getEmptyHotbar } from "../../../common/hotbars/types"; +import { generateNewIdFor } from "../utils"; +import { getLegacyGlobalDiForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import catalogCatalogEntityInjectable from "../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; +import { isDefined, isErrnoException } from "../../../common/utils"; + +interface Pre500WorkspaceStoreModel { + workspaces: { + id: string; + name: string; + }[]; +} + +interface PartialHotbar { + id: string; + name: string; + items: (null | HotbarItem)[]; +} + +import { getInjectable } from "@ogre-tools/injectable"; +import migrationLogInjectable from "../log.injectable"; +import { hotbarStoreMigrationDeclarationInjectionToken } from "./migration"; + +const hotbarStoreV500Beta10MigrationInjectable = getInjectable({ + id: "hotbar-store-v5.0.0-beta.10-migration", + instantiate: (di) => { + const migrationLog = di.inject(migrationLogInjectable); + const userDataPath = di.inject(directoryForUserDataInjectable); + + return { + version: "5.0.0-beta.10", + run(store) { + const rawHotbars = store.get("hotbars"); + const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars.filter(h => h && typeof h === "object") : []; + + // Hotbars might be empty, if some of the previous migrations weren't run + if (hotbars.length === 0) { + const hotbar = getEmptyHotbar("default"); + + const di = getLegacyGlobalDiForExtensionApi(); + const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); + + const { metadata: { uid, name, source }} = catalogCatalogEntity; + + hotbar.items[0] = { entity: { uid, name, source }}; + + hotbars.push(hotbar); + } + + try { + const workspaceStoreData: Pre500WorkspaceStoreModel = fse.readJsonSync(path.join(userDataPath, "lens-workspace-store.json")); + const { clusters = [] }: ClusterStoreModel = fse.readJSONSync(path.join(userDataPath, "lens-cluster-store.json")); + const workspaceHotbars = new Map(); // mapping from WorkspaceId to HotBar + + for (const { id, name } of workspaceStoreData.workspaces) { + migrationLog(`Creating new hotbar for ${name}`); + workspaceHotbars.set(id, { + id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs + items: [], + name: `Workspace: ${name}`, + }); + } + + { + // grab the default named hotbar or the first. + const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default")); + const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1); + + workspaceHotbars.set("default", { + name, + id, + items: items.filter(isDefined), + }); + } + + for (const cluster of clusters) { + const uid = generateNewIdFor(cluster); + + for (const workspaceId of cluster.workspaces ?? [cluster.workspace].filter(isDefined)) { + const workspaceHotbar = workspaceHotbars.get(workspaceId); + + if (!workspaceHotbar) { + migrationLog(`Cluster ${uid} has unknown workspace ID, skipping`); + continue; + } + + migrationLog(`Adding cluster ${uid} to ${workspaceHotbar.name}`); + + if (workspaceHotbar?.items.length < defaultHotbarCells) { + workspaceHotbar.items.push({ + entity: { + uid: generateNewIdFor(cluster), + name: cluster.preferences?.clusterName || cluster.contextName, + }, + }); + } + } + } + + for (const hotbar of workspaceHotbars.values()) { + if (hotbar.items.length === 0) { + migrationLog(`Skipping ${hotbar.name} due to it being empty`); + continue; + } + + while (hotbar.items.length < defaultHotbarCells) { + hotbar.items.push(null); + } + + hotbars.push(hotbar as Hotbar); + } + + /** + * Finally, make sure that the catalog entity hotbar item is in place. + * Just in case something else removed it. + * + * if every hotbar has elements that all not the `catalog-entity` item + */ + if (hotbars.every(hotbar => hotbar.items.every(item => item?.entity?.uid !== "catalog-entity"))) { + // note, we will add a new whole hotbar here called "default" if that was previously removed + const di = getLegacyGlobalDiForExtensionApi(); + const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); + + const defaultHotbar = hotbars.find(hotbar => hotbar.name === "default"); + const { metadata: { uid, name, source }} = catalogCatalogEntity; + + if (defaultHotbar) { + const freeIndex = defaultHotbar.items.findIndex(isNull); + + if (freeIndex === -1) { + // making a new hotbar is less destructive if the first hotbar + // called "default" is full than overriding a hotbar item + const hotbar = getEmptyHotbar("initial"); + + hotbar.items[0] = { entity: { uid, name, source }}; + hotbars.unshift(hotbar); + } else { + defaultHotbar.items[freeIndex] = { entity: { uid, name, source }}; + } + } else { + const hotbar = getEmptyHotbar("default"); + + hotbar.items[0] = { entity: { uid, name, source }}; + hotbars.unshift(hotbar); + } + } + + } catch (error) { + // ignore files being missing + if (isErrnoException(error) && error.code !== "ENOENT") { + throw error; + } + } + + store.set("hotbars", hotbars); + }, + }; + }, + injectionToken: hotbarStoreMigrationDeclarationInjectionToken, +}); + +export default hotbarStoreV500Beta10MigrationInjectable; diff --git a/src/main/migrations/hotbar-store/5.0.0-beta.5.injectable.ts b/src/main/migrations/hotbar-store/5.0.0-beta.5.injectable.ts new file mode 100644 index 0000000000..0083fe2d2b --- /dev/null +++ b/src/main/migrations/hotbar-store/5.0.0-beta.5.injectable.ts @@ -0,0 +1,54 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { Hotbar } from "../../../common/hotbars/types"; +import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { hotbarStoreMigrationDeclarationInjectionToken } from "./migration"; + +const hotbarStoreV500Beta5MigrationInjectable = getInjectable({ + id: "hotbar-store-v5.0.0-beta.5-migration", + instantiate: (di) => { + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + + return { + version: "5.0.0-beta.5", + run(store) { + const rawHotbars = store.get("hotbars"); + const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : []; + + for (const hotbar of hotbars) { + for (let i = 0; i < hotbar.items.length; i += 1) { + const item = hotbar.items[i]; + + if (!item) { + continue; + } + + const entity = catalogEntityRegistry.findById(item.entity.uid); + + if (!entity) { + // Clear disabled item + hotbar.items[i] = null; + } else { + // Save additional data + item.entity = { + ...item.entity, + name: entity.metadata.name, + source: entity.metadata.source, + }; + } + } + } + + store.set("hotbars", hotbars); + }, + }; + }, + injectionToken: hotbarStoreMigrationDeclarationInjectionToken, +}); + +export default hotbarStoreV500Beta5MigrationInjectable; + diff --git a/src/main/migrations/hotbar-store/migration.ts b/src/main/migrations/hotbar-store/migration.ts new file mode 100644 index 0000000000..af366954d0 --- /dev/null +++ b/src/main/migrations/hotbar-store/migration.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { MigrationDeclaration } from "../declaration"; + +export const hotbarStoreMigrationDeclarationInjectionToken = getInjectionToken({ + id: "hotbar-store-migration-declaration-token", +}); diff --git a/src/main/migrations/hotbar-store/migrations.injectable.ts b/src/main/migrations/hotbar-store/migrations.injectable.ts new file mode 100644 index 0000000000..4d3f4da856 --- /dev/null +++ b/src/main/migrations/hotbar-store/migrations.injectable.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import joinMigrationsInjectable from "../join.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { hotbarStoreMigrationsInjectionToken } from "../../../common/hotbars/migrations"; +import { hotbarStoreMigrationDeclarationInjectionToken } from "./migration"; + +const hotbarStoreMigrationsInjectable = getInjectable({ + id: "hotbar-store-migrations", + instantiate: (di) => { + const joinMigrations = di.inject(joinMigrationsInjectable); + const migrationDeclarations = di.injectMany(hotbarStoreMigrationDeclarationInjectionToken); + + return joinMigrations(migrationDeclarations); + }, + injectionToken: hotbarStoreMigrationsInjectionToken, +}); + +export default hotbarStoreMigrationsInjectable; diff --git a/src/main/migrations/join.injectable.ts b/src/main/migrations/join.injectable.ts new file mode 100644 index 0000000000..f1794fcb92 --- /dev/null +++ b/src/main/migrations/join.injectable.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { Migrations } from "conf/dist/source/types"; +import { getOrInsert, iter } from "../../common/utils"; +import type { MigrationDeclaration } from "./declaration"; +import { getInjectable } from "@ogre-tools/injectable"; +import migrationLogInjectable from "./log.injectable"; + +const joinMigrationsInjectable = getInjectable({ + id: "join-migrations", + instantiate: (di) => { + const migrationLog = di.inject(migrationLogInjectable); + + return (declarations: MigrationDeclaration[]): Migrations => { + const migrations = new Map(); + + for (const decl of declarations) { + getOrInsert(migrations, decl.version, []).push(decl.run); + } + + return Object.fromEntries( + iter.map( + migrations, + ([v, fns]) => [v, (store) => { + migrationLog(`Running ${v} migration for ${store.path}`); + + for (const fn of fns) { + fn(store); + } + }], + ), + ); + }; + }, +}); + +export default joinMigrationsInjectable; + diff --git a/src/main/migrations/log.injectable.ts b/src/main/migrations/log.injectable.ts new file mode 100644 index 0000000000..c89a91f6ce --- /dev/null +++ b/src/main/migrations/log.injectable.ts @@ -0,0 +1,19 @@ +/** + * 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 loggerInjectable from "../../common/logger.injectable"; + +export type MigrationLog = (message: string, ...args: any[]) => void; + +const migrationLogInjectable = getInjectable({ + id: "migration-log", + instantiate: (di): MigrationLog => { + const logger = di.inject(loggerInjectable); + + return (...args) => logger.info(...args); + }, +}); + +export default migrationLogInjectable; diff --git a/src/main/migrations/user-store/2.1.0-beta.4.migration.ts b/src/main/migrations/user-store/2.1.0-beta.4.migration.ts new file mode 100644 index 0000000000..c9991ee5a8 --- /dev/null +++ b/src/main/migrations/user-store/2.1.0-beta.4.migration.ts @@ -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 { userStoreMigrationDeclarationInjectionToken } from "./migration"; + +const userStoreV210Beta4MigrationInjectable = getInjectable({ + id: "user-store-v2.1.0-beta.4-migration", + instantiate: () => ({ + version: "2.1.0-beta.4", + run(store) { + store.set("lastSeenAppVersion", "0.0.0"); + }, + }), + injectionToken: userStoreMigrationDeclarationInjectionToken, +}); + +export default userStoreV210Beta4MigrationInjectable; + diff --git a/src/main/migrations/user-store/5.0.0-alpha.3.injectable.ts b/src/main/migrations/user-store/5.0.0-alpha.3.injectable.ts new file mode 100644 index 0000000000..36b6130aac --- /dev/null +++ b/src/main/migrations/user-store/5.0.0-alpha.3.injectable.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { hasTypedProperty, isObject } from "../../../common/utils"; +import { getInjectable } from "@ogre-tools/injectable"; +import { userStoreMigrationDeclarationInjectionToken } from "./migration"; + +const userStoreV500Alpha3MigrationInjectable = getInjectable({ + id: "user-store-v5.0.0-alpha.3-migration", + instantiate: () => ({ + version: "5.0.0-alpha.3", + run(store) { + const preferences = store.get("preferences"); + + if (!isObject(preferences)) { + store.delete("preferences"); + } else if (hasTypedProperty(preferences, "hiddenTableColumns", isObject)) { + preferences.hiddenTableColumns = Object.entries(preferences.hiddenTableColumns); + + store.set("preferences", preferences); + } + }, + }), + injectionToken: userStoreMigrationDeclarationInjectionToken, +}); + +export default userStoreV500Alpha3MigrationInjectable; + diff --git a/src/main/migrations/user-store/5.0.3-beta.1.injectable.ts b/src/main/migrations/user-store/5.0.3-beta.1.injectable.ts new file mode 100644 index 0000000000..80cd88ceb4 --- /dev/null +++ b/src/main/migrations/user-store/5.0.3-beta.1.injectable.ts @@ -0,0 +1,101 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { existsSync } from "fs"; +import path from "path"; +import os from "os"; +import type { ClusterStoreModel } from "../../../common/cluster-store/cluster-store"; +import type { KubeconfigSyncEntry } from "../../../common/user-store"; +import { hasTypedProperty, isErrnoException, isLogicalChildPath } from "../../../common/utils"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import migrationLogInjectable from "../log.injectable"; +import { userStoreMigrationDeclarationInjectionToken } from "./migration"; +import { isObject } from "lodash"; +import readFileSyncInjectable from "../../../common/fs/read-file-sync.injectable"; +import joinPathsInjectable from "../../../common/path/join-paths.injectable"; + +const userStoreV503Beta1MigrationInjectable = getInjectable({ + id: "user-store-v5.0.3-beta.1-migration", + instantiate: (di) => { + const migrationLog = di.inject(migrationLogInjectable); + const userDataPath = di.inject(directoryForUserDataInjectable); + const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable); + const readFileSync = di.inject(readFileSyncInjectable); + const joinPaths = di.inject(joinPathsInjectable); + + return { + version: "5.0.3-beta.1", + run(store) { + try { + const preferences = store.get("preferences"); + + if (!isObject(preferences)) { + store.delete("preferences"); + + return; + } + + if (!hasTypedProperty(preferences, "syncKubeconfigEntries", Array.isArray)) { + return; + } + + const { syncKubeconfigEntries } = preferences; + + const { clusters = [] }: ClusterStoreModel = JSON.parse(readFileSync(joinPaths(userDataPath, "lens-cluster-store.json"))) ?? {}; + const extensionDataDir = joinPaths(userDataPath, "extension_data"); + const syncPaths = new Set(syncKubeconfigEntries.map(s => s.filePath)); + + syncPaths.add(path.join(os.homedir(), ".kube")); + + for (const cluster of clusters) { + if (!cluster.kubeConfigPath) { + continue; + } + const dirOfKubeconfig = path.dirname(cluster.kubeConfigPath); + + if (dirOfKubeconfig === kubeConfigsPath) { + migrationLog(`Skipping ${cluster.id} because kubeConfigPath is under the stored KubeConfig folder`); + continue; + } + + if (syncPaths.has(cluster.kubeConfigPath) || syncPaths.has(dirOfKubeconfig)) { + migrationLog(`Skipping ${cluster.id} because kubeConfigPath is already being synced`); + continue; + } + + if (isLogicalChildPath(extensionDataDir, cluster.kubeConfigPath)) { + migrationLog(`Skipping ${cluster.id} because kubeConfigPath is placed under an extension_data folder`); + continue; + } + + if (!existsSync(cluster.kubeConfigPath)) { + migrationLog(`Skipping ${cluster.id} because kubeConfigPath no longer exists`); + continue; + } + + migrationLog(`Adding ${cluster.kubeConfigPath} from ${cluster.id} to sync paths`); + syncPaths.add(cluster.kubeConfigPath); + } + + const updatedSyncEntries: KubeconfigSyncEntry[] = [...syncPaths].map(filePath => ({ filePath })); + + migrationLog("Final list of synced paths", updatedSyncEntries); + store.set("preferences", { ...preferences, syncKubeconfigEntries: updatedSyncEntries }); + } catch (error) { + if (isErrnoException(error) && error.code !== "ENOENT") { + // ignore files being missing + throw error; + } + } + }, + }; + }, + injectionToken: userStoreMigrationDeclarationInjectionToken, +}); + +export default userStoreV503Beta1MigrationInjectable; + diff --git a/src/common/user-store/file-name-migration.injectable.ts b/src/main/migrations/user-store/file-name-migration.injectable.ts similarity index 75% rename from src/common/user-store/file-name-migration.injectable.ts rename to src/main/migrations/user-store/file-name-migration.injectable.ts index 8f2dcf1e23..91d8fe45d2 100644 --- a/src/common/user-store/file-name-migration.injectable.ts +++ b/src/main/migrations/user-store/file-name-migration.injectable.ts @@ -5,13 +5,14 @@ import fse from "fs-extra"; import path from "path"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { isErrnoException } from "../utils"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import { isErrnoException } from "../../../common/utils"; import { getInjectable } from "@ogre-tools/injectable"; +import { userStorePreMigrationsInjectionToken } from "../../../common/user-store/migrations"; const userStoreFileNameMigrationInjectable = getInjectable({ id: "user-store-file-name-migration", - instantiate: (di) => { + instantiate: (di) => () => { const userDataPath = di.inject(directoryForUserDataInjectable); const configJsonPath = path.join(userDataPath, "config.json"); const lensUserStoreJsonPath = path.join(userDataPath, "lens-user-store.json"); @@ -30,6 +31,7 @@ const userStoreFileNameMigrationInjectable = getInjectable({ } } }, + injectionToken: userStorePreMigrationsInjectionToken, }); export default userStoreFileNameMigrationInjectable; diff --git a/src/main/migrations/user-store/migration.ts b/src/main/migrations/user-store/migration.ts new file mode 100644 index 0000000000..f9237027ef --- /dev/null +++ b/src/main/migrations/user-store/migration.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { MigrationDeclaration } from "../declaration"; + +export const userStoreMigrationDeclarationInjectionToken = getInjectionToken({ + id: "user-store-migration-declaration-token", +}); diff --git a/src/main/migrations/user-store/migrations.injectable.ts b/src/main/migrations/user-store/migrations.injectable.ts new file mode 100644 index 0000000000..ad48c89590 --- /dev/null +++ b/src/main/migrations/user-store/migrations.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import joinMigrationsInjectable from "../join.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { userStoreMigrationDeclarationInjectionToken } from "./migration"; +import { userStoreMigrationsInjectionToken } from "../../../common/user-store/migrations"; + +const userStoreMigrationsInjectable = getInjectable({ + id: "user-store-migrations", + instantiate: (di) => { + const joinMigrations = di.inject(joinMigrationsInjectable); + const migrationDeclarataions = di.injectMany(userStoreMigrationDeclarationInjectionToken); + + return joinMigrations(migrationDeclarataions); + }, + injectionToken: userStoreMigrationsInjectionToken, +}); + +export default userStoreMigrationsInjectable; + diff --git a/src/migrations/utils.ts b/src/main/migrations/utils.ts similarity index 100% rename from src/migrations/utils.ts rename to src/main/migrations/utils.ts diff --git a/src/main/migrations/weblinks-store/5.1.4.injectable.ts b/src/main/migrations/weblinks-store/5.1.4.injectable.ts new file mode 100644 index 0000000000..e99f806cd6 --- /dev/null +++ b/src/main/migrations/weblinks-store/5.1.4.injectable.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { docsUrl, slackUrl } from "../../../common/vars"; +import type { WeblinkData } from "../../../common/weblinks/store"; +import { getInjectable } from "@ogre-tools/injectable"; +import { weblinksStoreMigrationDeclarationInjectionToken } from "./migration"; + +export const lensWebsiteLinkName = "Lens Website"; +export const lensDocumentationWeblinkName = "Lens Documentation"; +export const lensSlackWeblinkName = "Lens Community Slack"; +export const lensTwitterWeblinkName = "Lens on Twitter"; +export const lensBlogWeblinkName = "Lens Official Blog"; +export const kubernetesDocumentationWeblinkName = "Kubernetes Documentation"; + +const weblinksStoreV514MigrationInjectable = getInjectable({ + id: "weblinks-store-v5.1.4-migration", + instantiate: () => ({ + version: "5.1.4", + run(store) { + const weblinksRaw = store.get("weblinks"); + const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; + + weblinks.push( + { id: "https://k8slens.dev", name: lensWebsiteLinkName, url: "https://k8slens.dev" }, + { id: docsUrl, name: lensDocumentationWeblinkName, url: docsUrl }, + { id: slackUrl, name: lensSlackWeblinkName, url: slackUrl }, + { id: "https://twitter.com/k8slens", name: lensTwitterWeblinkName, url: "https://twitter.com/k8slens" }, + { id: "https://medium.com/k8slens", name: lensBlogWeblinkName, url: "https://medium.com/k8slens" }, + { id: "https://kubernetes.io/docs/home/", name: kubernetesDocumentationWeblinkName, url: "https://kubernetes.io/docs/home/" }, + ); + + store.set("weblinks", weblinks); + }, + }), + injectionToken: weblinksStoreMigrationDeclarationInjectionToken, +}); + +export default weblinksStoreV514MigrationInjectable; + diff --git a/src/main/migrations/weblinks-store/5.4.5-beta.1.injectable.ts b/src/main/migrations/weblinks-store/5.4.5-beta.1.injectable.ts new file mode 100644 index 0000000000..8150ec2db3 --- /dev/null +++ b/src/main/migrations/weblinks-store/5.4.5-beta.1.injectable.ts @@ -0,0 +1,64 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { kubernetesDocumentationWeblinkId, lensBlogWeblinkId, lensDocumentationWeblinkId, lensSlackWeblinkId, lensTwitterWeblinkId, lensWebsiteWeblinkId } from "../../../common/vars"; +import type { WeblinkData } from "../../../common/weblinks/store"; +import { kubernetesDocumentationWeblinkName, lensBlogWeblinkName, lensDocumentationWeblinkName, lensSlackWeblinkName, lensTwitterWeblinkName, lensWebsiteLinkName } from "./5.1.4.injectable"; + +import { getInjectable } from "@ogre-tools/injectable"; +import { weblinksStoreMigrationDeclarationInjectionToken } from "./migration"; + +const weblinksStoreV545Beta1MigrationInjectable = getInjectable({ + id: "weblinks-store-v5.4.5-beta.1-migration", + instantiate: () => ({ + version: "5.4.5-beta.1 || >=5.5.0-alpha.0", + run(store) { + const weblinksRaw = store.get("weblinks"); + const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; + + const lensWebsiteLink = weblinks.find(weblink => weblink.name === lensWebsiteLinkName); + + if (lensWebsiteLink) { + lensWebsiteLink.id = lensWebsiteWeblinkId; + } + + const lensDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === lensDocumentationWeblinkName); + + if (lensDocumentationWeblinkLink) { + lensDocumentationWeblinkLink.id = lensDocumentationWeblinkId; + } + + const lensSlackWeblinkLink = weblinks.find(weblink => weblink.name === lensSlackWeblinkName); + + if (lensSlackWeblinkLink) { + lensSlackWeblinkLink.id = lensSlackWeblinkId; + } + + const lensTwitterWeblinkLink = weblinks.find(weblink => weblink.name === lensTwitterWeblinkName); + + if (lensTwitterWeblinkLink) { + lensTwitterWeblinkLink.id = lensTwitterWeblinkId; + } + + const lensBlogWeblinkLink = weblinks.find(weblink => weblink.name === lensBlogWeblinkName); + + if (lensBlogWeblinkLink) { + lensBlogWeblinkLink.id = lensBlogWeblinkId; + } + + const kubernetesDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === kubernetesDocumentationWeblinkName); + + if (kubernetesDocumentationWeblinkLink) { + kubernetesDocumentationWeblinkLink.id = kubernetesDocumentationWeblinkId; + } + + store.set("weblinks", weblinks); + }, + }), + injectionToken: weblinksStoreMigrationDeclarationInjectionToken, +}); + +export default weblinksStoreV545Beta1MigrationInjectable; + diff --git a/src/main/migrations/weblinks-store/currentVersion.injectable.ts b/src/main/migrations/weblinks-store/currentVersion.injectable.ts new file mode 100644 index 0000000000..98203e9de2 --- /dev/null +++ b/src/main/migrations/weblinks-store/currentVersion.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { lensSlackWeblinkId, slackUrl } from "../../../common/vars"; +import type { WeblinkData } from "../../../common/weblinks/store"; +import { getInjectable } from "@ogre-tools/injectable"; +import { weblinksStoreMigrationDeclarationInjectionToken } from "./migration"; +import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable"; + +const weblinksStoreCurrentVersionMigrationInjectable = getInjectable({ + id: "weblinks-store-current-version-migration", + instantiate: (di) => ({ + version: di.inject(appVersionInjectable), // Run always after upgrade + run(store) { + const weblinksRaw = store.get("weblinks"); + const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; + const slackWeblink = weblinks.find(weblink => weblink.id === lensSlackWeblinkId); + + if (slackWeblink) { + slackWeblink.url = slackUrl; + } + + store.set("weblinks", weblinks); + }, + }), + injectionToken: weblinksStoreMigrationDeclarationInjectionToken, +}); + +export default weblinksStoreCurrentVersionMigrationInjectable; + diff --git a/src/main/migrations/weblinks-store/migration.ts b/src/main/migrations/weblinks-store/migration.ts new file mode 100644 index 0000000000..4f7bb54032 --- /dev/null +++ b/src/main/migrations/weblinks-store/migration.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { MigrationDeclaration } from "../declaration"; + +export const weblinksStoreMigrationDeclarationInjectionToken = getInjectionToken({ + id: "weblinks-store-migration-declaration-token", +}); diff --git a/src/main/migrations/weblinks-store/migrations.injectable.ts b/src/main/migrations/weblinks-store/migrations.injectable.ts new file mode 100644 index 0000000000..85b63b946f --- /dev/null +++ b/src/main/migrations/weblinks-store/migrations.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import joinMigrationsInjectable from "../join.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { weblinksStoreMigrationDeclarationInjectionToken } from "./migration"; +import { weblinksStoreMigrationsInjectionToken } from "../../../common/weblinks/migrations"; + +const weblinksStoreMigrationsInjectable = getInjectable({ + id: "weblinks-store-migrations", + instantiate: (di) => { + const joinMigrations = di.inject(joinMigrationsInjectable); + const migrationDeclarations = di.injectMany(weblinksStoreMigrationDeclarationInjectionToken); + + return joinMigrations(migrationDeclarations); + }, + injectionToken: weblinksStoreMigrationsInjectionToken, +}); + +export default weblinksStoreMigrationsInjectable; + diff --git a/src/migrations/cluster-store/3.6.0-beta.1.ts b/src/migrations/cluster-store/3.6.0-beta.1.ts deleted file mode 100644 index 09dbd1fd61..0000000000 --- a/src/migrations/cluster-store/3.6.0-beta.1.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Move embedded kubeconfig into separate file and add reference to it to cluster settings -// convert file path cluster icons to their base64 encoded versions - -import path from "path"; -import fse from "fs-extra"; -import { loadConfigFromFileSync } from "../../common/kube-helpers"; -import type { MigrationDeclaration } from "../helpers"; -import { migrationLog } from "../helpers"; -import type { ClusterModel } from "../../common/cluster-types"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable - from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable - from "../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import getCustomKubeConfigDirectoryInjectable - from "../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; - -interface Pre360ClusterModel extends ClusterModel { - kubeConfig?: string; -} - -export default { - version: "3.6.0-beta.1", - run(store) { - const di = getLegacyGlobalDiForExtensionApi(); - - const userDataPath = di.inject(directoryForUserDataInjectable); - const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable); - const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable); - - const storedClusters: Pre360ClusterModel[] = store.get("clusters") ?? []; - const migratedClusters: ClusterModel[] = []; - - fse.ensureDirSync(kubeConfigsPath); - - migrationLog("Number of clusters to migrate: ", storedClusters.length); - - for (const clusterModel of storedClusters) { - /** - * migrate kubeconfig - */ - try { - const absPath = getCustomKubeConfigDirectory(clusterModel.id); - - if (!clusterModel.kubeConfig) { - continue; - } - - // take the embedded kubeconfig and dump it into a file - fse.writeFileSync(absPath, clusterModel.kubeConfig, { encoding: "utf-8", mode: 0o600 }); - - clusterModel.kubeConfigPath = absPath; - clusterModel.contextName = loadConfigFromFileSync(clusterModel.kubeConfigPath).config.getCurrentContext(); - delete clusterModel.kubeConfig; - - } catch (error) { - migrationLog(`Failed to migrate Kubeconfig for cluster "${clusterModel.id}", removing clusterModel...`, error); - - continue; - } - - /** - * migrate cluster icon - */ - try { - if (clusterModel.preferences?.icon) { - migrationLog(`migrating ${clusterModel.preferences.icon} for ${clusterModel.preferences.clusterName}`); - const iconPath = clusterModel.preferences.icon.replace("store://", ""); - const fileData = fse.readFileSync(path.join(userDataPath, iconPath)); - - clusterModel.preferences.icon = `data:;base64,${fileData.toString("base64")}`; - } else { - delete clusterModel.preferences?.icon; - } - } catch (error) { - migrationLog(`Failed to migrate cluster icon for cluster "${clusterModel.id}"`, error); - delete clusterModel.preferences?.icon; - } - - migratedClusters.push(clusterModel); - } - - store.set("clusters", migratedClusters); - }, -} as MigrationDeclaration; diff --git a/src/migrations/cluster-store/5.0.0-beta.10.ts b/src/migrations/cluster-store/5.0.0-beta.10.ts deleted file mode 100644 index 9172969a85..0000000000 --- a/src/migrations/cluster-store/5.0.0-beta.10.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import fse from "fs-extra"; -import type { ClusterModel } from "../../common/cluster-types"; -import type { MigrationDeclaration } from "../helpers"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { isErrnoException } from "../../common/utils"; - -interface Pre500WorkspaceStoreModel { - workspaces: { - id: string; - name: string; - }[]; -} - -export default { - version: "5.0.0-beta.10", - run(store) { - const di = getLegacyGlobalDiForExtensionApi(); - - const userDataPath = di.inject(directoryForUserDataInjectable); - - try { - const workspaceData: Pre500WorkspaceStoreModel = fse.readJsonSync(path.join(userDataPath, "lens-workspace-store.json")); - const workspaces = new Map(); // mapping from WorkspaceId to name - - for (const { id, name } of workspaceData.workspaces) { - workspaces.set(id, name); - } - - const clusters: ClusterModel[] = store.get("clusters") ?? []; - - for (const cluster of clusters) { - if (cluster.workspace) { - const workspace = workspaces.get(cluster.workspace); - - if (workspace) { - (cluster.labels ??= {}).workspace = workspace; - } - } - } - - store.set("clusters", clusters); - } catch (error) { - if (isErrnoException(error) && !(error.code === "ENOENT" && error.path?.endsWith("lens-workspace-store.json"))) { - // ignore lens-workspace-store.json being missing - throw error; - } - } - }, -} as MigrationDeclaration; diff --git a/src/migrations/cluster-store/5.0.0-beta.13.ts b/src/migrations/cluster-store/5.0.0-beta.13.ts deleted file mode 100644 index dc85a04718..0000000000 --- a/src/migrations/cluster-store/5.0.0-beta.13.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../../common/cluster-types"; -import type { MigrationDeclaration } from "../helpers"; -import { migrationLog } from "../helpers"; -import { generateNewIdFor } from "../utils"; -import path from "path"; -import { moveSync, removeSync } from "fs-extra"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable - from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { isDefined } from "../../common/utils"; - -function mergePrometheusPreferences(left: ClusterPrometheusPreferences, right: ClusterPrometheusPreferences): ClusterPrometheusPreferences { - if (left.prometheus && left.prometheusProvider) { - return { - prometheus: left.prometheus, - prometheusProvider: left.prometheusProvider, - }; - } - - if (right.prometheus && right.prometheusProvider) { - return { - prometheus: right.prometheus, - prometheusProvider: right.prometheusProvider, - }; - } - - return {}; -} - -function mergePreferences(left: ClusterPreferences, right: ClusterPreferences): ClusterPreferences { - return { - terminalCWD: left.terminalCWD || right.terminalCWD || undefined, - clusterName: left.clusterName || right.clusterName || undefined, - iconOrder: left.iconOrder || right.iconOrder || undefined, - icon: left.icon || right.icon || undefined, - httpsProxy: left.httpsProxy || right.httpsProxy || undefined, - hiddenMetrics: mergeSet(left.hiddenMetrics ?? [], right.hiddenMetrics ?? []), - ...mergePrometheusPreferences(left, right), - }; -} - -function mergeLabels(left: Record, right: Record): Record { - return { - ...right, - ...left, - }; -} - -function mergeSet(...iterables: Iterable[]): string[] { - const res = new Set(); - - for (const iterable of iterables) { - for (const val of iterable) { - if (val) { - res.add(val); - } - } - } - - return [...res]; -} - -function mergeClusterModel(prev: ClusterModel, right: Omit): ClusterModel { - return { - id: prev.id, - kubeConfigPath: prev.kubeConfigPath, - contextName: prev.contextName, - preferences: mergePreferences(prev.preferences ?? {}, right.preferences ?? {}), - metadata: prev.metadata, - labels: mergeLabels(prev.labels ?? {}, right.labels ?? {}), - accessibleNamespaces: mergeSet(prev.accessibleNamespaces ?? [], right.accessibleNamespaces ?? []), - workspace: prev.workspace || right.workspace, - workspaces: mergeSet([prev.workspace, right.workspace], prev.workspaces ?? [], right.workspaces ?? []), - }; -} - -function moveStorageFolder({ folder, newId, oldId }: { folder: string; newId: string; oldId: string }): void { - const oldPath = path.resolve(folder, `${oldId}.json`); - const newPath = path.resolve(folder, `${newId}.json`); - - try { - moveSync(oldPath, newPath); - } catch (error) { - if (String(error).includes("dest already exists")) { - migrationLog(`Multiple old lens-local-storage files for newId=${newId}. Removing ${oldId}.json`); - removeSync(oldPath); - } - } -} - -export default { - version: "5.0.0-beta.13", - run(store) { - const di = getLegacyGlobalDiForExtensionApi(); - - const userDataPath = di.inject(directoryForUserDataInjectable); - - const folder = path.resolve(userDataPath, "lens-local-storage"); - - const oldClusters: ClusterModel[] = store.get("clusters") ?? []; - const clusters = new Map(); - - for (const { id: oldId, ...cluster } of oldClusters) { - const newId = generateNewIdFor(cluster); - const newCluster = clusters.get(newId); - - if (newCluster) { - migrationLog(`Duplicate entries for ${newId}`, { oldId }); - clusters.set(newId, mergeClusterModel(newCluster, cluster)); - } else { - migrationLog(`First entry for ${newId}`, { oldId }); - clusters.set(newId, { - ...cluster, - id: newId, - workspaces: [cluster.workspace].filter(isDefined), - }); - moveStorageFolder({ folder, newId, oldId }); - } - } - - store.set("clusters", [...clusters.values()]); - }, -} as MigrationDeclaration; diff --git a/src/migrations/cluster-store/index.ts b/src/migrations/cluster-store/index.ts deleted file mode 100644 index 4851d01cae..0000000000 --- a/src/migrations/cluster-store/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Cluster store migrations - -import { joinMigrations } from "../helpers"; - -import version360Beta1 from "./3.6.0-beta.1"; -import version500Beta10 from "./5.0.0-beta.10"; -import version500Beta13 from "./5.0.0-beta.13"; -import snap from "./snap"; - -export default joinMigrations( - version360Beta1, - version500Beta10, - version500Beta13, - snap, -); diff --git a/src/migrations/cluster-store/snap.ts b/src/migrations/cluster-store/snap.ts deleted file mode 100644 index 9128dca82a..0000000000 --- a/src/migrations/cluster-store/snap.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// 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"; - -export default { - version: getAppVersion(), // Run always after upgrade - run(store) { - if (!process.env["SNAP"]) return; - - migrationLog("Migrating embedded kubeconfig paths"); - const storedClusters: ClusterModel[] = store.get("clusters") || []; - - if (!storedClusters.length) return; - - migrationLog("Number of clusters to migrate: ", storedClusters.length); - const migratedClusters = storedClusters - .map(cluster => { - /** - * replace snap version with 'current' in kubeconfig path - */ - if (!fs.existsSync(cluster.kubeConfigPath)) { - const kubeconfigPath = cluster.kubeConfigPath.replace(/\/snap\/kontena-lens\/[0-9]*\//, "/snap/kontena-lens/current/"); - - cluster.kubeConfigPath = kubeconfigPath; - } - - return cluster; - }); - - - store.set("clusters", migratedClusters); - }, -} as MigrationDeclaration; diff --git a/src/migrations/helpers.ts b/src/migrations/helpers.ts deleted file mode 100644 index 6b35a86004..0000000000 --- a/src/migrations/helpers.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type Conf from "conf"; -import type { Migrations } from "conf/dist/source/types"; -import { getOrInsert, iter } from "../common/utils"; -import { isTestEnv } from "../common/vars"; - -export function migrationLog(...args: any[]) { - if (!isTestEnv) { - console.log(...args); - } -} - -export interface MigrationDeclaration { - version: string; - run(store: Conf): void; -} - -export function joinMigrations(...declarations: MigrationDeclaration[]): Migrations { - const migrations = new Map(); - - for (const decl of declarations) { - getOrInsert(migrations, decl.version, []).push(decl.run); - } - - return Object.fromEntries( - iter.map( - migrations, - ([v, fns]) => [v, (store: Conf) => { - migrationLog(`Running ${v} migration for ${store.path}`); - - for (const fn of fns) { - fn(store); - } - }], - ), - ); -} diff --git a/src/migrations/hotbar-store/5.0.0-alpha.0.ts b/src/migrations/hotbar-store/5.0.0-alpha.0.ts deleted file mode 100644 index c8f99dc4f6..0000000000 --- a/src/migrations/hotbar-store/5.0.0-alpha.0.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Cleans up a store that had the state related data stored -import type { MigrationDeclaration } from "../helpers"; -import { getEmptyHotbar } from "../../common/hotbars/types"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import catalogCatalogEntityInjectable from "../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; - -export default { - version: "5.0.0-alpha.0", - run(store) { - const hotbar = getEmptyHotbar("default"); - const di = getLegacyGlobalDiForExtensionApi(); - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - - const { metadata: { uid, name, source }} = catalogCatalogEntity; - - hotbar.items[0] = { entity: { uid, name, source }}; - - store.set("hotbars", [hotbar]); - }, -} as MigrationDeclaration; diff --git a/src/migrations/hotbar-store/5.0.0-alpha.2.ts b/src/migrations/hotbar-store/5.0.0-alpha.2.ts deleted file mode 100644 index 0885caa1c1..0000000000 --- a/src/migrations/hotbar-store/5.0.0-alpha.2.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Cleans up a store that had the state related data stored -import type { Hotbar } from "../../common/hotbars/types"; -import * as uuid from "uuid"; -import type { MigrationDeclaration } from "../helpers"; - -export default { - version: "5.0.0-alpha.2", - run(store) { - const rawHotbars = store.get("hotbars"); - const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : []; - - store.set("hotbars", hotbars.map(({ id, ...rest }) => ({ - id: id || uuid.v4(), - ...rest, - }))); - }, -} as MigrationDeclaration; diff --git a/src/migrations/hotbar-store/5.0.0-beta.10.ts b/src/migrations/hotbar-store/5.0.0-beta.10.ts deleted file mode 100644 index 95a4c616d4..0000000000 --- a/src/migrations/hotbar-store/5.0.0-beta.10.ts +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import fse from "fs-extra"; -import { isNull } from "lodash"; -import path from "path"; -import * as uuid from "uuid"; -import type { ClusterStoreModel } from "../../common/cluster-store/cluster-store"; -import type { Hotbar, HotbarItem } from "../../common/hotbars/types"; -import { defaultHotbarCells, getEmptyHotbar } from "../../common/hotbars/types"; -import type { MigrationDeclaration } from "../helpers"; -import { migrationLog } from "../helpers"; -import { generateNewIdFor } from "../utils"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import catalogCatalogEntityInjectable from "../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; -import { isDefined, isErrnoException } from "../../common/utils"; - -interface Pre500WorkspaceStoreModel { - workspaces: { - id: string; - name: string; - }[]; -} - -interface PartialHotbar { - id: string; - name: string; - items: (null | HotbarItem)[]; -} - -export default { - version: "5.0.0-beta.10", - run(store) { - const rawHotbars = store.get("hotbars"); - const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars.filter(h => h && typeof h === "object") : []; - - const di = getLegacyGlobalDiForExtensionApi(); - - const userDataPath = di.inject(directoryForUserDataInjectable); - - // Hotbars might be empty, if some of the previous migrations weren't run - if (hotbars.length === 0) { - const hotbar = getEmptyHotbar("default"); - - const di = getLegacyGlobalDiForExtensionApi(); - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - - const { metadata: { uid, name, source }} = catalogCatalogEntity; - - hotbar.items[0] = { entity: { uid, name, source }}; - - hotbars.push(hotbar); - } - - try { - const workspaceStoreData: Pre500WorkspaceStoreModel = fse.readJsonSync(path.join(userDataPath, "lens-workspace-store.json")); - const { clusters = [] }: ClusterStoreModel = fse.readJSONSync(path.join(userDataPath, "lens-cluster-store.json")); - const workspaceHotbars = new Map(); // mapping from WorkspaceId to HotBar - - for (const { id, name } of workspaceStoreData.workspaces) { - migrationLog(`Creating new hotbar for ${name}`); - workspaceHotbars.set(id, { - id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs - items: [], - name: `Workspace: ${name}`, - }); - } - - { - // grab the default named hotbar or the first. - const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default")); - const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1); - - workspaceHotbars.set("default", { - name, - id, - items: items.filter(isDefined), - }); - } - - for (const cluster of clusters) { - const uid = generateNewIdFor(cluster); - - for (const workspaceId of cluster.workspaces ?? [cluster.workspace].filter(isDefined)) { - const workspaceHotbar = workspaceHotbars.get(workspaceId); - - if (!workspaceHotbar) { - migrationLog(`Cluster ${uid} has unknown workspace ID, skipping`); - continue; - } - - migrationLog(`Adding cluster ${uid} to ${workspaceHotbar.name}`); - - if (workspaceHotbar?.items.length < defaultHotbarCells) { - workspaceHotbar.items.push({ - entity: { - uid: generateNewIdFor(cluster), - name: cluster.preferences?.clusterName || cluster.contextName, - }, - }); - } - } - } - - for (const hotbar of workspaceHotbars.values()) { - if (hotbar.items.length === 0) { - migrationLog(`Skipping ${hotbar.name} due to it being empty`); - continue; - } - - while (hotbar.items.length < defaultHotbarCells) { - hotbar.items.push(null); - } - - hotbars.push(hotbar as Hotbar); - } - - /** - * Finally, make sure that the catalog entity hotbar item is in place. - * Just in case something else removed it. - * - * if every hotbar has elements that all not the `catalog-entity` item - */ - if (hotbars.every(hotbar => hotbar.items.every(item => item?.entity?.uid !== "catalog-entity"))) { - // note, we will add a new whole hotbar here called "default" if that was previously removed - const di = getLegacyGlobalDiForExtensionApi(); - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - - const defaultHotbar = hotbars.find(hotbar => hotbar.name === "default"); - const { metadata: { uid, name, source }} = catalogCatalogEntity; - - if (defaultHotbar) { - const freeIndex = defaultHotbar.items.findIndex(isNull); - - if (freeIndex === -1) { - // making a new hotbar is less destructive if the first hotbar - // called "default" is full than overriding a hotbar item - const hotbar = getEmptyHotbar("initial"); - - hotbar.items[0] = { entity: { uid, name, source }}; - hotbars.unshift(hotbar); - } else { - defaultHotbar.items[freeIndex] = { entity: { uid, name, source }}; - } - } else { - const hotbar = getEmptyHotbar("default"); - - hotbar.items[0] = { entity: { uid, name, source }}; - hotbars.unshift(hotbar); - } - } - - } catch (error) { - // ignore files being missing - if (isErrnoException(error) && error.code !== "ENOENT") { - throw error; - } - } - - store.set("hotbars", hotbars); - }, -} as MigrationDeclaration; diff --git a/src/migrations/hotbar-store/5.0.0-beta.5.ts b/src/migrations/hotbar-store/5.0.0-beta.5.ts deleted file mode 100644 index 39f8c054db..0000000000 --- a/src/migrations/hotbar-store/5.0.0-beta.5.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Hotbar } from "../../common/hotbars/types"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable"; -import type { MigrationDeclaration } from "../helpers"; - -export default { - version: "5.0.0-beta.5", - run(store) { - const rawHotbars = store.get("hotbars"); - const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : []; - const di = getLegacyGlobalDiForExtensionApi(); - const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); - - for (const hotbar of hotbars) { - for (let i = 0; i < hotbar.items.length; i += 1) { - const item = hotbar.items[i]; - - if (!item) { - continue; - } - - const entity = catalogEntityRegistry.findById(item.entity.uid); - - if (!entity) { - // Clear disabled item - hotbar.items[i] = null; - } else { - // Save additional data - item.entity = { - ...item.entity, - name: entity.metadata.name, - source: entity.metadata.source, - }; - } - } - } - - store.set("hotbars", hotbars); - }, -} as MigrationDeclaration; diff --git a/src/migrations/hotbar-store/index.ts b/src/migrations/hotbar-store/index.ts deleted file mode 100644 index 73fd3e093f..0000000000 --- a/src/migrations/hotbar-store/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Hotbar store migrations - -import { joinMigrations } from "../helpers"; - -import version500alpha0 from "./5.0.0-alpha.0"; -import version500alpha2 from "./5.0.0-alpha.2"; -import version500beta5 from "./5.0.0-beta.5"; -import version500beta10 from "./5.0.0-beta.10"; - -export default joinMigrations( - version500alpha0, - version500alpha2, - version500beta5, - version500beta10, -); diff --git a/src/migrations/user-store/2.1.0-beta.4.ts b/src/migrations/user-store/2.1.0-beta.4.ts deleted file mode 100644 index a8083df711..0000000000 --- a/src/migrations/user-store/2.1.0-beta.4.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Add / reset "lastSeenAppVersion" -import type { MigrationDeclaration } from "../helpers"; - -export default { - version: "2.1.0-beta.4", - run(store) { - store.set("lastSeenAppVersion", "0.0.0"); - }, -} as MigrationDeclaration; diff --git a/src/migrations/user-store/5.0.0-alpha.3.ts b/src/migrations/user-store/5.0.0-alpha.3.ts deleted file mode 100644 index 1fe379c3d5..0000000000 --- a/src/migrations/user-store/5.0.0-alpha.3.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Switch representation of hiddenTableColumns in store -import type { MigrationDeclaration } from "../helpers"; - -export default { - version: "5.0.0-alpha.3", - run(store) { - const preferences = store.get("preferences"); - const oldHiddenTableColumns: Record = preferences?.hiddenTableColumns; - - if (!oldHiddenTableColumns) { - return; - } - - preferences.hiddenTableColumns = Object.entries(oldHiddenTableColumns); - - store.set("preferences", preferences); - }, -} as MigrationDeclaration; diff --git a/src/migrations/user-store/5.0.3-beta.1.ts b/src/migrations/user-store/5.0.3-beta.1.ts deleted file mode 100644 index 40f93312cf..0000000000 --- a/src/migrations/user-store/5.0.3-beta.1.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { existsSync, readFileSync } from "fs"; -import path from "path"; -import os from "os"; -import type { ClusterStoreModel } from "../../common/cluster-store/cluster-store"; -import type { KubeconfigSyncEntry, UserPreferencesModel } from "../../common/user-store"; -import type { MigrationDeclaration } from "../helpers"; -import { migrationLog } from "../helpers"; -import { isErrnoException, isLogicalChildPath } from "../../common/utils"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable - from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable - from "../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; - -export default { - version: "5.0.3-beta.1", - run(store) { - try { - const { syncKubeconfigEntries = [], ...preferences }: UserPreferencesModel = store.get("preferences") ?? {}; - - const di = getLegacyGlobalDiForExtensionApi(); - - const userDataPath = di.inject(directoryForUserDataInjectable); - const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable); - - const { clusters = [] }: ClusterStoreModel = JSON.parse(readFileSync(path.resolve(userDataPath, "lens-cluster-store.json"), "utf-8")) ?? {}; - const extensionDataDir = path.resolve(userDataPath, "extension_data"); - const syncPaths = new Set(syncKubeconfigEntries.map(s => s.filePath)); - - syncPaths.add(path.join(os.homedir(), ".kube")); - - for (const cluster of clusters) { - if (!cluster.kubeConfigPath) { - continue; - } - const dirOfKubeconfig = path.dirname(cluster.kubeConfigPath); - - if (dirOfKubeconfig === kubeConfigsPath) { - migrationLog(`Skipping ${cluster.id} because kubeConfigPath is under the stored KubeConfig folder`); - continue; - } - - if (syncPaths.has(cluster.kubeConfigPath) || syncPaths.has(dirOfKubeconfig)) { - migrationLog(`Skipping ${cluster.id} because kubeConfigPath is already being synced`); - continue; - } - - if (isLogicalChildPath(extensionDataDir, cluster.kubeConfigPath)) { - migrationLog(`Skipping ${cluster.id} because kubeConfigPath is placed under an extension_data folder`); - continue; - } - - if (!existsSync(cluster.kubeConfigPath)) { - migrationLog(`Skipping ${cluster.id} because kubeConfigPath no longer exists`); - continue; - } - - migrationLog(`Adding ${cluster.kubeConfigPath} from ${cluster.id} to sync paths`); - syncPaths.add(cluster.kubeConfigPath); - } - - const updatedSyncEntries: KubeconfigSyncEntry[] = [...syncPaths].map(filePath => ({ filePath })); - - migrationLog("Final list of synced paths", updatedSyncEntries); - store.set("preferences", { ...preferences, syncKubeconfigEntries: updatedSyncEntries }); - } catch (error) { - if (isErrnoException(error) && error.code !== "ENOENT") { - // ignore files being missing - throw error; - } - } - }, -} as MigrationDeclaration; diff --git a/src/migrations/user-store/index.ts b/src/migrations/user-store/index.ts deleted file mode 100644 index 557d367c63..0000000000 --- a/src/migrations/user-store/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// User store migrations - -import { joinMigrations } from "../helpers"; - -import version210Beta4 from "./2.1.0-beta.4"; -import version500Alpha3 from "./5.0.0-alpha.3"; -import version503Beta1 from "./5.0.3-beta.1"; - -export default joinMigrations( - version210Beta4, - version500Alpha3, - version503Beta1, -); diff --git a/src/migrations/weblinks-store/5.1.4.ts b/src/migrations/weblinks-store/5.1.4.ts deleted file mode 100644 index 59c47be8b0..0000000000 --- a/src/migrations/weblinks-store/5.1.4.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { docsUrl, slackUrl } from "../../common/vars"; -import type { WeblinkData } from "../../common/weblink-store"; -import type { MigrationDeclaration } from "../helpers"; - -export const lensWebsiteLinkName = "Lens Website"; -export const lensDocumentationWeblinkName = "Lens Documentation"; -export const lensSlackWeblinkName = "Lens Community Slack"; -export const lensTwitterWeblinkName = "Lens on Twitter"; -export const lensBlogWeblinkName = "Lens Official Blog"; -export const kubernetesDocumentationWeblinkName = "Kubernetes Documentation"; - -export default { - version: "5.1.4", - run(store) { - const weblinksRaw: any = store.get("weblinks"); - const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; - - weblinks.push( - { id: "https://k8slens.dev", name: lensWebsiteLinkName, url: "https://k8slens.dev" }, - { id: docsUrl, name: lensDocumentationWeblinkName, url: docsUrl }, - { id: slackUrl, name: lensSlackWeblinkName, url: slackUrl }, - { id: "https://twitter.com/k8slens", name: lensTwitterWeblinkName, url: "https://twitter.com/k8slens" }, - { id: "https://medium.com/k8slens", name: lensBlogWeblinkName, url: "https://medium.com/k8slens" }, - { id: "https://kubernetes.io/docs/home/", name: kubernetesDocumentationWeblinkName, url: "https://kubernetes.io/docs/home/" }, - ); - - store.set("weblinks", weblinks); - }, -} as MigrationDeclaration; diff --git a/src/migrations/weblinks-store/5.4.5-beta.1.ts b/src/migrations/weblinks-store/5.4.5-beta.1.ts deleted file mode 100644 index 57d6f5c4ba..0000000000 --- a/src/migrations/weblinks-store/5.4.5-beta.1.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { kubernetesDocumentationWeblinkId, lensBlogWeblinkId, lensDocumentationWeblinkId, lensSlackWeblinkId, lensTwitterWeblinkId, lensWebsiteWeblinkId } from "../../common/vars"; -import type { WeblinkData } from "../../common/weblink-store"; -import type { MigrationDeclaration } from "../helpers"; -import { kubernetesDocumentationWeblinkName, lensBlogWeblinkName, lensDocumentationWeblinkName, lensSlackWeblinkName, lensTwitterWeblinkName, lensWebsiteLinkName } from "./5.1.4"; - -export default { - version: "5.4.5-beta.1 || >=5.5.0-alpha.0", - run(store) { - const weblinksRaw: any = store.get("weblinks"); - const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; - - const lensWebsiteLink = weblinks.find(weblink => weblink.name === lensWebsiteLinkName); - - if (lensWebsiteLink) { - lensWebsiteLink.id = lensWebsiteWeblinkId; - } - - const lensDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === lensDocumentationWeblinkName); - - if (lensDocumentationWeblinkLink) { - lensDocumentationWeblinkLink.id = lensDocumentationWeblinkId; - } - - const lensSlackWeblinkLink = weblinks.find(weblink => weblink.name === lensSlackWeblinkName); - - if (lensSlackWeblinkLink) { - lensSlackWeblinkLink.id = lensSlackWeblinkId; - } - - const lensTwitterWeblinkLink = weblinks.find(weblink => weblink.name === lensTwitterWeblinkName); - - if (lensTwitterWeblinkLink) { - lensTwitterWeblinkLink.id = lensTwitterWeblinkId; - } - - const lensBlogWeblinkLink = weblinks.find(weblink => weblink.name === lensBlogWeblinkName); - - if (lensBlogWeblinkLink) { - lensBlogWeblinkLink.id = lensBlogWeblinkId; - } - - const kubernetesDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === kubernetesDocumentationWeblinkName); - - if (kubernetesDocumentationWeblinkLink) { - kubernetesDocumentationWeblinkLink.id = kubernetesDocumentationWeblinkId; - } - - store.set("weblinks", weblinks); - }, -} as MigrationDeclaration; diff --git a/src/migrations/weblinks-store/currentVersion.ts b/src/migrations/weblinks-store/currentVersion.ts deleted file mode 100644 index 6a319bc286..0000000000 --- a/src/migrations/weblinks-store/currentVersion.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getAppVersion } from "../../common/utils"; -import { lensSlackWeblinkId, slackUrl } from "../../common/vars"; -import type { WeblinkData } from "../../common/weblink-store"; -import type { MigrationDeclaration } from "../helpers"; - -export default { - version: getAppVersion(), // Run always after upgrade - run(store) { - const weblinksRaw: any = store.get("weblinks"); - const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; - const slackWeblink = weblinks.find(weblink => weblink.id === lensSlackWeblinkId); - - if (slackWeblink) { - slackWeblink.url = slackUrl; - } - - store.set("weblinks", weblinks); - }, -} as MigrationDeclaration; diff --git a/src/migrations/weblinks-store/index.ts b/src/migrations/weblinks-store/index.ts deleted file mode 100644 index a6e6ea1eea..0000000000 --- a/src/migrations/weblinks-store/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { joinMigrations } from "../helpers"; - -import version514 from "./5.1.4"; -import version545Beta1 from "./5.4.5-beta.1"; -import currentVersion from "./currentVersion"; - -export default joinMigrations( - version514, - version545Beta1, - currentVersion, -); diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 5573dbdbd2..5a0ce9b81f 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -19,7 +19,6 @@ import { DefaultProps } from "./mui-base-theme"; 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"; @@ -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 weblinkStoreInjectable from "../common/weblinks/store.injectable"; configurePackages(); // global packages registerCustomThemes(); // monaco editor themes @@ -141,7 +141,7 @@ export async function bootstrap(di: DiContainer) { // TODO: Remove temporal dependencies di.inject(themeStoreInjectable); - WeblinkStore.createInstance(); + di.inject(weblinkStoreInjectable); const extensionInstallationStateStore = di.inject(extensionInstallationStateStoreInjectable); diff --git a/src/renderer/components/catalog-entities/weblink-add-command.tsx b/src/renderer/components/catalog-entities/weblink-add-command.tsx index 91778f8514..dc08348bd7 100644 --- a/src/renderer/components/catalog-entities/weblink-add-command.tsx +++ b/src/renderer/components/catalog-entities/weblink-add-command.tsx @@ -7,7 +7,7 @@ import React from "react"; import { observer } from "mobx-react"; import { Input } from "../input"; import { isUrl } from "../input/input_validators"; -import { WeblinkStore } from "../../../common/weblink-store"; +import { WeblinkStore } from "../../../common/weblinks/store"; import { computed, makeObservable, observable } from "mobx"; import { withInjectables } from "@ogre-tools/injectable-react"; import commandOverlayInjectable from "../command-palette/command-overlay.injectable"; @@ -64,7 +64,7 @@ class NonInjectedWeblinkAddCommand extends React.Component { value={this.url} onChange={(v) => this.onChangeUrl(v)} onSubmit={(v) => this.onSubmitUrl(v)} - showValidationLine={true} + showValidationLine={true} /> { this.nameHidden && ( diff --git a/src/renderer/getDiForUnitTesting.tsx b/src/renderer/getDiForUnitTesting.tsx index dbae2a575d..bf6c1167a5 100644 --- a/src/renderer/getDiForUnitTesting.tsx +++ b/src/renderer/getDiForUnitTesting.tsx @@ -101,7 +101,6 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) di.override(startTopbarStateSyncInjectable, () => ({ run: () => {}, })); - di.override(terminalSpawningPoolInjectable, () => document.createElement("div")); di.override(hostedClusterIdInjectable, () => undefined); diff --git a/src/renderer/migrations/cluster-store.injectable.ts b/src/renderer/migrations/cluster-store.injectable.ts new file mode 100644 index 0000000000..bca71c1bd7 --- /dev/null +++ b/src/renderer/migrations/cluster-store.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { clusterStoreMigrationsInjectionToken } from "../../common/cluster-store/migrations"; + +const clusterStoreMigrationsInjectable = getInjectable({ + id: "cluster-store-migrations", + instantiate: () => undefined, + injectionToken: clusterStoreMigrationsInjectionToken, +}); + +export default clusterStoreMigrationsInjectable; diff --git a/src/renderer/migrations/hotbar-store.injectable.ts b/src/renderer/migrations/hotbar-store.injectable.ts new file mode 100644 index 0000000000..991c2104bc --- /dev/null +++ b/src/renderer/migrations/hotbar-store.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { hotbarStoreMigrationsInjectionToken } from "../../common/hotbars/migrations"; + +const hotbarStoreMigrationsInjectable = getInjectable({ + id: "hotbar-store-migrations", + instantiate: () => undefined, + injectionToken: hotbarStoreMigrationsInjectionToken, +}); + +export default hotbarStoreMigrationsInjectable; diff --git a/src/renderer/migrations/user-store.injectable.ts b/src/renderer/migrations/user-store.injectable.ts new file mode 100644 index 0000000000..438724745c --- /dev/null +++ b/src/renderer/migrations/user-store.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { userStoreMigrationsInjectionToken } from "../../common/user-store/migrations"; + +const userStoreMigrationsInjectable = getInjectable({ + id: "user-store-migrations", + instantiate: () => undefined, + injectionToken: userStoreMigrationsInjectionToken, +}); + +export default userStoreMigrationsInjectable; diff --git a/src/renderer/migrations/weblinks-store.injectable.ts b/src/renderer/migrations/weblinks-store.injectable.ts new file mode 100644 index 0000000000..d4e2c90777 --- /dev/null +++ b/src/renderer/migrations/weblinks-store.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { weblinksStoreMigrationsInjectionToken } from "../../common/weblinks/migrations"; + +const weblinksStoreMigrationsInjectable = getInjectable({ + id: "weblinks-store-migrations", + instantiate: () => undefined, + injectionToken: weblinksStoreMigrationsInjectionToken, +}); + +export default weblinksStoreMigrationsInjectable; diff --git a/src/renderer/utils/create-storage/create-storage.ts b/src/renderer/utils/create-storage/create-storage.ts index 501e425161..646d4e4d12 100755 --- a/src/renderer/utils/create-storage/create-storage.ts +++ b/src/renderer/utils/create-storage/create-storage.ts @@ -8,7 +8,6 @@ import { comparer, reaction, toJS, when } from "mobx"; import type { StorageLayer } from "../storageHelper"; import { StorageHelper } from "../storageHelper"; -import { isTestEnv } from "../../../common/vars"; import type { JsonObject, JsonValue } from "type-fest"; import type { Logger } from "../../../common/logger"; import type { GetAbsolutePath } from "../../../common/path/get-absolute-path.injectable"; @@ -52,10 +51,7 @@ export const createStorage = ({ } catch { // do nothing } finally { - if (!isTestEnv) { - logger.info(`${logPrefix} loading finished for ${filePath}`); - } - + logger.debug(`${logPrefix} loading finished for ${filePath}`); storage.loaded = true; }