mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Release 6.4.0 (#7250)
* General fixes for release-tool (#7238)
* General fixes for release-tool
Signed-off-by: Sebastian Malton <sebastian@malton.name>
* Revert change to number of PRs retrieved
Signed-off-by: Sebastian Malton <sebastian@malton.name>
---------
Signed-off-by: Sebastian Malton <sebastian@malton.name>
* Throw on errors in kubectlApplyFolder (#7239)
Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
* Quick fix for store migration version being wrong (#7243)
Signed-off-by: Sebastian Malton <sebastian@malton.name>
* Revert "Renderer file logging transport (#6795)" (#7245)
Renderer file logging still caused UI freezing (at least on apple silicon macs) when cluster frame was open and main frame was reloaded.
See #544
This reverts commit ac2d0e46ff.
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Fix extension install (#7247)
* Fix extension install
- Remove old bundled extension dependencies
- Make sure external extensions are installed as optional
Signed-off-by: Sebastian Malton <sebastian@malton.name>
* Ignore ENOENT errors
Signed-off-by: Sebastian Malton <sebastian@malton.name>
* Add comment
Signed-off-by: Sebastian Malton <sebastian@malton.name>
---------
Signed-off-by: Sebastian Malton <sebastian@malton.name>
* Release 6.4.0
Signed-off-by: Sebastian Malton <sebastian@malton.name>
* Fix lint
Signed-off-by: Sebastian Malton <sebastian@malton.name>
---------
Signed-off-by: Sebastian Malton <sebastian@malton.name>
Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
Co-authored-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
Co-authored-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
This commit is contained in:
parent
b74cc14b72
commit
5ac90e9178
@ -4,7 +4,7 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "6.4.0-beta.17",
|
||||
"version": "6.4.0",
|
||||
"npmClient": "yarn",
|
||||
"npmClientArgs": [
|
||||
"--network-timeout=100000"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"productName": "",
|
||||
"description": "Lens Desktop Core",
|
||||
"homepage": "https://github.com/lensapp/lens",
|
||||
"version": "6.4.0-beta.17",
|
||||
"version": "6.4.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lensapp/lens.git"
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import kubectlApplyAllInjectable from "../../main/kubectl/kubectl-apply-all.injectable";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import type { KubernetesCluster } from "../catalog-entities";
|
||||
import readDirectoryInjectable from "../fs/read-directory.injectable";
|
||||
import readFileInjectable from "../fs/read-file.injectable";
|
||||
import createResourceStackInjectable from "../k8s/create-resource-stack.injectable";
|
||||
import appPathsStateInjectable from "../app-paths/app-paths-state.injectable";
|
||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
|
||||
describe("create resource stack tests", () => {
|
||||
let di: DiContainer;
|
||||
let cluster: KubernetesCluster;
|
||||
|
||||
beforeEach(async () => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
cluster = {
|
||||
getId: () => "test-cluster",
|
||||
} as any;
|
||||
|
||||
di.override(readDirectoryInjectable, () => () => Promise.resolve(["file1"]) as any);
|
||||
di.override(readFileInjectable, () => () => Promise.resolve("filecontents"));
|
||||
di.override(appPathsStateInjectable, () => ({
|
||||
get: () => ({}),
|
||||
}));
|
||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||
|
||||
});
|
||||
|
||||
describe("kubectlApplyFolder", () => {
|
||||
it("returns response", async () => {
|
||||
di.override(kubectlApplyAllInjectable, () => () => Promise.resolve({
|
||||
callWasSuccessful: true as const,
|
||||
response: "success",
|
||||
}));
|
||||
|
||||
const createResourceStack = di.inject(createResourceStackInjectable);
|
||||
const resourceStack = createResourceStack(cluster, "test");
|
||||
|
||||
const response = await resourceStack.kubectlApplyFolder("/foo/bar");
|
||||
|
||||
expect(response).toEqual("success");
|
||||
});
|
||||
|
||||
it("throws on error", async () => {
|
||||
di.override(kubectlApplyAllInjectable, () => () => Promise.resolve({
|
||||
callWasSuccessful: false as const,
|
||||
error: "No permissions",
|
||||
}));
|
||||
|
||||
const createResourceStack = di.inject(createResourceStackInjectable);
|
||||
const resourceStack = createResourceStack(cluster, "test");
|
||||
|
||||
await expect(() => resourceStack.kubectlApplyFolder("/foo/bar")).rejects.toThrow("No permissions");
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -7,7 +7,7 @@ import type { ReadOptions } from "fs-extra";
|
||||
import fse from "fs-extra";
|
||||
|
||||
/**
|
||||
* NOTE: Add corrisponding a corrisponding override of this injecable in `src/test-utils/override-fs-with-fakes.ts`
|
||||
* NOTE: Add corresponding override of this injectable in `src/test-utils/override-fs-with-fakes.ts`
|
||||
*/
|
||||
const fsInjectable = getInjectable({
|
||||
id: "fs",
|
||||
|
||||
@ -51,7 +51,7 @@ export class ResourceStack {
|
||||
|
||||
this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to apply resources: ${result.error}`);
|
||||
|
||||
return "";
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,13 +3,20 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { createLogger, format } from "winston";
|
||||
import type { Logger } from "./logger";
|
||||
import winstonLoggerInjectable from "./winston-logger.injectable";
|
||||
import { loggerTransportInjectionToken } from "./logger/transports";
|
||||
|
||||
const loggerInjectable = getInjectable({
|
||||
id: "logger",
|
||||
instantiate: (di): Logger => {
|
||||
const baseLogger = di.inject(winstonLoggerInjectable);
|
||||
const baseLogger = createLogger({
|
||||
format: format.combine(
|
||||
format.splat(),
|
||||
format.simple(),
|
||||
),
|
||||
transports: di.injectMany(loggerTransportInjectionToken),
|
||||
});
|
||||
|
||||
return {
|
||||
debug: (message, ...data) => baseLogger.debug(message, ...data),
|
||||
|
||||
@ -3,11 +3,10 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { applicationInformationToken } from "./application-information-token";
|
||||
|
||||
const storeMigrationVersionInjectable = getInjectable({
|
||||
id: "store-migration-version",
|
||||
instantiate: (di) => di.inject(applicationInformationToken).version,
|
||||
instantiate: () => "6.4.0",
|
||||
});
|
||||
|
||||
export default storeMigrationVersionInjectable;
|
||||
|
||||
@ -1,20 +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 { createLogger, format } from "winston";
|
||||
import { loggerTransportInjectionToken } from "./logger/transports";
|
||||
|
||||
const winstonLoggerInjectable = getInjectable({
|
||||
id: "winston-logger",
|
||||
instantiate: (di) => createLogger({
|
||||
format: format.combine(
|
||||
format.splat(),
|
||||
format.simple(),
|
||||
),
|
||||
transports: di.injectMany(loggerTransportInjectionToken),
|
||||
}),
|
||||
});
|
||||
|
||||
export default winstonLoggerInjectable;
|
||||
@ -8,8 +8,8 @@ import extensionLoaderInjectable from "../extension-loader/extension-loader.inje
|
||||
import isCompatibleExtensionInjectable from "./is-compatible-extension/is-compatible-extension.injectable";
|
||||
import extensionsStoreInjectable from "../extensions-store/extensions-store.injectable";
|
||||
import extensionInstallationStateStoreInjectable from "../extension-installation-state-store/extension-installation-state-store.injectable";
|
||||
import installExtensionInjectable from "../extension-installer/install-extension/install-extension.injectable";
|
||||
import extensionPackageRootDirectoryInjectable from "../extension-installer/extension-package-root-directory/extension-package-root-directory.injectable";
|
||||
import installExtensionInjectable from "../install-extension/install-extension.injectable";
|
||||
import extensionPackageRootDirectoryInjectable from "../install-extension/extension-package-root-directory.injectable";
|
||||
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||
|
||||
@ -7,7 +7,7 @@ import type { FSWatcher } from "chokidar";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import extensionDiscoveryInjectable from "../extension-discovery/extension-discovery.injectable";
|
||||
import type { ExtensionDiscovery } from "../extension-discovery/extension-discovery";
|
||||
import installExtensionInjectable from "../extension-installer/install-extension/install-extension.injectable";
|
||||
import installExtensionInjectable from "../install-extension/install-extension.injectable";
|
||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { delay } from "../../renderer/utils";
|
||||
import { observable, runInAction, when } from "mobx";
|
||||
|
||||
@ -1,21 +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 pathToNpmCliInjectable from "../../common/app-paths/path-to-npm-cli.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import { ExtensionInstaller } from "./extension-installer";
|
||||
import extensionPackageRootDirectoryInjectable from "./extension-package-root-directory/extension-package-root-directory.injectable";
|
||||
|
||||
const extensionInstallerInjectable = getInjectable({
|
||||
id: "extension-installer",
|
||||
|
||||
instantiate: (di) => new ExtensionInstaller({
|
||||
extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
pathToNpmCli: di.inject(pathToNpmCliInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default extensionInstallerInjectable;
|
||||
@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import AwaitLock from "await-lock";
|
||||
import child_process from "child_process";
|
||||
import type { Logger } from "../../common/logger";
|
||||
|
||||
const logModule = "[EXTENSION-INSTALLER]";
|
||||
|
||||
interface Dependencies {
|
||||
readonly extensionPackageRootDirectory: string;
|
||||
readonly logger: Logger;
|
||||
readonly pathToNpmCli: string;
|
||||
}
|
||||
|
||||
const baseNpmInstallArgs = [
|
||||
"install",
|
||||
"--audit=false",
|
||||
"--fund=false",
|
||||
// NOTE: we do not omit the `optional` dependencies because that is how we specify the non-bundled extensions
|
||||
"--omit=dev",
|
||||
"--omit=peer",
|
||||
"--prefer-offline",
|
||||
];
|
||||
|
||||
/**
|
||||
* Installs dependencies for extensions
|
||||
*/
|
||||
export class ExtensionInstaller {
|
||||
private readonly installLock = new AwaitLock();
|
||||
|
||||
constructor(private readonly dependencies: Dependencies) {}
|
||||
|
||||
/**
|
||||
* Install single package using npm
|
||||
*/
|
||||
installPackage = async (name: string): Promise<void> => {
|
||||
// Mutual exclusion to install packages in sequence
|
||||
await this.installLock.acquireAsync();
|
||||
|
||||
try {
|
||||
this.dependencies.logger.info(`${logModule} installing package from ${name} to ${this.dependencies.extensionPackageRootDirectory}`);
|
||||
await this.npm(...baseNpmInstallArgs, name);
|
||||
this.dependencies.logger.info(`${logModule} package ${name} installed to ${this.dependencies.extensionPackageRootDirectory}`);
|
||||
} finally {
|
||||
this.installLock.release();
|
||||
}
|
||||
};
|
||||
|
||||
private npm(...args: string[]): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = child_process.fork(this.dependencies.pathToNpmCli, args, {
|
||||
cwd: this.dependencies.extensionPackageRootDirectory,
|
||||
silent: true,
|
||||
env: {},
|
||||
});
|
||||
let stderr = "";
|
||||
|
||||
child.stderr?.on("data", data => {
|
||||
stderr += String(data);
|
||||
});
|
||||
|
||||
child.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(stderr));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
child.on("error", error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,13 +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 extensionInstallerInjectable from "../extension-installer.injectable";
|
||||
|
||||
const installExtensionInjectable = getInjectable({
|
||||
id: "install-extension",
|
||||
instantiate: (di) => di.inject(extensionInstallerInjectable).installPackage,
|
||||
});
|
||||
|
||||
export default installExtensionInjectable;
|
||||
@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
|
||||
const extensionPackageRootDirectoryInjectable = getInjectable({
|
||||
id: "extension-package-root-directory",
|
||||
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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 { fork } from "child_process";
|
||||
import AwaitLock from "await-lock";
|
||||
import pathToNpmCliInjectable from "../../common/app-paths/path-to-npm-cli.injectable";
|
||||
import extensionPackageRootDirectoryInjectable from "./extension-package-root-directory.injectable";
|
||||
import prefixedLoggerInjectable from "../../common/logger/prefixed-logger.injectable";
|
||||
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
||||
import type { PackageJson } from "../common-api";
|
||||
import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable";
|
||||
import { once } from "lodash";
|
||||
import { isErrnoException } from "../../common/utils";
|
||||
|
||||
const baseNpmInstallArgs = [
|
||||
"install",
|
||||
"--save-optional",
|
||||
"--audit=false",
|
||||
"--fund=false",
|
||||
// NOTE: we do not omit the `optional` dependencies because that is how we specify the non-bundled extensions
|
||||
"--omit=dev",
|
||||
"--omit=peer",
|
||||
"--prefer-offline",
|
||||
];
|
||||
|
||||
export type InstallExtension = (name: string) => Promise<void>;
|
||||
|
||||
const installExtensionInjectable = getInjectable({
|
||||
id: "install-extension",
|
||||
instantiate: (di): InstallExtension => {
|
||||
const pathToNpmCli = di.inject(pathToNpmCliInjectable);
|
||||
const extensionPackageRootDirectory = di.inject(extensionPackageRootDirectoryInjectable);
|
||||
const readJsonFile = di.inject(readJsonFileInjectable);
|
||||
const writeJsonFile = di.inject(writeJsonFileInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const logger = di.inject(prefixedLoggerInjectable, "EXTENSION-INSTALLER");
|
||||
|
||||
const forkNpm = (...args: string[]) => new Promise<void>((resolve, reject) => {
|
||||
const child = fork(pathToNpmCli, args, {
|
||||
cwd: extensionPackageRootDirectory,
|
||||
silent: true,
|
||||
env: {},
|
||||
});
|
||||
let stderr = "";
|
||||
|
||||
child.stderr?.on("data", data => {
|
||||
stderr += String(data);
|
||||
});
|
||||
|
||||
child.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(stderr));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
child.on("error", error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
const packageJsonPath = joinPaths(extensionPackageRootDirectory, "package.json");
|
||||
|
||||
/**
|
||||
* NOTES:
|
||||
* - We have to keep the `package.json` because `npm install` removes files from `node_modules`
|
||||
* if they are no longer in the `package.json`
|
||||
* - In v6.2.X we saved bundled extensions as `"dependencies"` and external extensions as
|
||||
* `"optionalDependencies"` at startup. This was done because `"optionalDependencies"` can
|
||||
* fail to install and that is OK.
|
||||
* - We continue to maintain this behavior here by only installing new dependencies as
|
||||
* `"optionalDependencies"`
|
||||
*/
|
||||
const fixupPackageJson = once(async () => {
|
||||
try {
|
||||
const packageJson = await readJsonFile(packageJsonPath) as PackageJson;
|
||||
|
||||
delete packageJson.dependencies;
|
||||
|
||||
await writeJsonFile(packageJsonPath, packageJson);
|
||||
} catch (error) {
|
||||
if (isErrnoException(error) && error.code === "ENOENT") {
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
const installLock = new AwaitLock();
|
||||
|
||||
return async (name) => {
|
||||
await installLock.acquireAsync();
|
||||
await fixupPackageJson();
|
||||
|
||||
try {
|
||||
logger.info(`installing package for extension "${name}"`);
|
||||
await forkNpm(...baseNpmInstallArgs, name);
|
||||
logger.info(`installed package for extension "${name}"`);
|
||||
} finally {
|
||||
installLock.release();
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default installExtensionInjectable;
|
||||
@ -46,9 +46,7 @@ export async function bootstrap(di: DiContainer) {
|
||||
}
|
||||
|
||||
try {
|
||||
await initializeApp(() => {
|
||||
unmountComponentAtNode(rootElem);
|
||||
});
|
||||
await initializeApp(() => unmountComponentAtNode(rootElem));
|
||||
} catch (error) {
|
||||
console.error(`[BOOTSTRAP]: view initialization error: ${error}`, {
|
||||
origin: location.href,
|
||||
|
||||
@ -12,7 +12,6 @@ import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.
|
||||
import loadExtensionsInjectable from "../../load-extensions.injectable";
|
||||
import loggerInjectable from "../../../../common/logger.injectable";
|
||||
import showErrorNotificationInjectable from "../../../components/notifications/show-error-notification.injectable";
|
||||
import closeRendererLogFileInjectable from "../../../logger/close-renderer-log-file.injectable";
|
||||
|
||||
const initClusterFrameInjectable = getInjectable({
|
||||
id: "init-cluster-frame",
|
||||
@ -30,7 +29,6 @@ const initClusterFrameInjectable = getInjectable({
|
||||
emitAppEvent: di.inject(emitAppEventInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
showErrorNotification: di.inject(showErrorNotificationInjectable),
|
||||
closeFileLogging: di.inject(closeRendererLogFileInjectable),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { once } from "lodash";
|
||||
import type { Cluster } from "../../../../common/cluster/cluster";
|
||||
import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry";
|
||||
import type { ShowNotification } from "../../../components/notifications";
|
||||
@ -19,7 +18,6 @@ interface Dependencies {
|
||||
emitAppEvent: EmitAppEvent;
|
||||
logger: Logger;
|
||||
showErrorNotification: ShowNotification;
|
||||
closeFileLogging: () => void;
|
||||
}
|
||||
|
||||
const logPrefix = "[CLUSTER-FRAME]:";
|
||||
@ -32,7 +30,6 @@ export const initClusterFrame = ({
|
||||
emitAppEvent,
|
||||
logger,
|
||||
showErrorNotification,
|
||||
closeFileLogging,
|
||||
}: Dependencies) =>
|
||||
async (unmountRoot: () => void) => {
|
||||
// TODO: Make catalogEntityRegistry already initialized when passed as dependency
|
||||
@ -76,14 +73,11 @@ export const initClusterFrame = ({
|
||||
});
|
||||
});
|
||||
|
||||
const onCloseFrame = once(() => {
|
||||
window.onbeforeunload = () => {
|
||||
logger.info(
|
||||
`${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`,
|
||||
);
|
||||
closeFileLogging();
|
||||
unmountRoot();
|
||||
});
|
||||
|
||||
window.addEventListener("beforeunload", onCloseFrame);
|
||||
window.addEventListener("pagehide", onCloseFrame);
|
||||
unmountRoot();
|
||||
};
|
||||
};
|
||||
|
||||
@ -13,7 +13,6 @@ import loggerInjectable from "../../../common/logger.injectable";
|
||||
import { delay } from "../../../common/utils";
|
||||
import { broadcastMessage } from "../../../common/ipc";
|
||||
import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling";
|
||||
import closeRendererLogFileInjectable from "../../logger/close-renderer-log-file.injectable";
|
||||
|
||||
const initRootFrameInjectable = getInjectable({
|
||||
id: "init-root-frame",
|
||||
@ -25,7 +24,6 @@ const initRootFrameInjectable = getInjectable({
|
||||
const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable);
|
||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const closeRendererLogFile = di.inject(closeRendererLogFileInjectable);
|
||||
|
||||
return async (unmountRoot: () => void) => {
|
||||
catalogEntityRegistry.init();
|
||||
@ -61,7 +59,7 @@ const initRootFrameInjectable = getInjectable({
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
logger.info("[ROOT-FRAME]: Unload app");
|
||||
closeRendererLogFile();
|
||||
|
||||
unmountRoot();
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,22 +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 winstonLoggerInjectable from "../../common/winston-logger.injectable";
|
||||
import rendererFileLoggerTransportInjectable from "./file-transport.injectable";
|
||||
|
||||
const closeRendererLogFileInjectable = getInjectable({
|
||||
id: "close-renderer-log-file",
|
||||
instantiate: (di) => {
|
||||
const winstonLogger = di.inject(winstonLoggerInjectable);
|
||||
const fileLoggingTransport = di.inject(rendererFileLoggerTransportInjectable);
|
||||
|
||||
return () => {
|
||||
fileLoggingTransport.close?.();
|
||||
winstonLogger.remove(fileLoggingTransport);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default closeRendererLogFileInjectable;
|
||||
@ -1,44 +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 { transports } from "winston";
|
||||
import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
|
||||
import { loggerTransportInjectionToken } from "../../common/logger/transports";
|
||||
import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
|
||||
import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
|
||||
import { getClusterIdFromHost } from "../utils";
|
||||
|
||||
const rendererFileLoggerTransportInjectable = getInjectable({
|
||||
id: "renderer-file-logger-transport",
|
||||
instantiate: (di) => {
|
||||
let frameId: string;
|
||||
|
||||
const currentlyInClusterFrame = di.inject(
|
||||
currentlyInClusterFrameInjectable,
|
||||
);
|
||||
|
||||
if (currentlyInClusterFrame) {
|
||||
const { host } = di.inject(windowLocationInjectable);
|
||||
const clusterId = getClusterIdFromHost(host);
|
||||
|
||||
frameId = clusterId ? `cluster-${clusterId}` : "cluster";
|
||||
} else {
|
||||
frameId = "main";
|
||||
}
|
||||
|
||||
return new transports.File({
|
||||
handleExceptions: false,
|
||||
level: "info",
|
||||
filename: `lens-renderer-${frameId}.log`,
|
||||
dirname: di.inject(directoryForLogsInjectable),
|
||||
maxsize: 1024 * 1024,
|
||||
maxFiles: 2,
|
||||
tailable: true,
|
||||
});
|
||||
},
|
||||
injectionToken: loggerTransportInjectionToken,
|
||||
});
|
||||
|
||||
export default rendererFileLoggerTransportInjectable;
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "@k8slens/extensions",
|
||||
"productName": "OpenLens extensions",
|
||||
"description": "OpenLens - Open Source Kubernetes IDE: extensions",
|
||||
"version": "6.4.0-beta.17",
|
||||
"version": "6.4.0",
|
||||
"copyright": "© 2022 OpenLens Authors",
|
||||
"license": "MIT",
|
||||
"main": "dist/extension-api.js",
|
||||
@ -26,7 +26,7 @@
|
||||
"prepare:dev": "yarn run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@k8slens/core": "^6.4.0-beta.17"
|
||||
"@k8slens/core": "^6.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.18.6",
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"productName": "OpenLens",
|
||||
"description": "OpenLens - Open Source IDE for Kubernetes",
|
||||
"homepage": "https://github.com/lensapp/lens",
|
||||
"version": "6.4.0-beta.17",
|
||||
"version": "6.4.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lensapp/lens.git"
|
||||
@ -192,7 +192,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@k8slens/core": "^6.4.0-beta.17",
|
||||
"@k8slens/core": "^6.4.0",
|
||||
"@k8slens/ensure-binaries": "^6.4.0-beta.16",
|
||||
"@k8slens/generate-tray-icons": "^6.4.0-beta.16",
|
||||
"@ogre-tools/fp": "^12.0.1",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@k8slens/release-tool",
|
||||
"version": "6.4.0-beta.17",
|
||||
"version": "6.4.0",
|
||||
"description": "Release tool for lens monorepo",
|
||||
"main": "dist/index.mjs",
|
||||
"license": "MIT",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
*/
|
||||
import assert from "assert";
|
||||
import chalk from "chalk";
|
||||
import child_process from "child_process";
|
||||
import child_process, { spawn } from "child_process";
|
||||
import { readFile } from "fs/promises";
|
||||
import inquirer from "inquirer";
|
||||
import { createInterface, ReadLine } from "readline";
|
||||
@ -16,9 +16,21 @@ type SemVer = semver.SemVer;
|
||||
|
||||
const { SemVer } = semver;
|
||||
const exec = promisify(child_process.exec);
|
||||
const spawn = promisify(child_process.spawn);
|
||||
const execFile = promisify(child_process.execFile);
|
||||
|
||||
async function pipeExecFile(file: string, args: string[], opts?: { stdin: string }) {
|
||||
const p = execFile(file, args);
|
||||
|
||||
p.child.stdout?.pipe(process.stdout);
|
||||
p.child.stderr?.pipe(process.stderr);
|
||||
|
||||
if (opts) {
|
||||
p.child.stdin?.end(opts.stdin);
|
||||
}
|
||||
|
||||
await p;
|
||||
}
|
||||
|
||||
interface GithubPrData {
|
||||
author: {
|
||||
login: string;
|
||||
@ -65,9 +77,35 @@ async function fetchAllGitTags(): Promise<string[]> {
|
||||
.map(line => line.trim());
|
||||
}
|
||||
|
||||
async function bumpPackageVersions(): Promise<void> {
|
||||
await spawn("npm", ["run", "bump-version"], {
|
||||
stdio: "inherit",
|
||||
function bumpPackageVersions() {
|
||||
const bumpPackages = spawn("npm", ["run", "bump-version"], {
|
||||
stdio: "inherit"
|
||||
});
|
||||
const cleaners: (() => void)[] = [
|
||||
() => bumpPackages.stdout?.unpipe(),
|
||||
() => bumpPackages.stderr?.unpipe(),
|
||||
];
|
||||
const cleanup = () => cleaners.forEach(clean => clean());
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const onExit = (code: number | null) => {
|
||||
cleanup();
|
||||
if (code) {
|
||||
reject(new Error(`"npm run bump-version" failed with code ${code}`));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
const onError = (error: Error) => {
|
||||
cleanup();
|
||||
reject(error);
|
||||
};
|
||||
|
||||
bumpPackages.once("error", onError);
|
||||
cleaners.push(() => bumpPackages.off("error", onError));
|
||||
|
||||
bumpPackages.once("exit", onExit);
|
||||
cleaners.push(() => bumpPackages.off("exit", onExit));
|
||||
});
|
||||
}
|
||||
|
||||
@ -110,20 +148,12 @@ function formatSemverForMilestone(version: SemVer): string {
|
||||
async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prBody: string): Promise<void> {
|
||||
const prBranch = `release/v${version.format()}`;
|
||||
|
||||
await spawn("git", ["checkout", "-b", prBranch], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
await spawn("git", ["add", "lerna.json", "packages/*/package.json"], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
await spawn("git", ["commit", "-sm", `"Release ${version.format()}"`], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
await spawn("git", ["push", "--set-upstream", "origin", prBranch], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
await pipeExecFile("git", ["checkout", "-b", prBranch]);
|
||||
await pipeExecFile("git", ["add", "lerna.json", "packages/*/package.json"]);
|
||||
await pipeExecFile("git", ["commit", "-sm", `Release ${version.format()}`]);
|
||||
await pipeExecFile("git", ["push", "--set-upstream", "origin", prBranch]);
|
||||
|
||||
await spawn("gh", [
|
||||
await pipeExecFile("gh", [
|
||||
"pr",
|
||||
"create",
|
||||
"--base", prBase,
|
||||
@ -131,9 +161,9 @@ async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prB
|
||||
"--label", "skip-changelog",
|
||||
"--label", "release",
|
||||
"--milestone", formatSemverForMilestone(version),
|
||||
"--body-file", prBody,
|
||||
"--body-file", "-",
|
||||
], {
|
||||
stdio: "inherit"
|
||||
stdin: prBody,
|
||||
});
|
||||
}
|
||||
|
||||
@ -153,7 +183,7 @@ function sortExtendedGithubPrData(left: ExtendedGithubPrData, right: ExtendedGit
|
||||
}
|
||||
|
||||
async function getRelevantPRs(milestone: string, previousReleasedVersion: string): Promise<ExtendedGithubPrData[]> {
|
||||
console.log("retreiving previous 500 PRs...");
|
||||
console.log("retrieving previous 200 PRs...");
|
||||
|
||||
const getMergedPrsArgs = [
|
||||
"gh",
|
||||
@ -167,14 +197,14 @@ async function getRelevantPRs(milestone: string, previousReleasedVersion: string
|
||||
|
||||
const mergedPrs = JSON.parse((await exec(getMergedPrsArgs.join(" "), { encoding: "utf-8" })).stdout) as GithubPrData[];
|
||||
const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone?.title === milestone);
|
||||
const relaventPrsQuery = await Promise.all(
|
||||
const relevantPrsQuery = await Promise.all(
|
||||
milestoneRelevantPrs.map(async pr => ({
|
||||
pr,
|
||||
stdout: (await exec(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout,
|
||||
})),
|
||||
);
|
||||
|
||||
return relaventPrsQuery
|
||||
return relevantPrsQuery
|
||||
.filter(query => query.stdout)
|
||||
.map(query => query.pr)
|
||||
.filter(pr => pr.labels.every(label => label.name !== "skip-changelog"))
|
||||
@ -189,40 +219,11 @@ function formatPrEntry(pr: ExtendedGithubPrData) {
|
||||
const isEnhancementPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "enhancement");
|
||||
const isBugfixPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "bug");
|
||||
|
||||
const cherrypickCommitWith = (rl: ReadLine) => async (commit: string) => {
|
||||
const cherryPickCommitWith = (rl: ReadLine) => async (commit: string) => {
|
||||
try {
|
||||
const cherryPick = child_process.spawn("git", ["cherry-pick", commit]);
|
||||
|
||||
cherryPick.stdout.pipe(process.stdout);
|
||||
cherryPick.stderr.pipe(process.stderr);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const cleaners: (() => void)[] = [];
|
||||
const cleanup = () => cleaners.forEach(cleaner => cleaner());
|
||||
|
||||
const onExit = (code: number | null) => {
|
||||
if (code) {
|
||||
reject(new Error(`git cherry-pick failed with exit code ${code}`));
|
||||
cleanup();
|
||||
}
|
||||
|
||||
resolve();
|
||||
cleanup();
|
||||
};
|
||||
|
||||
cherryPick.once("exit", onExit);
|
||||
cleaners.push(() => cherryPick.off("exit", onExit));
|
||||
|
||||
const onError = (error: Error) => {
|
||||
cleanup();
|
||||
reject(error);
|
||||
};
|
||||
|
||||
cherryPick.once("error", onError);
|
||||
cleaners.push(() => cherryPick.off("error", onError));
|
||||
});
|
||||
await pipeExecFile("git", ["cherry-pick", commit]);
|
||||
} catch {
|
||||
console.error(chalk.bold("Please resolve conflicts in a seperate terminal and then press enter here..."));
|
||||
console.error(chalk.bold("Please resolve conflicts in a separate terminal and then press enter here..."));
|
||||
await new Promise<void>(resolve => rl.once("line", () => resolve()));
|
||||
}
|
||||
};
|
||||
@ -249,7 +250,7 @@ async function pickWhichPRsToUse(prs: ExtendedGithubPrData[]): Promise<ExtendedG
|
||||
function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrData[]): string {
|
||||
const enhancementPrLines: string[] = [];
|
||||
const bugPrLines: string[] = [];
|
||||
const maintenencePrLines: string[] = [];
|
||||
const maintenancePrLines: string[] = [];
|
||||
|
||||
for (const pr of prs) {
|
||||
if (isEnhancementPr(pr)) {
|
||||
@ -257,7 +258,7 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD
|
||||
} else if (isBugfixPr(pr)) {
|
||||
bugPrLines.push(formatPrEntry(pr));
|
||||
} else {
|
||||
maintenencePrLines.push(formatPrEntry(pr));
|
||||
maintenancePrLines.push(formatPrEntry(pr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,9 +272,9 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD
|
||||
bugPrLines.push("");
|
||||
}
|
||||
|
||||
if (maintenencePrLines.length > 0) {
|
||||
maintenencePrLines.unshift("## 🧰 Maintenance", "");
|
||||
maintenencePrLines.push("");
|
||||
if (maintenancePrLines.length > 0) {
|
||||
maintenancePrLines.unshift("## 🧰 Maintenance", "");
|
||||
maintenancePrLines.push("");
|
||||
}
|
||||
|
||||
return [
|
||||
@ -281,22 +282,22 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD
|
||||
"",
|
||||
...enhancementPrLines,
|
||||
...bugPrLines,
|
||||
...maintenencePrLines,
|
||||
...maintenancePrLines,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
async function cherrypickCommits(prs: ExtendedGithubPrData[]): Promise<void> {
|
||||
async function cherryPickCommits(prs: ExtendedGithubPrData[]): Promise<void> {
|
||||
const rl = createInterface(process.stdin);
|
||||
const cherrypickCommit = cherrypickCommitWith(rl);
|
||||
const cherryPickCommit = cherryPickCommitWith(rl);
|
||||
|
||||
for (const pr of prs) {
|
||||
await cherrypickCommit(pr.mergeCommit.oid);
|
||||
await cherryPickCommit(pr.mergeCommit.oid);
|
||||
}
|
||||
|
||||
rl.close();
|
||||
}
|
||||
|
||||
async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise<ExtendedGithubPrData[]> {
|
||||
async function pickRelevantPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise<ExtendedGithubPrData[]> {
|
||||
if (isMasterBranch) {
|
||||
return prs;
|
||||
}
|
||||
@ -307,7 +308,7 @@ async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: bool
|
||||
selectedPrs = await pickWhichPRsToUse(prs);
|
||||
} while (selectedPrs.length === 0 && (console.warn("[WARNING]: must pick at least once commit"), true));
|
||||
|
||||
await cherrypickCommits(selectedPrs);
|
||||
await cherryPickCommits(selectedPrs);
|
||||
|
||||
return selectedPrs;
|
||||
}
|
||||
@ -326,8 +327,8 @@ async function createRelease(): Promise<void> {
|
||||
}
|
||||
|
||||
const prMilestone = formatSemverForMilestone(await getCurrentVersionOfSubPackage("core"));
|
||||
const relaventPrs = await getRelevantPRs(prMilestone, previousReleasedVersion);
|
||||
const selectedPrs = await pickRelaventPrs(relaventPrs, isMasterBranch);
|
||||
const relevantPrs = await getRelevantPRs(prMilestone, previousReleasedVersion);
|
||||
const selectedPrs = await pickRelevantPrs(relevantPrs, isMasterBranch);
|
||||
const prBody = formatChangelog(previousReleasedVersion, selectedPrs);
|
||||
|
||||
if (!isMasterBranch) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user