diff --git a/src/common/user-store/migrations-token.ts b/src/common/user-store/migrations-token.ts new file mode 100644 index 0000000000..f3959beb3a --- /dev/null +++ b/src/common/user-store/migrations-token.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 "../base-store/migrations.injectable"; + +export const userStoreMigrationInjectionToken = getInjectionToken({ + id: "user-store-migration-token", +}); diff --git a/src/common/user-store/user-store.injectable.ts b/src/common/user-store/user-store.injectable.ts index 6d7fd7c722..e242a9fe91 100644 --- a/src/common/user-store/user-store.injectable.ts +++ b/src/common/user-store/user-store.injectable.ts @@ -10,6 +10,8 @@ import directoryForUserDataInjectable from "../app-paths/directory-for-user-data import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import loggerInjectable from "../logger.injectable"; import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; +import storeMigrationsInjectable from "../base-store/migrations.injectable"; +import { userStoreMigrationInjectionToken } from "./migrations-token"; const userStoreInjectable = getInjectable({ id: "user-store", @@ -21,6 +23,7 @@ const userStoreInjectable = getInjectable({ getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), logger: di.inject(loggerInjectable), storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + migrations: di.inject(storeMigrationsInjectable, userStoreMigrationInjectionToken), }), }); diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index 74f8f2a300..d781cf457a 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 { action, observable, reaction, makeObservable, isObservableArray, isObservableSet, isObservableMap } from "mobx"; import type { BaseStoreDependencies } from "../base-store/base-store"; import { BaseStore } from "../base-store/base-store"; -import migrations from "../../migrations/user-store"; import { getOrInsertSet, toggle, toJS, object } from "../../renderer/utils"; import { DESCRIPTORS } from "./preferences-helpers"; import type { UserPreferencesModel, StoreType } from "./preferences-helpers"; @@ -18,7 +17,6 @@ import type { SelectedUpdateChannel } from "../../features/application-update/co import type { ReleaseChannel } from "../../features/application-update/common/update-channels"; export interface UserStoreModel { - lastSeenAppVersion: string; preferences: UserPreferencesModel; } @@ -33,14 +31,11 @@ export class UserStore extends BaseStore /* implements UserStore constructor(protected readonly dependencies: Dependencies) { super(dependencies, { configName: "lens-user-store", - migrations, }); makeObservable(this); } - @observable lastSeenAppVersion = "0.0.0"; - /** * @deprecated No longer used */ @@ -137,12 +132,8 @@ export class UserStore extends BaseStore /* implements UserStore } @action - protected fromStore({ lastSeenAppVersion, preferences }: Partial = {}) { - this.dependencies.logger.debug("UserStore.fromStore()", { lastSeenAppVersion, preferences }); - - if (lastSeenAppVersion) { - this.lastSeenAppVersion = lastSeenAppVersion; - } + protected fromStore({ preferences }: Partial = {}) { + this.dependencies.logger.debug("UserStore.fromStore()", { preferences }); for (const [key, { fromStore }] of object.entries(DESCRIPTORS)) { const curVal = this[key]; @@ -170,11 +161,8 @@ export class UserStore extends BaseStore /* implements UserStore ) as UserPreferencesModel; return toJS({ - lastSeenAppVersion: this.lastSeenAppVersion, - preferences: { ...preferences, - updateChannel: this.dependencies.selectedUpdateChannel.value.get().id, }, }); diff --git a/src/main/user-store/migrations/5.0.0-alpha.3.injectable.ts b/src/main/user-store/migrations/5.0.0-alpha.3.injectable.ts new file mode 100644 index 0000000000..617f32ad5d --- /dev/null +++ b/src/main/user-store/migrations/5.0.0-alpha.3.injectable.ts @@ -0,0 +1,36 @@ +/** + * 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 { getInjectable } from "@ogre-tools/injectable"; +import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token"; + +interface PreV500Alpha3UserPreferencesModel { + hiddenTableColumns?: Record; +} + +const v500Alpha3UserStoreMigrationInjectable = getInjectable({ + id: "v5.0.0-alpha.3-user-store-migration", + instantiate: () => ({ + version: "5.0.0-alpha.3", + run(store) { + const preferences = (store.get("preferences") ?? {}) as PreV500Alpha3UserPreferencesModel; + const oldHiddenTableColumns = preferences.hiddenTableColumns; + + if (!oldHiddenTableColumns) { + return; + } + + store.set("preferences", { + ...preferences, + hiddenTableColumns: Object.entries(oldHiddenTableColumns), + }); + }, + }), + injectionToken: userStoreMigrationInjectionToken, +}); + +export default v500Alpha3UserStoreMigrationInjectable; + diff --git a/src/main/user-store/migrations/5.0.3-beta.1.injectable.ts b/src/main/user-store/migrations/5.0.3-beta.1.injectable.ts new file mode 100644 index 0000000000..f02971d815 --- /dev/null +++ b/src/main/user-store/migrations/5.0.3-beta.1.injectable.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { ClusterStoreModel } from "../../../common/cluster-store/cluster-store"; +import type { KubeconfigSyncEntry, UserPreferencesModel } from "../../../common/user-store"; +import { isErrnoException } 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 joinPathsInjectable from "../../../common/path/join-paths.injectable"; +import isLogicalChildPathInjectable from "../../../common/path/is-logical-child-path.injectable"; +import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token"; +import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable"; +import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable"; +import fsInjectable from "../../../common/fs/fs.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; + +const v503Beta1UserStoreMigrationInjectable = getInjectable({ + id: "v5.0.3-beta.1-user-store-migration", + instantiate: (di) => { + const userDataPath = di.inject(directoryForUserDataInjectable); + const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable); + const joinPaths = di.inject(joinPathsInjectable); + const logger = di.inject(loggerInjectable); + const isLogicalChildPath = di.inject(isLogicalChildPathInjectable); + const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); + const readJsonSync = di.inject(readJsonSyncInjectable); + const homeDirectoryPath = di.inject(homeDirectoryPathInjectable); + const { existsSync } = di.inject(fsInjectable); + + return { + version: "5.0.3-beta.1", + run(store) { + try { + const { syncKubeconfigEntries = [], ...preferences } = (store.get("preferences") ?? {}) as UserPreferencesModel; + const { clusters = [] }: ClusterStoreModel = readJsonSync(joinPaths(userDataPath, "lens-cluster-store.json"), "utf-8") ?? {}; + const extensionDataDir = joinPaths(userDataPath, "extension_data"); + const syncPaths = new Set(syncKubeconfigEntries.map(s => s.filePath)); + + syncPaths.add(joinPaths(homeDirectoryPath, ".kube")); + + for (const cluster of clusters) { + if (!cluster.kubeConfigPath) { + continue; + } + const dirOfKubeconfig = getDirnameOfPath(cluster.kubeConfigPath); + + if (dirOfKubeconfig === kubeConfigsPath) { + logger.info(`Skipping ${cluster.id} because kubeConfigPath is under the stored KubeConfig folder`); + continue; + } + + if (syncPaths.has(cluster.kubeConfigPath) || syncPaths.has(dirOfKubeconfig)) { + logger.info(`Skipping ${cluster.id} because kubeConfigPath is already being synced`); + continue; + } + + if (isLogicalChildPath(extensionDataDir, cluster.kubeConfigPath)) { + logger.info(`Skipping ${cluster.id} because kubeConfigPath is placed under an extension_data folder`); + continue; + } + + if (!existsSync(cluster.kubeConfigPath)) { + logger.info(`Skipping ${cluster.id} because kubeConfigPath no longer exists`); + continue; + } + + logger.info(`Adding ${cluster.kubeConfigPath} from ${cluster.id} to sync paths`); + syncPaths.add(cluster.kubeConfigPath); + } + + const updatedSyncEntries: KubeconfigSyncEntry[] = [...syncPaths].map(filePath => ({ filePath })); + + logger.info("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: userStoreMigrationInjectionToken, +}); + +export default v503Beta1UserStoreMigrationInjectable; 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 cf18ccd3e4..0000000000 --- a/src/migrations/user-store/5.0.3-beta.1.ts +++ /dev/null @@ -1,81 +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 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 } 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"; -import joinPathsInjectable from "../../common/path/join-paths.injectable"; -import isLogicalChildPathInjectable from "../../common/path/is-logical-child-path.injectable"; -import getDirnameOfPathInjectable from "../../common/path/get-dirname.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 joinPaths = di.inject(joinPathsInjectable); - const isLogicalChildPath = di.inject(isLogicalChildPathInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - const { clusters = [] }: ClusterStoreModel = JSON.parse(readFileSync(joinPaths(userDataPath, "lens-cluster-store.json"), "utf-8")) ?? {}; - const extensionDataDir = joinPaths(userDataPath, "extension_data"); - const syncPaths = new Set(syncKubeconfigEntries.map(s => s.filePath)); - - syncPaths.add(joinPaths(os.homedir(), ".kube")); - - for (const cluster of clusters) { - if (!cluster.kubeConfigPath) { - continue; - } - const dirOfKubeconfig = getDirnameOfPath(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, -);