Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

- [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/)

## 1.60.0 -

<a name="breaking_changes_1.61.0">[Breaking Changes:](#breaking_changes_1.61.0)</a>

- [core] allow to disable plugins. The PR includes a couple of renamings: `HostedPluginDeployerHandler` => `PluginDeployerHandlerImpl` and
`PluginServerHandler` => `PluginServerImpl`. Also removed the ability of `HostedPluginProcess` to add extra deployed plugins. [#15205](https://github.com/eclipse-theia/theia/pull/15205) - contributed on behalf of STMicroelectronics

## 1.60.0 - 04/03/2025

- [ai] add dummy preference descriptions to open AI config widget [#15166](https://github.com/eclipse-theia/theia/pull/15166)
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/node/backend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { BackendRequestFacade } from './request/backend-request-facade';
import { FileSystemLocking, FileSystemLockingImpl } from './filesystem-locking';
import { BackendRemoteService } from './remote/backend-remote-service';
import { RemoteCliContribution } from './remote/remote-cli-contribution';
import { SettingService, SettingServiceImpl } from './setting-service';

decorate(injectable(), ApplicationPackage);

Expand Down Expand Up @@ -136,4 +137,7 @@ export const backendApplicationModule = new ContainerModule(bind => {
bindBackendStopwatchServer(bind);

bind(FileSystemLocking).to(FileSystemLockingImpl).inSingletonScope();

bind(SettingServiceImpl).toSelf().inSingletonScope();
bind(SettingService).toService(SettingServiceImpl);
});
1 change: 1 addition & 0 deletions packages/core/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export * from './debug';
export * from '../common/file-uri';
export * from './messaging';
export * from './cli';
export * from './setting-service';
export { FileSystemLocking } from './filesystem-locking';
78 changes: 78 additions & 0 deletions packages/core/src/node/setting-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// *****************************************************************************
// Copyright (C) 2025 STMicroelectronics and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { inject, injectable, postConstruct } from 'inversify';
import { EnvVariablesServer } from '../common/env-variables';
import { Deferred } from '../common/promise-util';
import { promises as fs } from 'fs';
import { URI } from '../common';

export const SettingService = Symbol('SettingService');

/**
* A service providing a simple user-level, persistent key-value store on the back end
*/
export interface SettingService {
set(key: string, value: string): Promise<void>;
get(key: string): Promise<string | undefined>;
}

@injectable()
export class SettingServiceImpl implements SettingService {

@inject(EnvVariablesServer)
protected readonly envVarServer: EnvVariablesServer;

protected readonly ready = new Deferred<void>();
protected values: Record<string, string> = {};

@postConstruct()
protected init(): void {
const asyncInit = async () => {
const configDir = new URI(await this.envVarServer.getConfigDirUri());
const path: string = configDir.resolve('backend-settings.json').path.fsPath();
try {
const contents = await fs.readFile(path, {
encoding: 'utf-8'
});
this.values = JSON.parse(contents);
} catch (e) {
console.log(e);
} finally {
this.ready.resolve();
}
};
asyncInit();
}

async set(key: string, value: string): Promise<void> {
await this.ready.promise;
this.values[key] = value;
await this.writeFile();
}

async writeFile(): Promise<void> {
const configDir = new URI(await this.envVarServer.getConfigDirUri());
const path: string = configDir.resolve('backend-settings.json').path.fsPath();
const values = JSON.stringify(this.values);
await fs.writeFile(path, values);
}

async get(key: string): Promise<string | undefined> {
await this.ready.promise;
return this.values[key];
}
}
7 changes: 3 additions & 4 deletions packages/plugin-dev/src/node/hosted-plugin-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { HostedPluginReader as PluginReaderHosted } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { PluginMetadata } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { PluginDeployerHandler, PluginMetadata } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { PluginDeployerEntryImpl } from '@theia/plugin-ext/lib/main/node/plugin-deployer-entry-impl';
import { HostedPluginDeployerHandler } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-deployer-handler';

@injectable()
export class HostedPluginReader implements BackendApplicationContribution {
Expand All @@ -30,8 +29,8 @@ export class HostedPluginReader implements BackendApplicationContribution {

private readonly hostedPlugin = new Deferred<PluginMetadata | undefined>();

@inject(HostedPluginDeployerHandler)
protected deployerHandler: HostedPluginDeployerHandler;
@inject(PluginDeployerHandler)
protected deployerHandler: PluginDeployerHandler;

async initialize(): Promise<void> {
this.pluginReader.getPluginMetadata(process.env.HOSTED_PLUGIN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ export class PluginVscodeCommandsContribution implements CommandContribution {
commands.registerCommand(VscodeCommands.INSTALL_EXTENSION_FROM_ID_OR_URI, {
execute: async (vsixUriOrExtensionId: TheiaURI | UriComponents | string) => {
if (typeof vsixUriOrExtensionId === 'string') {
await this.pluginServer.deploy(VSCodeExtensionUri.fromId(vsixUriOrExtensionId).toString());
await this.pluginServer.install(VSCodeExtensionUri.fromId(vsixUriOrExtensionId).toString());
} else {
await this.deployPlugin(vsixUriOrExtensionId);
}
Expand Down Expand Up @@ -1001,7 +1001,7 @@ export class PluginVscodeCommandsContribution implements CommandContribution {

private async deployPlugin(uri: TheiaURI | UriComponents): Promise<void> {
const uriPath = isUriComponents(uri) ? URI.revive(uri).fsPath : await this.fileService.fsPath(uri);
return this.pluginServer.deploy(`local-file:${uriPath}`);
return this.pluginServer.install(`local-file:${uriPath}`);
}

private async resolveLanguageId(resource: URI): Promise<string> {
Expand Down
7 changes: 4 additions & 3 deletions packages/plugin-ext/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ The implementation is inspired from: <https://blog.mattbierner.com/vscode-webvie
When you change the host pattern via the `THEIA_WEBVIEW_EXTERNAL_ENDPOINT` environment variable warning will be emitted both from the frontend and from the backend.
You can disable those warnings by setting `warnOnPotentiallyInsecureHostPattern: false` in the appropriate application configurations in your application's `package.json`.

## Runtime System Plugin Resolvement
## Naming in this package

The backend application property `resolveSystemPlugins` is used to control the resolvement behavior for system plugins (builtins).
The property is used to control whether or not extension-packs and extension-dependencies are resolved at runtime.
This package has a different folder structure than other Theia packages. Stuff in the "hosted" folder is meant to be scoped to a front end,
whereas "main" is global to a back end instance. Code in "plugin" runs inside the plugin host process. But be aware that this is not always the case,
for example the plugin manifest scanners (e.g. `scanner-theia.ts`) are in the `hosted` folder, even though they a global concern.

## Additional Information

Expand Down
48 changes: 28 additions & 20 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@ export const PluginDeployerHandler = Symbol('PluginDeployerHandler');
export interface PluginDeployerHandler {
deployFrontendPlugins(frontendPlugins: PluginDeployerEntry[]): Promise<number | undefined>;
deployBackendPlugins(backendPlugins: PluginDeployerEntry[]): Promise<number | undefined>;
getDeployedPluginIds(): Promise<readonly PluginIdentifiers.VersionedId[]>;

getDeployedPlugins(): Promise<DeployedPlugin[]>;
getDeployedPluginsById(pluginId: string): DeployedPlugin[];
Expand All @@ -995,17 +996,30 @@ export interface PluginDeployerHandler {
* Unless `--uncompressed-plugins-in-place` is passed to the CLI, this operation is safe.
*/
uninstallPlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;

/**
* Removes the plugin from the locations to which it had been deployed.
* This operation is not safe - references to deleted assets may remain.
*/
undeployPlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;

getPluginDependencies(pluginToBeInstalled: PluginDeployerEntry): Promise<PluginDependencies | undefined>;
}

export interface GetDeployedPluginsParams {
pluginIds: PluginIdentifiers.VersionedId[]
/**
* Marks the given plugins as "disabled". While the plugin remains installed, it will no longer
* be used. Has no effect if the plugin is not installed
* @param pluginId the plugin to disable
* @returns whether the plugin was installed, enabled and could be disabled
*/
disablePlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;

/**
* Marks the given plugins as "enabled". Has no effect if the plugin is not installed.
* @param pluginId the plugin to enabled
* @returns whether the plugin was installed, disabled and could be enabled
*/
enablePlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;

}

export interface DeployedPlugin {
Expand All @@ -1024,7 +1038,9 @@ export interface HostedPluginServer extends RpcServer<HostedPluginClient> {

getUninstalledPluginIds(): Promise<readonly PluginIdentifiers.VersionedId[]>;

getDeployedPlugins(params: GetDeployedPluginsParams): Promise<DeployedPlugin[]>;
getDisabledPluginIds(): Promise<readonly PluginIdentifiers.VersionedId[]>;

getDeployedPlugins(ids: PluginIdentifiers.VersionedId[]): Promise<DeployedPlugin[]>;

getExtPluginAPI(): Promise<ExtPluginApi[]>;

Expand All @@ -1047,9 +1063,6 @@ export interface PluginDeployOptions {
ignoreOtherVersions?: boolean;
}

/**
* The JSON-RPC workspace interface.
*/
export const pluginServerJsonRpcPath = '/services/plugin-ext';
export const PluginServer = Symbol('PluginServer');
export interface PluginServer {
Expand All @@ -1059,9 +1072,15 @@ export interface PluginServer {
*
* @param type whether a plugin is installed by a system or a user, defaults to a user
*/
deploy(pluginEntry: string, type?: PluginType, options?: PluginDeployOptions): Promise<void>;
install(pluginEntry: string, type?: PluginType, options?: PluginDeployOptions): Promise<void>;
uninstall(pluginId: PluginIdentifiers.VersionedId): Promise<void>;
undeploy(pluginId: PluginIdentifiers.VersionedId): Promise<void>;

enablePlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;
disablePlugin(pluginId: PluginIdentifiers.VersionedId): Promise<boolean>;

getInstalledPlugins(): Promise<readonly PluginIdentifiers.VersionedId[]>;
getUninstalledPlugins(): Promise<readonly PluginIdentifiers.VersionedId[]>;
getDisabledPlugins(): Promise<readonly PluginIdentifiers.VersionedId[]>;

setStorageValue(key: string, value: KeysToAnyValues, kind: PluginStorageKind): Promise<boolean>;
getStorageValue(key: string, kind: PluginStorageKind): Promise<KeysToAnyValues>;
Expand All @@ -1077,17 +1096,6 @@ export interface ServerPluginRunner {
setClient(client: HostedPluginClient): void;
setDefault(defaultRunner: ServerPluginRunner): void;
clientClosed(): void;

/**
* Provides additional deployed plugins.
*/
getExtraDeployedPlugins(): Promise<DeployedPlugin[]>;

/**
* Provides additional plugin ids.
*/
getExtraDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]>;

}

export const PluginHostEnvironmentVariable = Symbol('PluginHostEnvironmentVariable');
Expand Down
11 changes: 7 additions & 4 deletions packages/plugin-ext/src/hosted/common/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ export abstract class AbstractHostedPluginSupport<PM extends AbstractPluginManag
let didChangeInstallationStatus = false;
try {
const newPluginIds: PluginIdentifiers.VersionedId[] = [];
const [deployedPluginIds, uninstalledPluginIds] = await Promise.all([this.server.getDeployedPluginIds(), this.server.getUninstalledPluginIds()]);
const [deployedPluginIds, uninstalledPluginIds, disabledPlugins] = await Promise.all(
[this.server.getDeployedPluginIds(), this.server.getUninstalledPluginIds(), this.server.getDisabledPluginIds()]);

const ignoredPlugins = [...disabledPlugins, ...uninstalledPluginIds];
waitPluginsMeasurement.log('Waiting for backend deployment');
syncPluginsMeasurement = this.measure('syncPlugins');
for (const versionedId of deployedPluginIds) {
Expand All @@ -221,20 +224,20 @@ export abstract class AbstractHostedPluginSupport<PM extends AbstractPluginManag
for (const pluginId of toUnload) {
this.contributions.get(pluginId)?.dispose();
}
for (const versionedId of uninstalledPluginIds) {
for (const versionedId of ignoredPlugins) {
const plugin = this.getPlugin(PluginIdentifiers.unversionedFromVersioned(versionedId));
if (plugin && PluginIdentifiers.componentsToVersionedId(plugin.metadata.model) === versionedId && !plugin.metadata.outOfSync) {
plugin.metadata.outOfSync = didChangeInstallationStatus = true;
}
}
for (const contribution of this.contributions.values()) {
if (contribution.plugin.metadata.outOfSync && !uninstalledPluginIds.includes(PluginIdentifiers.componentsToVersionedId(contribution.plugin.metadata.model))) {
if (contribution.plugin.metadata.outOfSync && !ignoredPlugins.includes(PluginIdentifiers.componentsToVersionedId(contribution.plugin.metadata.model))) {
contribution.plugin.metadata.outOfSync = false;
didChangeInstallationStatus = true;
}
}
if (newPluginIds.length) {
const deployedPlugins = await this.server.getDeployedPlugins({ pluginIds: newPluginIds });
const deployedPlugins = await this.server.getDeployedPlugins(newPluginIds);

const plugins: DeployedPlugin[] = [];
for (const plugin of deployedPlugins) {
Expand Down
16 changes: 1 addition & 15 deletions packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { createIpcEnv } from '@theia/core/lib/node/messaging/ipc-protocol';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import * as cp from 'child_process';
import { Duplex } from 'stream';
import { DeployedPlugin, HostedPluginClient, PLUGIN_HOST_BACKEND, PluginHostEnvironmentVariable, PluginIdentifiers, ServerPluginRunner } from '../../common/plugin-protocol';
import { HostedPluginClient, PLUGIN_HOST_BACKEND, PluginHostEnvironmentVariable, ServerPluginRunner } from '../../common/plugin-protocol';
import { HostedPluginCliContribution } from './hosted-plugin-cli-contribution';
import { HostedPluginLocalizationService } from './hosted-plugin-localization-service';
import { ProcessTerminateMessage, ProcessTerminatedMessage } from './hosted-plugin-protocol';
Expand Down Expand Up @@ -230,18 +230,4 @@ export class HostedPluginProcess implements ServerPluginRunner {
this.logger.error(`Error from plugin host: ${err.message}`);
}

/**
* Provides additional plugin ids.
*/
public async getExtraDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
return [];
}

/**
* Provides additional deployed plugins.
*/
public async getExtraDeployedPlugins(): Promise<DeployedPlugin[]> {
return [];
}

}
16 changes: 1 addition & 15 deletions packages/plugin-ext/src/hosted/node/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { injectable, inject, multiInject, postConstruct, optional } from '@theia/core/shared/inversify';
import { ILogger, ConnectionErrorHandler } from '@theia/core/lib/common';
import { HostedPluginClient, PluginModel, ServerPluginRunner, DeployedPlugin, PluginIdentifiers } from '../../common/plugin-protocol';
import { HostedPluginClient, PluginModel, ServerPluginRunner } from '../../common/plugin-protocol';
import { LogPart } from '../../common/types';
import { HostedPluginProcess } from './hosted-plugin-process';

Expand Down Expand Up @@ -92,20 +92,6 @@ export class HostedPluginSupport {
}
}

/**
* Provides additional plugin ids.
*/
async getExtraDeployedPluginIds(): Promise<PluginIdentifiers.VersionedId[]> {
return [].concat.apply([], await Promise.all(this.pluginRunners.map(runner => runner.getExtraDeployedPluginIds())));
}

/**
* Provides additional deployed plugins.
*/
async getExtraDeployedPlugins(): Promise<DeployedPlugin[]> {
return [].concat.apply([], await Promise.all(this.pluginRunners.map(runner => runner.getExtraDeployedPlugins())));
}

sendLog(logPart: LogPart): void {
this.client.log(logPart);
}
Expand Down
5 changes: 3 additions & 2 deletions packages/plugin-ext/src/hosted/node/metadata-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ export class MetadataScanner {
});
}

getPluginMetadata(plugin: PluginPackage): PluginMetadata {
async getPluginMetadata(plugin: PluginPackage): Promise<PluginMetadata> {
const scanner = this.getScanner(plugin);
const id = PluginIdentifiers.componentsToVersionedId(plugin);
return {
host: PLUGIN_HOST_BACKEND,
model: scanner.getModel(plugin),
lifecycle: scanner.getLifecycle(plugin),
outOfSync: this.uninstallationManager.isUninstalled(PluginIdentifiers.componentsToVersionedId(plugin)),
outOfSync: this.uninstallationManager.isUninstalled(id) || await this.uninstallationManager.isDisabled(id),
};
}

Expand Down
Loading
Loading