diff --git a/.github/workflows/publish-master-npm.yml b/.github/workflows/publish-master-npm.yml
new file mode 100644
index 0000000000..c6c4284e6a
--- /dev/null
+++ b/.github/workflows/publish-master-npm.yml
@@ -0,0 +1,37 @@
+name: Publish NPM Package `master`
+on:
+ pull_request:
+ branches:
+ - master
+ types:
+ - closed
+jobs:
+ publish:
+ name: Publish NPM Package `master`
+ runs-on: ubuntu-latest
+ if: |
+ ${{ github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'area/extension') }}
+ strategy:
+ matrix:
+ node-version: [12.x]
+ steps:
+ - name: Checkout Release
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Using Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ - name: Generate NPM package
+ run: |
+ make build-npm
+
+ - name: publish new release
+ run: |
+ make publish-npm
+ env:
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ NPM_RELEASE_TAG: master
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-release-npm.yml
similarity index 90%
rename from .github/workflows/publish-npm.yml
rename to .github/workflows/publish-release-npm.yml
index 2502249ee3..5a8bd9aff5 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-release-npm.yml
@@ -1,11 +1,11 @@
-name: Publish NPM
+name: Publish NPM Package Release
on:
release:
types:
- published
jobs:
publish:
- name: Publish NPM
+ name: Publish NPM Package Release
runs-on: ubuntu-latest
strategy:
matrix:
diff --git a/Makefile b/Makefile
index fc764d0a94..3139b077b4 100644
--- a/Makefile
+++ b/Makefile
@@ -110,7 +110,8 @@ build-extension-types: node_modules src/extensions/npm/extensions/dist
.PHONY: publish-npm
publish-npm: node_modules build-npm
./node_modules/.bin/npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- cd src/extensions/npm/extensions && npm publish --access=public
+ cd src/extensions/npm/extensions && npm publish --access=public --tag=${NPM_RELEASE_TAG:-latest}
+ git restore src/extensions/npm/extensions/package.json
.PHONY: docs
docs:
diff --git a/build/set_build_version.ts b/build/set_build_version.ts
index df48a1da13..13d46d4ff3 100644
--- a/build/set_build_version.ts
+++ b/build/set_build_version.ts
@@ -22,6 +22,7 @@ import * as fs from "fs";
import * as path from "path";
import appInfo from "../package.json";
import semver from "semver";
+import fastGlob from "fast-glob";
const packagePath = path.join(__dirname, "../package.json");
const versionInfo = semver.parse(appInfo.version);
@@ -41,3 +42,14 @@ if (versionInfo.prerelease) {
fs.writeFileSync(packagePath, `${JSON.stringify(appInfo, null, 2)}\n`);
+
+const extensionManifests = fastGlob.sync(["extensions/*/package.json"]);
+
+for (const manifestPath of extensionManifests) {
+ const packagePath = path.join(__dirname, "..", manifestPath);
+
+ import(packagePath).then((packageInfo) => {
+ packageInfo.default.version = `${versionInfo.raw}.${Date.now()}`;
+ fs.writeFileSync(packagePath, `${JSON.stringify(packageInfo.default, null, 2)}\n`);
+ });
+}
diff --git a/build/set_npm_version.ts b/build/set_npm_version.ts
index b7614103a2..c0b24b54a6 100644
--- a/build/set_npm_version.ts
+++ b/build/set_npm_version.ts
@@ -22,8 +22,20 @@ import * as fs from "fs";
import * as path from "path";
import packageInfo from "../src/extensions/npm/extensions/package.json";
import appInfo from "../package.json";
+import { SemVer } from "semver";
+import { execSync } from "child_process";
-const packagePath = path.join(__dirname, "../src/extensions/npm/extensions/package.json");
+const { NPM_RELEASE_TAG = "latest" } = process.env;
+const version = new SemVer(appInfo.version);
-packageInfo.version = appInfo.version;
-fs.writeFileSync(packagePath, `${JSON.stringify(packageInfo, null, 2)}\n`);
+if (NPM_RELEASE_TAG !== "latest") {
+ const gitRef = execSync("git rev-parse --short HEAD", {
+ encoding: "utf-8",
+ });
+
+ version.inc("prerelease", `git.${gitRef.trim()}`);
+}
+
+packageInfo.version = version.format();
+
+fs.writeFileSync(path.join(__dirname, "../src/extensions/npm/extensions/package.json"), `${JSON.stringify(packageInfo, null, 2)}\n`);
diff --git a/docs/extensions/capabilities/common-capabilities.md b/docs/extensions/capabilities/common-capabilities.md
index 676603ce21..ffb066f72f 100644
--- a/docs/extensions/capabilities/common-capabilities.md
+++ b/docs/extensions/capabilities/common-capabilities.md
@@ -14,9 +14,9 @@ In order to see logs from this extension, you need to start Lens from the comman
This extension can register a custom callback that is executed when an extension is activated (started).
``` javascript
-import { LensMainExtension } from "@k8slens/extensions"
+import { Main } from "@k8slens/extensions"
-export default class ExampleMainExtension extends LensMainExtension {
+export default class ExampleMainExtension extends Main.LensExtension {
async onActivate() {
console.log("hello world")
}
@@ -28,9 +28,9 @@ export default class ExampleMainExtension extends LensMainExtension {
This extension can register a custom callback that is executed when an extension is deactivated (stopped).
``` javascript
-import { LensMainExtension } from "@k8slens/extensions"
+import { Main } from "@k8slens/extensions"
-export default class ExampleMainExtension extends LensMainExtension {
+export default class ExampleMainExtension extends Main.LensExtension {
async onDeactivate() {
console.log("bye bye")
}
@@ -44,15 +44,15 @@ This extension can register custom app menus that will be displayed on OS native
Example:
```typescript
-import { LensMainExtension, windowManager } from "@k8slens/extensions"
+import { Main } from "@k8slens/extensions"
-export default class ExampleMainExtension extends LensMainExtension {
+export default class ExampleMainExtension extends Main.LensExtension {
appMenus = [
{
parentId: "help",
label: "Example item",
click() {
- windowManager.navigate("https://k8slens.dev");
+ Main.Navigation.navigate("https://k8slens.dev");
}
}
]
@@ -69,9 +69,9 @@ In order to see logs from this extension you need to check them via **View** > *
This extension can register a custom callback that is executed when an extension is activated (started).
``` javascript
-import { LensRendererExtension } from "@k8slens/extensions"
+import { Renderer } from "@k8slens/extensions"
-export default class ExampleExtension extends LensRendererExtension {
+export default class ExampleExtension extends Renderer.LensExtension {
async onActivate() {
console.log("hello world")
}
@@ -83,9 +83,9 @@ export default class ExampleExtension extends LensRendererExtension {
This extension can register a custom callback that is executed when an extension is deactivated (stopped).
``` javascript
-import { LensRendererExtension } from "@k8slens/extensions"
+import { Renderer } from "@k8slens/extensions"
-export default class ExampleMainExtension extends LensRendererExtension {
+export default class ExampleMainExtension extends Renderer.LensExtension {
async onDeactivate() {
console.log("bye bye")
}
@@ -99,10 +99,16 @@ The global page is a full-screen page that hides all other content from a window
```typescript
import React from "react"
-import { Component, LensRendererExtension } from "@k8slens/extensions"
+import { Renderer } from "@k8slens/extensions"
import { ExamplePage } from "./src/example-page"
-export default class ExampleRendererExtension extends LensRendererExtension {
+const {
+ Component: {
+ Icon,
+ }
+} = Renderer;
+
+export default class ExampleRendererExtension extends Renderer.LensExtension {
globalPages = [
{
id: "example",
@@ -117,7 +123,7 @@ export default class ExampleRendererExtension extends LensRendererExtension {
title: "Example page", // used in icon's tooltip
target: { pageId: "example" }
components: {
- Icon: () =>
(path: str return parts.filter(Boolean).join(""); }; } + +export function buildURLPositional
(path: string | any) { + const builder = buildURL(path); + + return function(params?: P, query?: Q, fragment?: string): string { + return builder({ params, query, fragment }); + }; +} diff --git a/src/common/vars.ts b/src/common/vars.ts index aa965e4721..431b6de4d7 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -37,7 +37,7 @@ export const isPublishConfigured = Object.keys(packageInfo.build).includes("publ export const productName = packageInfo.productName; export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`; -export const publicPath = "/build/"; +export const publicPath = "/build/" as string; // Webpack build paths export const contextDir = process.cwd(); @@ -60,13 +60,13 @@ defineGlobal("__static", { }); // Apis -export const apiPrefix = "/api"; // local router apis -export const apiKubePrefix = "/api-kube"; // k8s cluster apis +export const apiPrefix = "/api" as string; // local router apis +export const apiKubePrefix = "/api-kube" as string; // k8s cluster apis // Links -export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues"; -export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI"; -export const supportUrl = "https://docs.k8slens.dev/latest/support/"; +export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues" as string; +export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI" as string; +export const supportUrl = "https://docs.k8slens.dev/latest/support/" as string; // This explicitly ignores the prerelease info on the package version export const appSemVer = new SemVer(packageInfo.version); diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index ebf2a5da3e..ec42818064 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -250,6 +250,7 @@ export class ExtensionLoader extends Singleton { registries.statusBarRegistry.add(extension.statusBarItems), registries.commandRegistry.add(extension.commands), registries.welcomeMenuRegistry.add(extension.welcomeMenus), + registries.catalogEntityDetailRegistry.add(extension.catalogEntityDetailItems), ]; this.events.on("remove", (removedExtension: LensRendererExtension) => { @@ -279,6 +280,7 @@ export class ExtensionLoader extends Singleton { registries.kubeObjectMenuRegistry.add(extension.kubeObjectMenuItems), registries.kubeObjectDetailRegistry.add(extension.kubeObjectDetailItems), registries.kubeObjectStatusRegistry.add(extension.kubeObjectStatusTexts), + registries.workloadsOverviewDetailRegistry.add(extension.kubeWorkloadsOverviewItems), registries.commandRegistry.add(extension.commands), ]; diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 520ffae05b..fb26d383b0 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -20,8 +20,8 @@ */ import type { - AppPreferenceRegistration, ClusterPageMenuRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, - KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, WelcomeMenuRegistration, + AppPreferenceRegistration, CatalogEntityDetailRegistration, ClusterPageMenuRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, + KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, WelcomeMenuRegistration, WorkloadsOverviewDetailRegistration, } from "./registries"; import type { Cluster } from "../main/cluster"; import { LensExtension } from "./lens-extension"; @@ -40,8 +40,10 @@ export class LensRendererExtension extends LensExtension { statusBarItems: StatusBarRegistration[] = []; kubeObjectDetailItems: KubeObjectDetailRegistration[] = []; kubeObjectMenuItems: KubeObjectMenuRegistration[] = []; + kubeWorkloadsOverviewItems: WorkloadsOverviewDetailRegistration[] = []; commands: CommandRegistration[] = []; welcomeMenus: WelcomeMenuRegistration[] = []; + catalogEntityDetailItems: CatalogEntityDetailRegistration[] = []; async navigate
(pageId?: string, params?: P) {
const { navigate } = await import("../renderer/navigation");
diff --git a/src/extensions/main-api/index.ts b/src/extensions/main-api/index.ts
index 72102ebc24..a596e8169e 100644
--- a/src/extensions/main-api/index.ts
+++ b/src/extensions/main-api/index.ts
@@ -20,11 +20,13 @@
*/
import * as Catalog from "./catalog";
+import * as Navigation from "./navigation";
import { IpcMain as Ipc } from "../ipc/ipc-main";
import { LensMainExtension as LensExtension } from "../lens-main-extension";
export {
Catalog,
+ Navigation,
Ipc,
LensExtension,
};
diff --git a/src/extensions/main-api/navigation.ts b/src/extensions/main-api/navigation.ts
new file mode 100644
index 0000000000..95ae718b33
--- /dev/null
+++ b/src/extensions/main-api/navigation.ts
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2021 OpenLens Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+import { WindowManager } from "../../main/window-manager";
+
+export function navigate(url: string) {
+ return WindowManager.getInstance().navigate(url);
+}
diff --git a/src/extensions/registries/catalog-entity-detail-registry.ts b/src/extensions/registries/catalog-entity-detail-registry.ts
new file mode 100644
index 0000000000..0ed00b7a8b
--- /dev/null
+++ b/src/extensions/registries/catalog-entity-detail-registry.ts
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2021 OpenLens Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+import type React from "react";
+import { BaseRegistry } from "./base-registry";
+
+export interface CatalogEntityDetailComponents {
+ Details: React.ComponentType