1
0
mirror of https://github.com/lensapp/lens.git synced 2024-09-19 05:17:22 +03:00

Don't bundle extension with eula on default build (#2542)

This commit is contained in:
Jari Kolehmainen 2021-04-20 16:35:37 +03:00 committed by GitHub
parent 7970b11fe7
commit 05a3494acb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 68 additions and 30171 deletions

View File

@ -35,6 +35,15 @@ jobs:
yarn | "$(Agent.OS)"
path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- bash: |
set -e
git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay
rm -rf .lens-ide-overlay/.git
cp -r .lens-ide-overlay/* ./
cp build/package.json.patch . && patch package.json package.json.patch
displayName: Customize config
env:
GH_TOKEN: $(LENS_IDE_GH_TOKEN)
- script: make node_modules
displayName: Install dependencies
- script: make build-npm
@ -51,7 +60,6 @@ jobs:
env:
WIN_CSC_LINK: $(WIN_CSC_LINK)
WIN_CSC_KEY_PASSWORD: $(WIN_CSC_KEY_PASSWORD)
GH_TOKEN: $(GH_TOKEN)
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
- job: macOS
@ -76,6 +84,15 @@ jobs:
yarn | "$(Agent.OS)"
path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- bash: |
set -e
git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay
rm -rf .lens-ide-overlay/.git
cp -r .lens-ide-overlay/* ./
cp build/package.json.patch . && patch package.json package.json.patch
displayName: Customize config
env:
GH_TOKEN: $(LENS_IDE_GH_TOKEN)
- script: make node_modules
displayName: Install dependencies
- script: make build-npm
@ -94,7 +111,6 @@ jobs:
APPLEIDPASS: $(APPLEIDPASS)
CSC_LINK: $(CSC_LINK)
CSC_KEY_PASSWORD: $(CSC_KEY_PASSWORD)
GH_TOKEN: $(GH_TOKEN)
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
- job: Linux
@ -119,6 +135,15 @@ jobs:
yarn | "$(Agent.OS)"
path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- bash: |
set -e
git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay
rm -rf .lens-ide-overlay/.git
cp -r .lens-ide-overlay/* ./
cp build/package.json.patch . && patch package.json package.json.patch
displayName: Customize config
env:
GH_TOKEN: $(LENS_IDE_GH_TOKEN)
- script: make node_modules
displayName: Install dependencies
- script: make build-npm
@ -143,7 +168,6 @@ jobs:
displayName: Build
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
env:
GH_TOKEN: $(GH_TOKEN)
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
- script: make publish-npm

8
.bundled-extensions.json Normal file
View File

@ -0,0 +1,8 @@
{
"extensions": [
"pod-menu",
"node-menu",
"metrics-cluster-feature",
"kube-object-event-status"
]
}

View File

@ -77,27 +77,15 @@ jobs:
sudo chown -R $USER $HOME/.kube $HOME/.minikube
name: Install integration test dependencies
if: runner.os == 'Linux'
- run: |
set -e
rm -rf extensions/telemetry
xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration-linux
git checkout extensions/telemetry
- run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration-linux
name: Run Linux integration tests
if: runner.os == 'Linux'
- run: |
set -e
rm -rf extensions/telemetry
make integration-win
git checkout extensions/telemetry
- run: make integration-win
name: Run Windows integration tests
shell: bash
if: runner.os == 'Windows'
- run: |
set -e
rm -rf extensions/telemetry
make integration-mac
git checkout extensions/telemetry
- run: make integration-mac
name: Run macOS integration tests
if: runner.os == 'macOS'

View File

@ -1,11 +1,9 @@
Copyright (c) 2021 Mirantis, Inc.
Copyright (c) 2021 OpenLens Authors.
Portions of this software are licensed as follows:
* All content residing under the "docs/" directory of this repository, if that
directory exists, is licensed under "Creative Commons: CC BY-SA 4.0 license".
* All third party components incorporated into the Lens Software are licensed
under the original license provided by the owner of the applicable component.
* Content outside of the above mentioned directories or restrictions above is
available under the "MIT" license as defined below.

View File

@ -65,10 +65,11 @@ integration-win: binaries/client build-extension-types build-extensions
.PHONY: build
build: node_modules binaries/client build-extensions
yarn run compile
ifeq "$(DETECTED_OS)" "Windows"
yarn dist:win
./node_modules/.bin/electron-builder --publish onTag --x64 --ia32
else
yarn dist
./node_modules/.bin/electron-builder --publish onTag
endif
$(extension_node_modules):

View File

@ -1,32 +1,22 @@
# Lens | The Kubernetes IDE
# Lens Open Source Project (OpenLens)
[![Build Status](https://github.com/lensapp/lens/actions/workflows/test.yml/badge.svg)](https://github.com/lensapp/lens/actions/workflows/test.yml)
[![Releases](https://img.shields.io/github/downloads/lensapp/lens/total.svg)](https://github.com/lensapp/lens/releases?label=Downloads)
[![Chat on Slack](https://img.shields.io/badge/chat-on%20slack-blue.svg?logo=slack&longCache=true&style=flat)](https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI)
Lens provides the full situational awareness for everything that runs in Kubernetes. It's lowering the barrier of entry for people just getting started and radically improving productivity for people with more experience.
## The Repository
The Lens open source project is backed by a number of Kubernetes and cloud native ecosystem pioneers. It's a standalone application for MacOS, Windows and Linux operating systems. Lens is 100% open source and free of charge for any purpose.
This repository ("OpenLens") is where Team Lens develops the [Lens IDE](https://k8slens.dev) product together with the community. It is backed by a number of Kubernetes and cloud native ecosystem pioneers. This source code is available to everyone under the [MIT license](./LICENSE).
## Lens - The Kubernetes IDE
Lens - The Kubernetes IDE ("Lens IDE") is a distribution of the OpenLens repository with Team Lens specific customizations released under a traditional [EULA](https://k8slens.dev/licenses/eula).
Lens IDE provides the full situational awareness for everything that runs in Kubernetes. It's lowering the barrier of entry for people just getting started and radically improving productivity for people with more experience.
Lens IDE a standalone application for MacOS, Windows and Linux operating systems. You can download it free of charge for Windows, MacOS, and Linux from [Lens IDE website](https://k8slens.dev).
[![Screenshot](.github/screenshot.png)](https://www.youtube.com/watch?v=eeDwdVXattc)
## What makes Lens special?
* Amazing usability and end-user experience
* Unified, secure, multi-cluster management on any platform: support for hundreds of clusters
* Standalone application: no need to install anything in-cluster
* Lens installs anywhere, elimanting the need to wrangle credentials
* Real-time cluster state visualization
* Resource utilization charts and trends with history powered by built-in Prometheus
* Smart terminal access to nodes and containers
* Clusters can be local (e.g. minikube) or external (e.g. EKS, GKE, AKS)
* Performance optimized to handle massive clusters (tested with a cluster running 25k pods)
* RBAC security is preserved, as Lens uses the standard Kubernetes API
* Lens Extensions are used to add custom visualizations and functionality to accelerate development workflows for all the technologies and services that integrate with Kubernetes
* Port forwarding
* Helm package deployment: Browse and deploy Helm charts with one click-Install
* Extensions via Lens Extensions API
## Installation
See [Getting Started](https://docs.k8slens.dev/latest/getting-started/) page.

View File

@ -1,2 +0,0 @@
node_modules/
dist/

View File

@ -1,11 +0,0 @@
# Lens Example Extension
*TODO*: add more info
## Build
`npm run build`
## Dev
`npm run dev`

View File

@ -1,11 +0,0 @@
import { LensMainExtension } from "@k8slens/extensions";
export default class ExampleExtensionMain extends LensMainExtension {
onActivate() {
console.log("EXAMPLE EXTENSION MAIN: ACTIVATED", this.name, this.id);
}
onDeactivate() {
console.log("EXAMPLE EXTENSION MAIN: DEACTIVATED", this.name, this.id);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
{
"name": "example-extension",
"version": "1.0.0",
"description": "Example extension",
"main": "dist/main.js",
"renderer": "dist/renderer.js",
"lens": {
"metadata": {},
"styles": []
},
"scripts": {
"build": "webpack --config webpack.config.js",
"dev": "npm run build --watch",
"test": "jest --passWithNoTests --env=jsdom src $@"
},
"dependencies": {
"react-open-doodles": "^1.0.5"
},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"jest": "^26.6.3",
"ts-loader": "^8.0.4",
"typescript": "^4.0.3",
"webpack": "^4.44.2"
}
}

View File

@ -1,65 +0,0 @@
import React from "react";
import { observer } from "mobx-react";
import { CoffeeDoodle } from "react-open-doodles";
import { Component, Interface, K8sApi, LensRendererExtension } from "@k8slens/extensions";
export interface ExamplePageProps extends Interface.PageComponentProps<ExamplePageParams> {
extension: LensRendererExtension; // provided in "./renderer.tsx"
}
export interface ExamplePageParams {
exampleId: string;
selectedNamespaces: K8sApi.Namespace[];
}
export const namespaceStore = K8sApi.apiManager.getStore<K8sApi.NamespaceStore>(K8sApi.namespacesApi);
@observer
export class ExamplePage extends React.Component<ExamplePageProps> {
async componentDidMount() {
await namespaceStore.loadAll();
}
deactivate = () => {
const { extension } = this.props;
extension.disable();
};
renderSelectedNamespaces() {
const { selectedNamespaces } = this.props.params;
return (
<div className="flex gaps inline">
{selectedNamespaces.get().map(ns => {
const name = ns.getName();
return <Component.Badge key={name} label={name} tooltip={`Created: ${ns.getAge()}`}/>;
})}
</div>
);
}
render() {
const { exampleId } = this.props.params;
return (
<div className="flex column gaps align-flex-start" style={{ padding: 24 }}>
<div style={{ width: 200 }}>
<CoffeeDoodle accent="#3d90ce"/>
</div>
<div>Hello from Example extension!</div>
<div>Location: <i>{location.href}</i></div>
<div>Namespaces: {this.renderSelectedNamespaces()}</div>
<p className="url-params-demo flex column gaps">
<a onClick={() => exampleId.set("secret")}>Show secret button</a>
{exampleId.get() === "secret" && (
<Component.Button accent label="Deactivate" onClick={this.deactivate}/>
)}
</p>
</div>
);
}
}

View File

@ -1,45 +0,0 @@
import { Component, Interface, K8sApi, LensRendererExtension } from "@k8slens/extensions";
import { ExamplePage, ExamplePageParams, namespaceStore } from "./page";
import React from "react";
import path from "path";
export default class ExampleExtension extends LensRendererExtension {
clusterPages: Interface.PageRegistration[] = [
{
components: {
Page: (props: Interface.PageComponentProps<ExamplePageParams>) => {
return <ExamplePage {...props} extension={this}/>;
},
},
params: {
// setup basic param "exampleId" with default value "demo"
exampleId: "demo",
// setup advanced multi-values param "selectedNamespaces" with custom parsing/stringification
selectedNamespaces: {
defaultValueStringified: ["default", "kube-system"],
multiValues: true,
parse(values: string[]) { // from URL
return values.map(name => namespaceStore.getByName(name)).filter(Boolean);
},
stringify(values: K8sApi.Namespace[]) { // to URL
return values.map(namespace => namespace.getName());
},
}
}
}
];
clusterPageMenus: Interface.ClusterPageMenuRegistration[] = [
{
title: "Example extension",
components: {
Icon: ExampleIcon,
},
},
];
}
export function ExampleIcon(props: Component.IconProps) {
return <Component.Icon {...props} material="pages" tooltip={path.basename(__filename)}/>;
}

View File

@ -1,26 +0,0 @@
{
"compilerOptions": {
"outDir": "dist",
"module": "CommonJS",
"target": "ES2017",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"moduleResolution": "Node",
"sourceMap": false,
"declaration": false,
"strict": false,
"noImplicitAny": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"jsx": "react"
},
"include": [
"./*.ts",
"./*.tsx"
],
"exclude": [
"node_modules",
"*.js"
]
}

View File

@ -1,65 +0,0 @@
const path = require("path");
module.exports = [
{
entry: "./main.ts",
context: __dirname,
target: "electron-main",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
externals: [
{
"@k8slens/extensions": "var global.LensExtensions",
"mobx": "var global.Mobx",
"react": "var global.React"
}
],
resolve: {
extensions: [ ".tsx", ".ts", ".js" ],
},
output: {
libraryTarget: "commonjs2",
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
},
{
entry: "./renderer.tsx",
context: __dirname,
target: "electron-renderer",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
externals: [
{
"@k8slens/extensions": "var global.LensExtensions",
"react": "var global.React",
"mobx": "var global.Mobx"
}
],
resolve: {
extensions: [ ".tsx", ".ts", ".js" ],
},
output: {
libraryTarget: "commonjs2",
globalObject: "this",
filename: "renderer.js",
path: path.resolve(__dirname, "dist"),
},
},
];

View File

@ -1,13 +0,0 @@
import { LensMainExtension, Util } from "@k8slens/extensions";
export default class LicenseLensMainExtension extends LensMainExtension {
appMenus = [
{
parentId: "help",
label: "License",
async click() {
Util.openExternal("https://k8slens.dev/licenses/eula");
}
}
];
}

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +0,0 @@
{
"name": "lens-license",
"version": "0.1.0",
"description": "License menu item",
"main": "dist/main.js",
"scripts": {
"build": "webpack -p",
"dev": "webpack --watch",
"test": "jest --passWithNoTests --env=jsdom src $@"
},
"dependencies": {},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"@types/webpack": "^4.41.17",
"jest": "^26.6.3",
"mobx": "^5.15.5",
"react": "^16.13.1",
"ts-loader": "^8.0.4",
"ts-node": "^9.0.0",
"typescript": "^4.0.3",
"webpack": "^4.44.2"
}
}

View File

@ -1,19 +0,0 @@
{
"compilerOptions": {
"outDir": "dist",
"baseUrl": ".",
"module": "CommonJS",
"target": "ES2017",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"moduleResolution": "Node",
"sourceMap": false,
"declaration": false,
"strict": false,
"noImplicitAny": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"jsx": "react"
}
}

View File

@ -1,34 +0,0 @@
import path from "path";
const outputPath = path.resolve(__dirname, "dist");
export default [
{
entry: "./main.ts",
context: __dirname,
target: "electron-main",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
externals: {
"@k8slens/extensions": "var global.LensExtensions",
"mobx": "var global.Mobx",
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
output: {
libraryTarget: "commonjs2",
globalObject: "this",
filename: "main.js",
path: outputPath,
},
},
];

View File

@ -1,9 +0,0 @@
import { LensMainExtension } from "@k8slens/extensions";
import { surveyPreferencesStore } from "./src/survey-preferences-store";
export default class SurveyMainExtension extends LensMainExtension {
async onActivate() {
await surveyPreferencesStore.loadExtension(this);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
{
"name": "lens-survey",
"version": "0.1.0",
"description": "Lens survey",
"main": "dist/main.js",
"renderer": "dist/renderer.js",
"lens": {
"metadata": {},
"styles": []
},
"scripts": {
"build": "webpack -p",
"dev": "webpack --watch",
"test": "jest --passWithNoTests --env=jsdom src $@"
},
"dependencies": {},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"got": "^11.8.1",
"jest": "^26.6.3",
"node-machine-id": "^1.1.12",
"react": "^16.13.1",
"refiner-js": "^1.0.1",
"ts-loader": "^8.0.4",
"typescript": "^4.0.3",
"webpack": "^4.44.2"
}
}

View File

@ -1,25 +0,0 @@
import { LensRendererExtension } from "@k8slens/extensions";
import { survey } from "./src/survey";
import { SurveyPreferenceHint, SurveyPreferenceInput } from "./src/survey-preference";
import { surveyPreferencesStore } from "./src/survey-preferences-store";
import React from "react";
export default class SurveyRendererExtension extends LensRendererExtension {
appPreferences = [
{
title: "In-App Surveys",
showInPreferencesTab: "telemetry",
components: {
Hint: () => <SurveyPreferenceHint/>,
Input: () => <SurveyPreferenceInput survey={surveyPreferencesStore}/>
}
}
];
async onActivate() {
// Activate extension only on main renderer
if (window.location.hostname === "localhost") {
await surveyPreferencesStore.loadExtension(this);
survey.start();
}
}
}

View File

@ -1,3 +0,0 @@
declare module "refiner-js" {
export default function Refiner(key: string, value: string|object|number|Boolean|Array): void;
}

View File

@ -1,27 +0,0 @@
import { Component } from "@k8slens/extensions";
import React from "react";
import { observer } from "mobx-react";
import { SurveyPreferencesStore } from "./survey-preferences-store";
@observer
export class SurveyPreferenceInput extends React.Component<{survey: SurveyPreferencesStore}, {}> {
render() {
const { survey } = this.props;
return (
<Component.Checkbox
label="Allow in-app surveys"
value={survey.enabled}
onChange={v => survey.enabled = v }
/>
);
}
}
export class SurveyPreferenceHint extends React.Component {
render() {
return (
<span>This will allow you to participate in surveys to improve the Lens experience.</span>
);
}
}

View File

@ -1,36 +0,0 @@
import { Store } from "@k8slens/extensions";
import { observable, toJS, when } from "mobx";
export type SurveyPreferencesModel = {
enabled: boolean;
};
export class SurveyPreferencesStore extends Store.ExtensionStore<SurveyPreferencesModel> {
@observable enabled = true;
whenEnabled = when(() => this.enabled);
private constructor() {
super({
configName: "preferences-store",
defaults: {
enabled: true
}
});
}
protected fromStore({ enabled }: SurveyPreferencesModel): void {
this.enabled = enabled;
}
toJSON(): SurveyPreferencesModel {
return toJS({
enabled: this.enabled
}, {
recurseEverything: true
});
}
}
export const surveyPreferencesStore = SurveyPreferencesStore.getInstance<SurveyPreferencesStore>();

View File

@ -1,46 +0,0 @@
import { Util } from "@k8slens/extensions";
import { machineId } from "node-machine-id";
import Refiner from "refiner-js";
import got from "got";
import { surveyPreferencesStore } from "./survey-preferences-store";
type SurveyIdResponse = {
surveyId: string;
};
export class Survey extends Util.Singleton {
static readonly PROJECT_ID = "af468d00-4f8f-11eb-b01d-23b6562fef43";
protected anonymousId: string;
private constructor() {
super();
}
async start() {
await surveyPreferencesStore.whenEnabled;
const surveyId = await this.fetchSurveyId();
if (surveyId) {
Refiner("setProject", Survey.PROJECT_ID);
Refiner("identifyUser", {
id: surveyId,
});
}
}
async fetchSurveyId() {
try {
const surveyApi = process.env.SURVEY_API_URL || "https://survey.k8slens.dev";
const anonymousId = await machineId();
const { body } = await got(`${surveyApi}/api/survey-id?anonymousId=${anonymousId}`, { responseType: "json"});
return (body as SurveyIdResponse).surveyId;
} catch(error) {
return null;
}
}
}
export const survey = Survey.getInstance<Survey>();

View File

@ -1,29 +0,0 @@
{
"compilerOptions": {
"outDir": "dist",
"baseUrl": ".",
"module": "CommonJS",
"target": "ES2017",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"moduleResolution": "Node",
"sourceMap": false,
"declaration": false,
"strict": false,
"noImplicitAny": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"jsx": "react",
"paths": {
"*": [
"node_modules/*",
"../../types/*"
]
}
},
"include": [
"renderer.ts",
"src/**/*"
]
}

View File

@ -1,67 +0,0 @@
const path = require("path");
module.exports = [
{
entry: "./main.ts",
context: __dirname,
target: "electron-main",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
externals: [
{
"@k8slens/extensions": "var global.LensExtensions",
"react": "var global.React",
"mobx": "var global.Mobx"
}
],
resolve: {
extensions: [ ".tsx", ".ts", ".js" ],
},
output: {
libraryTarget: "commonjs2",
globalObject: "this",
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
},
{
entry: "./renderer.tsx",
context: __dirname,
target: "electron-renderer",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
externals: [
{
"@k8slens/extensions": "var global.LensExtensions",
"react": "var global.React",
"mobx": "var global.Mobx",
"mobx-react": "var global.MobxReact"
}
],
resolve: {
extensions: [ ".tsx", ".ts", ".js" ],
},
output: {
libraryTarget: "commonjs2",
globalObject: "this",
filename: "renderer.js",
path: path.resolve(__dirname, "dist"),
},
},
];

View File

@ -1,19 +0,0 @@
import { LensMainExtension } from "@k8slens/extensions";
import { telemetryPreferencesStore } from "./src/telemetry-preferences-store";
import { tracker } from "./src/tracker";
export default class TelemetryMainExtension extends LensMainExtension {
async onActivate() {
console.log("telemetry main extension activated");
tracker.start();
tracker.reportPeriodically();
tracker.watchExtensions();
await telemetryPreferencesStore.loadExtension(this);
}
onDeactivate() {
tracker.stop();
console.log("telemetry main extension deactivated");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
{
"name": "lens-telemetry",
"version": "0.1.0",
"description": "Lens telemetry",
"main": "dist/main.js",
"renderer": "dist/renderer.js",
"lens": {
"metadata": {},
"styles": []
},
"scripts": {
"build": "webpack -p",
"dev": "webpack --watch",
"test": "jest --passWithNoTests --env=jsdom src $@"
},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"@types/analytics-node": "^3.1.3",
"analytics-node": "^3.4.0-beta.3",
"jest": "^26.6.3",
"mobx": "^5.15.5",
"node-machine-id": "^1.1.12",
"react": "^16.13.1",
"ts-loader": "^8.0.4",
"typescript": "^4.0.3",
"universal-analytics": "^0.4.23",
"webpack": "^4.44.2"
}
}

View File

@ -1,25 +0,0 @@
import { LensRendererExtension } from "@k8slens/extensions";
import { telemetryPreferencesStore } from "./src/telemetry-preferences-store";
import { TelemetryPreferenceHint, TelemetryPreferenceInput } from "./src/telemetry-preference";
import { tracker } from "./src/tracker";
import React from "react";
export default class TelemetryRendererExtension extends LensRendererExtension {
appPreferences = [
{
title: "Telemetry & Usage Tracking",
showInPreferencesTab: "telemetry",
id: "telemetry-tracking",
components: {
Hint: () => <TelemetryPreferenceHint/>,
Input: () => <TelemetryPreferenceInput telemetry={telemetryPreferencesStore}/>
}
}
];
async onActivate() {
console.log("telemetry extension activated");
tracker.start();
await telemetryPreferencesStore.loadExtension(this);
}
}

View File

@ -1,27 +0,0 @@
import { Component } from "@k8slens/extensions";
import React from "react";
import { observer } from "mobx-react";
import { TelemetryPreferencesStore } from "./telemetry-preferences-store";
@observer
export class TelemetryPreferenceInput extends React.Component<{telemetry: TelemetryPreferencesStore}, {}> {
render() {
const { telemetry } = this.props;
return (
<Component.Checkbox
label="Allow telemetry & usage tracking"
value={telemetry.enabled}
onChange={v => { telemetry.enabled = v; }}
/>
);
}
}
export class TelemetryPreferenceHint extends React.Component {
render() {
return (
<span>Telemetry & usage data is collected to continuously improve the Lens experience.</span>
);
}
}

View File

@ -1,34 +0,0 @@
import { Store } from "@k8slens/extensions";
import { observable, toJS } from "mobx";
export type TelemetryPreferencesModel = {
enabled: boolean;
};
export class TelemetryPreferencesStore extends Store.ExtensionStore<TelemetryPreferencesModel> {
@observable enabled = true;
private constructor() {
super({
configName: "preferences-store",
defaults: {
enabled: true
}
});
}
protected fromStore({ enabled }: TelemetryPreferencesModel): void {
this.enabled = enabled;
}
toJSON(): TelemetryPreferencesModel {
return toJS({
enabled: this.enabled
}, {
recurseEverything: true
});
}
}
export const telemetryPreferencesStore = TelemetryPreferencesStore.getInstance<TelemetryPreferencesStore>();

View File

@ -1,185 +0,0 @@
import { EventBus, Util, Catalog, App } from "@k8slens/extensions";
import ua from "universal-analytics";
import Analytics from "analytics-node";
import { machineIdSync } from "node-machine-id";
import { telemetryPreferencesStore } from "./telemetry-preferences-store";
import { reaction, IReactionDisposer } from "mobx";
import { comparer } from "mobx";
export class Tracker extends Util.Singleton {
static readonly GA_ID = "UA-159377374-1";
static readonly SEGMENT_KEY = "YENwswyhlOgz8P7EFKUtIZ2MfON7Yxqb";
protected eventHandlers: Array<(ev: EventBus.AppEvent ) => void> = [];
protected started = false;
protected visitor: ua.Visitor;
protected analytics: Analytics;
protected machineId: string = null;
protected ip: string = null;
protected appVersion: string;
protected locale: string;
protected userAgent: string;
protected anonymousId: string;
protected os: string;
protected disposers: IReactionDisposer[];
protected reportInterval: NodeJS.Timeout;
private constructor() {
super();
this.anonymousId = machineIdSync();
this.os = this.resolveOS();
this.userAgent = `Lens ${App.version} (${this.os})`;
try {
this.visitor = ua(Tracker.GA_ID, this.anonymousId, { strictCidFormat: false });
} catch (error) {
this.visitor = ua(Tracker.GA_ID);
}
this.analytics = new Analytics(Tracker.SEGMENT_KEY, { flushAt: 1 });
this.visitor.set("dl", "https://telemetry.k8slens.dev");
this.visitor.set("ua", this.userAgent);
this.disposers = [];
}
start() {
if (this.started === true) { return; }
this.started = true;
const handler = (ev: EventBus.AppEvent) => {
this.event(ev.name, ev.action, ev.params);
};
this.eventHandlers.push(handler);
EventBus.appEventBus.addListener(handler);
}
watchExtensions() {
let previousExtensions = App.getEnabledExtensions();
this.disposers.push(reaction(() => App.getEnabledExtensions(), (currentExtensions) => {
const removedExtensions = previousExtensions.filter(x => !currentExtensions.includes(x));
removedExtensions.forEach(ext => {
this.event("extension", "disable", { extension: ext });
});
const newExtensions = currentExtensions.filter(x => !previousExtensions.includes(x));
newExtensions.forEach(ext => {
this.event("extension", "enable", { extension: ext });
});
previousExtensions = currentExtensions;
}, { equals: comparer.structural }));
}
reportPeriodically() {
this.reportData();
this.reportInterval = setInterval(() => {
this.reportData();
}, 60 * 60 * 1000); // report every 1h
}
stop() {
if (!this.started) { return; }
this.started = false;
for (const handler of this.eventHandlers) {
EventBus.appEventBus.removeListener(handler);
}
if (this.reportInterval) {
clearInterval(this.reportInterval);
}
this.disposers.forEach(disposer => {
disposer();
});
}
protected async isTelemetryAllowed(): Promise<boolean> {
return telemetryPreferencesStore.enabled;
}
protected reportData() {
const clustersList = Catalog.catalogEntities.getItemsForApiKind<Catalog.KubernetesCluster>("entity.k8slens.dev/v1alpha1", "KubernetesCluster");
this.event("generic-data", "report", {
appVersion: App.version,
os: this.os,
clustersCount: clustersList.length,
extensions: App.getEnabledExtensions()
});
clustersList.forEach((cluster) => {
if (!cluster?.metadata.lastSeen) { return; }
this.reportClusterData(cluster);
});
}
protected reportClusterData(cluster: Catalog.KubernetesCluster) {
this.event("cluster-data", "report", {
id: cluster.metadata.id,
managed: cluster.metadata.source !== "local",
kubernetesVersion: cluster.metadata.version,
distribution: cluster.metadata.distribution,
nodesCount: cluster.metadata.nodes,
lastSeen: cluster.metadata.lastSeen,
prometheus: cluster.metadata.prometheus
});
}
protected resolveOS() {
let os = "";
if (App.isMac) {
os = "MacOS";
} else if(App.isWindows) {
os = "Windows";
} else if (App.isLinux) {
os = "Linux";
if (App.isSnap) {
os += "; Snap";
} else {
os += "; AppImage";
}
} else {
os = "Unknown";
}
return os;
}
protected async event(eventCategory: string, eventAction: string, otherParams = {}) {
try {
const allowed = await this.isTelemetryAllowed();
if (!allowed) {
return;
}
this.visitor.event({
ec: eventCategory,
ea: eventAction,
...otherParams,
}).send();
this.analytics.track({
anonymousId: this.anonymousId,
event: `${eventCategory} ${eventAction}`,
context: {
userAgent: this.userAgent,
},
properties: {
category: eventCategory,
...otherParams,
},
});
} catch (err) {
console.error(`Failed to track "${eventCategory}:${eventAction}"`, err);
}
}
}
export const tracker = Tracker.getInstance<Tracker>();

View File

@ -1,29 +0,0 @@
{
"compilerOptions": {
"outDir": "dist",
"baseUrl": ".",
"module": "CommonJS",
"target": "ES2017",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"moduleResolution": "Node",
"sourceMap": false,
"declaration": false,
"strict": false,
"noImplicitAny": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"jsx": "react",
"paths": {
"*": [
"node_modules/*",
"../../types/*"
]
}
},
"include": [
"renderer.ts",
"src/**/*"
]
}

View File

@ -1,67 +0,0 @@
const path = require("path");
module.exports = [
{
entry: "./main.ts",
context: __dirname,
target: "electron-main",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
externals: [
{
"@k8slens/extensions": "var global.LensExtensions",
"react": "var global.React",
"mobx": "var global.Mobx"
}
],
resolve: {
extensions: [ ".tsx", ".ts", ".js" ],
},
output: {
libraryTarget: "commonjs2",
globalObject: "this",
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
},
{
entry: "./renderer.tsx",
context: __dirname,
target: "electron-renderer",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
externals: [
{
"@k8slens/extensions": "var global.LensExtensions",
"react": "var global.React",
"mobx": "var global.Mobx",
"mobx-react": "var global.MobxReact"
}
],
resolve: {
extensions: [ ".tsx", ".ts", ".js" ],
},
output: {
libraryTarget: "commonjs2",
globalObject: "this",
filename: "renderer.js",
path: path.resolve(__dirname, "dist"),
},
},
];

View File

@ -41,7 +41,7 @@ describe("Lens integration tests", () => {
describe("preferences page", () => {
it('shows "preferences"', async () => {
const appName: string = process.platform === "darwin" ? "Lens" : "File";
const appName: string = process.platform === "darwin" ? "OpenLens" : "File";
await app.electron.ipcRenderer.send("test-menu-item-click", appName, "Preferences");
await app.client.waitUntilTextExists("[data-testid=application-header]", "APPLICATION");

View File

@ -3,9 +3,9 @@ import * as util from "util";
import { exec } from "child_process";
const AppPaths: Partial<Record<NodeJS.Platform, string>> = {
"win32": "./dist/win-unpacked/Lens.exe",
"linux": "./dist/linux-unpacked/kontena-lens",
"darwin": "./dist/mac/Lens.app/Contents/MacOS/Lens",
"win32": "./dist/win-unpacked/OpenLens.exe",
"linux": "./dist/linux-unpacked/open-lens",
"darwin": "./dist/mac/OpenLens.app/Contents/MacOS/OpenLens",
};
interface DoneCallback {

View File

@ -1,14 +1,13 @@
{
"name": "kontena-lens",
"productName": "Lens",
"description": "Lens - The Kubernetes IDE",
"name": "open-lens",
"productName": "OpenLens",
"description": "OpenLens - Open Source IDE for Kubernetes",
"version": "5.0.0-alpha.1",
"main": "static/build/main.js",
"copyright": "© 2021, Mirantis, Inc.",
"copyright": "© 2021, OpenLens Authors",
"license": "MIT",
"author": {
"name": "Mirantis, Inc.",
"email": "info@k8slens.dev"
"name": "OpenLens Authors"
},
"scripts": {
"dev": "concurrently -k \"yarn run dev-run -C\" yarn:dev:*",
@ -23,9 +22,9 @@
"compile:renderer": "yarn run webpack --config webpack.renderer.ts",
"compile:extension-types": "yarn run webpack --config webpack.extensions.ts",
"npm:fix-package-version": "yarn run ts-node build/set_npm_version.ts",
"build:linux": "yarn run compile && electron-builder --linux --dir -c.productName=Lens",
"build:mac": "yarn run compile && electron-builder --mac --dir -c.productName=Lens",
"build:win": "yarn run compile && electron-builder --win --dir -c.productName=Lens",
"build:linux": "yarn run compile && electron-builder --linux --dir",
"build:mac": "yarn run compile && electron-builder --mac --dir",
"build:win": "yarn run compile && electron-builder --win --dir",
"integration": "jest --runInBand integration",
"dist": "yarn run compile && electron-builder --publish onTag",
"dist:win": "yarn run compile && electron-builder --publish onTag --x64 --ia32",
@ -107,7 +106,6 @@
"target": [
"deb",
"rpm",
"snap",
"AppImage"
],
"extraResources": [
@ -161,16 +159,6 @@
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"snap": {
"confinement": "classic"
},
"publish": [
{
"provider": "s3",
"bucket": "lens-binaries",
"path": "/ide"
}
],
"protocols": {
"name": "Lens Protocol Handler",
"schemes": [
@ -179,17 +167,6 @@
"role": "Viewer"
}
},
"lens": {
"extensions": [
"telemetry",
"pod-menu",
"node-menu",
"metrics-cluster-feature",
"license-menu-item",
"kube-object-event-status",
"survey"
]
},
"dependencies": {
"@hapi/call": "^8.0.0",
"@hapi/subtext": "^7.0.3",

View File

@ -9,10 +9,6 @@ export function getBundledKubectlVersion(): string {
return packageInfo.config.bundledKubectlVersion;
}
export function getBundledExtensions(): string[] {
return packageInfo.lens?.extensions || [];
}
export async function getAppVersionFromProxyServer(proxyPort: number): Promise<string> {
const response = await requestPromise({
method: "GET",

View File

@ -6,7 +6,6 @@ import { observable, reaction, toJS, when } from "mobx";
import os from "os";
import path from "path";
import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc";
import { getBundledExtensions } from "../common/utils/app-version";
import logger from "../main/logger";
import { extensionInstaller, PackageJson } from "./extension-installer";
import { extensionsStore } from "./extensions-store";
@ -348,7 +347,7 @@ export class ExtensionDiscovery {
await this.installBundledPackages(this.packageJsonPath, bundledExtensions);
const userExtensions = await this.loadFromFolder(this.localFolderPath);
const userExtensions = await this.loadFromFolder(this.localFolderPath, bundledExtensions.map((extension) => extension.manifest.name));
for (const extension of userExtensions) {
if (await fs.pathExists(extension.manifestPath) === false) {
@ -382,14 +381,9 @@ export class ExtensionDiscovery {
async loadBundledExtensions() {
const extensions: InstalledExtension[] = [];
const folderPath = this.bundledFolderPath;
const bundledExtensions = getBundledExtensions();
const paths = await fs.readdir(folderPath);
for (const fileName of paths) {
if (!bundledExtensions.includes(fileName)) {
continue;
}
const absPath = path.resolve(folderPath, fileName);
const extension = await this.loadExtensionFromFolder(absPath, { isBundled: true });
@ -402,8 +396,7 @@ export class ExtensionDiscovery {
return extensions;
}
async loadFromFolder(folderPath: string): Promise<InstalledExtension[]> {
const bundledExtensions = getBundledExtensions();
async loadFromFolder(folderPath: string, bundledExtensions: string[]): Promise<InstalledExtension[]> {
const extensions: InstalledExtension[] = [];
const paths = await fs.readdir(folderPath);

View File

@ -68,7 +68,7 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
extractComments: {
condition: "some",
banner: [
`Lens - The Kubernetes IDE. Copyright ${new Date().getFullYear()} by Mirantis, Inc. All rights reserved.`
`OpenLens - Open Source Kubernetes IDE. Copyright ${new Date().getFullYear()} OpenLens Authors`
].join("\n")
}
})