mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
initial support for checking engines.lens version
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
57c87a2e71
commit
ba22372c64
@ -69,7 +69,8 @@ export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5
|
||||
export const supportUrl = "https://docs.k8slens.dev/latest/support/";
|
||||
|
||||
// This explicitly ignores the prerelease info on the package version
|
||||
const { major, minor, patch } = new SemVer(packageInfo.version);
|
||||
export const appSemVer = new SemVer(packageInfo.version);
|
||||
const { major, minor, patch } = appSemVer;
|
||||
const mmpVersion = [major, minor, patch].join(".");
|
||||
const docsVersion = isProduction ? `v${mmpVersion}` : "latest";
|
||||
|
||||
|
||||
@ -38,7 +38,8 @@ describe("lens extension", () => {
|
||||
absolutePath: "/absolute/fake/",
|
||||
manifestPath: "/this/is/fake/package.json",
|
||||
isBundled: false,
|
||||
isEnabled: true
|
||||
isEnabled: true,
|
||||
isValid: true
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -30,11 +30,13 @@ import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } fr
|
||||
import { Singleton, toJS } from "../common/utils";
|
||||
import logger from "../main/logger";
|
||||
import { ExtensionInstallationStateStore } from "../renderer/components/+extensions/extension-install.store";
|
||||
import { extensionInstaller, PackageJson } from "./extension-installer";
|
||||
import { extensionInstaller } from "./extension-installer";
|
||||
import { ExtensionsStore } from "./extensions-store";
|
||||
import { ExtensionLoader } from "./extension-loader";
|
||||
import type { LensExtensionId, LensExtensionManifest } from "./lens-extension";
|
||||
|
||||
import type { PackageJson } from "type-fest";
|
||||
import semver from "semver";
|
||||
import { appSemVer } from "../common/vars";
|
||||
export interface InstalledExtension {
|
||||
id: LensExtensionId;
|
||||
|
||||
@ -47,6 +49,7 @@ export interface InstalledExtension {
|
||||
// Absolute to the symlinked package.json file
|
||||
readonly manifestPath: string;
|
||||
readonly isBundled: boolean; // defined in project root's package.json
|
||||
readonly isValid: boolean;
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
@ -348,12 +351,17 @@ export class ExtensionDiscovery extends Singleton {
|
||||
*/
|
||||
protected async getByManifest(manifestPath: string, { isBundled = false } = {}): Promise<InstalledExtension | null> {
|
||||
try {
|
||||
const manifest = await fse.readJson(manifestPath);
|
||||
const manifest = await fse.readJson(manifestPath) as LensExtensionManifest;
|
||||
const installedManifestPath = this.getInstalledManifestPath(manifest.name);
|
||||
const isEnabled = isBundled || ExtensionsStore.getInstance().isEnabled(installedManifestPath);
|
||||
const extensionDir = path.dirname(manifestPath);
|
||||
const npmPackage = path.join(extensionDir, `${manifest.name}-${manifest.version}.tgz`);
|
||||
const absolutePath = (await fse.pathExists(npmPackage)) ? npmPackage : extensionDir;
|
||||
let isValid = false;
|
||||
|
||||
if (manifest.engines?.lens) {
|
||||
isValid = semver.satisfies(appSemVer, manifest.engines.lens);
|
||||
}
|
||||
|
||||
return {
|
||||
id: installedManifestPath,
|
||||
@ -361,7 +369,8 @@ export class ExtensionDiscovery extends Singleton {
|
||||
manifestPath: installedManifestPath,
|
||||
manifest,
|
||||
isBundled,
|
||||
isEnabled
|
||||
isEnabled,
|
||||
isValid
|
||||
};
|
||||
} catch (error) {
|
||||
if (error.code === "ENOTDIR") {
|
||||
|
||||
@ -25,17 +25,10 @@ import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import logger from "../main/logger";
|
||||
import { extensionPackagesRoot } from "./extension-loader";
|
||||
import type { PackageJson } from "type-fest";
|
||||
|
||||
const logModule = "[EXTENSION-INSTALLER]";
|
||||
|
||||
type Dependencies = {
|
||||
[name: string]: string;
|
||||
};
|
||||
|
||||
// Type for the package.json file that is written by ExtensionInstaller
|
||||
export type PackageJson = {
|
||||
dependencies: Dependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* Installs dependencies for extensions
|
||||
|
||||
@ -299,7 +299,7 @@ export class ExtensionLoader extends Singleton {
|
||||
for (const [extId, extension] of installedExtensions) {
|
||||
const alreadyInit = this.instances.has(extId);
|
||||
|
||||
if (extension.isEnabled && !alreadyInit) {
|
||||
if (extension.isValid && extension.isEnabled && !alreadyInit) {
|
||||
try {
|
||||
const LensExtensionClass = this.requireExtension(extension);
|
||||
|
||||
|
||||
@ -25,17 +25,16 @@ import { FilesystemProvisionerStore } from "../main/extension-filesystem";
|
||||
import logger from "../main/logger";
|
||||
import type { ProtocolHandlerRegistration } from "./registries";
|
||||
import { disposer } from "../common/utils";
|
||||
import type { PackageJson } from "type-fest";
|
||||
|
||||
export type LensExtensionId = string; // path to manifest (package.json)
|
||||
export type LensExtensionConstructor = new (...args: ConstructorParameters<typeof LensExtension>) => LensExtension;
|
||||
|
||||
export interface LensExtensionManifest {
|
||||
export interface LensExtensionManifest extends PackageJson {
|
||||
name: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
main?: string; // path to %ext/dist/main.js
|
||||
renderer?: string; // path to %ext/dist/renderer.js
|
||||
lens?: object; // fixme: add more required fields for validation
|
||||
}
|
||||
|
||||
export const Disposers = Symbol();
|
||||
|
||||
@ -40,7 +40,8 @@ describe("getPageUrl", () => {
|
||||
absolutePath: "/absolute/fake/",
|
||||
manifestPath: "/this/is/fake/package.json",
|
||||
isBundled: false,
|
||||
isEnabled: true
|
||||
isEnabled: true,
|
||||
isValid: true
|
||||
});
|
||||
globalPageRegistry.add({
|
||||
id: "page-with-params",
|
||||
@ -107,7 +108,8 @@ describe("globalPageRegistry", () => {
|
||||
absolutePath: "/absolute/fake/",
|
||||
manifestPath: "/this/is/fake/package.json",
|
||||
isBundled: false,
|
||||
isEnabled: true
|
||||
isEnabled: true,
|
||||
isValid: true
|
||||
});
|
||||
globalPageRegistry.add([
|
||||
{
|
||||
|
||||
@ -87,6 +87,7 @@ describe("protocol router tests", () => {
|
||||
},
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
isValid: true,
|
||||
absolutePath: "/foo/bar",
|
||||
});
|
||||
const lpr = LensProtocolRouterMain.getInstance();
|
||||
@ -164,6 +165,7 @@ describe("protocol router tests", () => {
|
||||
},
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
isValid: true,
|
||||
absolutePath: "/foo/bar",
|
||||
});
|
||||
|
||||
@ -204,6 +206,7 @@ describe("protocol router tests", () => {
|
||||
},
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
isValid: true,
|
||||
absolutePath: "/foo/bar",
|
||||
});
|
||||
|
||||
@ -228,6 +231,7 @@ describe("protocol router tests", () => {
|
||||
},
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
isValid: true,
|
||||
absolutePath: "/foo/bar",
|
||||
});
|
||||
|
||||
|
||||
@ -76,7 +76,8 @@ describe("Extensions", () => {
|
||||
absolutePath: "/absolute/path",
|
||||
manifestPath: "/symlinked/path/package.json",
|
||||
isBundled: false,
|
||||
isEnabled: true
|
||||
isEnabled: true,
|
||||
isValid: true
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -11,6 +11,10 @@
|
||||
color: var(--colorOk);
|
||||
}
|
||||
|
||||
.invalid {
|
||||
color: var(--colorWarning);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 0!important;
|
||||
}
|
||||
@ -22,4 +26,4 @@
|
||||
|
||||
.frozenRow {
|
||||
@apply opacity-30 pointer-events-none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,14 +39,18 @@ interface Props {
|
||||
uninstall: (extension: InstalledExtension) => void;
|
||||
}
|
||||
|
||||
function getStatus(isEnabled: boolean) {
|
||||
return isEnabled ? "Enabled" : "Disabled";
|
||||
function getStatus(extension: InstalledExtension) {
|
||||
if (!extension.isValid) {
|
||||
return "Incompatible";
|
||||
}
|
||||
|
||||
return extension.isEnabled ? "Enabled" : "Disabled";
|
||||
}
|
||||
|
||||
export const InstalledExtensions = observer(({ extensions, uninstall, enable, disable }: Props) => {
|
||||
const filters = [
|
||||
(extension: InstalledExtension) => extension.manifest.name,
|
||||
(extension: InstalledExtension) => getStatus(extension.isEnabled),
|
||||
(extension: InstalledExtension) => getStatus(extension),
|
||||
(extension: InstalledExtension) => extension.manifest.version,
|
||||
];
|
||||
|
||||
@ -87,7 +91,7 @@ export const InstalledExtensions = observer(({ extensions, uninstall, enable, di
|
||||
const data = useMemo(
|
||||
() => {
|
||||
return extensions.map(extension => {
|
||||
const { id, isEnabled, manifest } = extension;
|
||||
const { id, isEnabled, isValid, manifest } = extension;
|
||||
const { name, description, version } = manifest;
|
||||
const isUninstalling = ExtensionInstallationStateStore.isExtensionUninstalling(id);
|
||||
|
||||
@ -102,29 +106,34 @@ export const InstalledExtensions = observer(({ extensions, uninstall, enable, di
|
||||
),
|
||||
version,
|
||||
status: (
|
||||
<div className={cssNames({[styles.enabled]: getStatus(isEnabled) == "Enabled"})}>
|
||||
{getStatus(isEnabled)}
|
||||
<div className={cssNames({[styles.enabled]: isEnabled, [styles.invalid]: !isValid})}>
|
||||
{getStatus(extension)}
|
||||
</div>
|
||||
),
|
||||
actions: (
|
||||
<MenuActions usePortal toolbar={false}>
|
||||
{isEnabled ? (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => disable(id)}
|
||||
>
|
||||
<Icon material="unpublished"/>
|
||||
<span className="title" aria-disabled={isUninstalling}>Disable</span>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => enable(id)}
|
||||
>
|
||||
<Icon material="check_circle"/>
|
||||
<span className="title" aria-disabled={isUninstalling}>Enable</span>
|
||||
</MenuItem>
|
||||
{ isValid && (
|
||||
<>
|
||||
{isEnabled ? (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => disable(id)}
|
||||
>
|
||||
<Icon material="unpublished"/>
|
||||
<span className="title" aria-disabled={isUninstalling}>Disable</span>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => enable(id)}
|
||||
>
|
||||
<Icon material="check_circle"/>
|
||||
<span className="title" aria-disabled={isUninstalling}>Enable</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => uninstall(extension)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user