Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a command to list installed plugins. #12818

Merged
merged 1 commit into from
Nov 24, 2023
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
35 changes: 24 additions & 11 deletions dev-packages/application-manager/src/generator/backend-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ ${Array.from(electronMainModules?.values() ?? [], jsModulePath => `\
await load(require('${jsModulePath}'));`).join(EOL)}
await start();
} catch (reason) {
console.error('Failed to start the electron application.');
if (reason) {
console.error(reason);
if (typeof reason !== 'number') {
console.error('Failed to start the electron application.');
if (reason) {
console.error(reason);
}
}
app.quit();
};
Expand Down Expand Up @@ -139,8 +141,17 @@ async function start(port, host, argv = process.argv) {
if (!container.isBound(BackendApplicationServer)) {
container.bind(BackendApplicationServer).toConstantValue({ configure: defaultServeStatic });
}
await container.get(CliManager).initializeCli(argv);
return container.get(BackendApplication).start(port, host);
let result = undefined;
await container.get(CliManager).initializeCli(argv.slice(2),
() => container.get(BackendApplication).configured,
async () => {
result = container.get(BackendApplication).start(port, host);
});
if (result) {
return result;
} else {
return Promise.reject(0);
}
}

module.exports = async (port, host, argv) => {
Expand All @@ -149,9 +160,11 @@ ${Array.from(backendModules.values(), jsModulePath => `\
await load(require('${jsModulePath}'));`).join(EOL)}
return await start(port, host, argv);
} catch (error) {
console.error('Failed to start the backend application:');
console.error(error);
process.exitCode = 1;
if (typeof error !== 'number') {
console.error('Failed to start the backend application:');
console.error(error);
process.exitCode = 1;
}
throw error;
}
}
Expand All @@ -168,9 +181,9 @@ BackendApplicationConfigProvider.set(${this.prettyStringify(this.pck.props.backe
const serverModule = require('./server');
const serverAddress = main.start(serverModule());

serverAddress.then(({ port, address, family }) => {
if (process && process.send) {
process.send({ port, address, family });
serverAddress.then((addressInfo) => {
if (process && process.send && addressInfo) {
process.send(addressInfo);
}
});

Expand Down
38 changes: 30 additions & 8 deletions examples/api-samples/src/node/sample-mock-open-vsx-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { OVSXMockClient, VSXExtensionRaw } from '@theia/ovsx-client';
import * as path from 'path';
import { SampleAppInfo } from '../common/vsx/sample-app-info';
import * as http from 'http';
import * as https from 'https';
import { Deferred } from '@theia/core/lib/common/promise-util';

type VersionedId = `${string}.${string}@${string}`;

Expand All @@ -35,6 +38,16 @@ export class SampleMockOpenVsxServer implements BackendApplicationContribution {
@inject(SampleAppInfo)
protected appInfo: SampleAppInfo;

protected mockClient: OVSXMockClient;
protected staticFileHandlers: Map<string, express.RequestHandler<{
namespace: string;
name: string;
version: string;
}, express.Response>>;

private readyDeferred = new Deferred<void>();
private ready = this.readyDeferred.promise;

get mockServerPath(): string {
return '/mock-open-vsx';
}
Expand All @@ -43,23 +56,30 @@ export class SampleMockOpenVsxServer implements BackendApplicationContribution {
return '../../sample-plugins';
}

async configure(app: express.Application): Promise<void> {
async onStart?(server: http.Server | https.Server): Promise<void> {
const selfOrigin = await this.appInfo.getSelfOrigin();
const baseUrl = `${selfOrigin}${this.mockServerPath}`;
const pluginsDb = await this.findMockPlugins(this.pluginsDbPath, baseUrl);
const staticFileHandlers = new Map(Array.from(pluginsDb.entries(), ([key, value]) => [key, express.static(value.path)]));
const mockClient = new OVSXMockClient(Array.from(pluginsDb.values(), value => value.data));
this.staticFileHandlers = new Map(Array.from(pluginsDb.entries(), ([key, value]) => [key, express.static(value.path)]));
this.mockClient = new OVSXMockClient(Array.from(pluginsDb.values(), value => value.data));
this.readyDeferred.resolve();
}

async configure(app: express.Application): Promise<void> {
app.use(
this.mockServerPath + '/api',
express.Router()
.get('/-/query', async (req, res) => {
res.json(await mockClient.query(this.sanitizeQuery(req.query)));
await this.ready;
res.json(await this.mockClient.query(this.sanitizeQuery(req.query)));
})
.get('/-/search', async (req, res) => {
res.json(await mockClient.search(this.sanitizeQuery(req.query)));
await this.ready;
res.json(await this.mockClient.search(this.sanitizeQuery(req.query)));
})
.get('/:namespace', async (req, res) => {
const extensions = mockClient.extensions
await this.ready;
const extensions = this.mockClient.extensions
.filter(ext => req.params.namespace === ext.namespace)
.map(ext => `${ext.namespaceUrl}/${ext.name}`);
if (extensions.length === 0) {
Expand All @@ -72,15 +92,17 @@ export class SampleMockOpenVsxServer implements BackendApplicationContribution {
}
})
.get('/:namespace/:name', async (req, res) => {
res.json(mockClient.extensions.find(ext => req.params.namespace === ext.namespace && req.params.name === ext.name));
await this.ready;
res.json(this.mockClient.extensions.find(ext => req.params.namespace === ext.namespace && req.params.name === ext.name));
})
.get('/:namespace/:name/reviews', async (req, res) => {
res.json([]);
})
// implicitly GET/HEAD because of the express.static handlers
.use('/:namespace/:name/:version/file', async (req, res, next) => {
await this.ready;
const versionedId = this.getVersionedId(req.params.namespace, req.params.name, req.params.version);
const staticFileHandler = staticFileHandlers.get(versionedId);
const staticFileHandler = this.staticFileHandlers.get(versionedId);
if (!staticFileHandler) {
return next();
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/electron-main/electron-main-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,8 @@ export class ElectronMainApplication {
backendProcess.on('error', error => {
reject(error);
});
backendProcess.on('exit', () => {
reject(new Error('backend process exited'));
backendProcess.on('exit', code => {
reject(code);
});
app.on('quit', () => {
// Only issue a kill signal if the backend process is running.
Expand Down
25 changes: 14 additions & 11 deletions packages/core/src/node/backend-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ export class BackendApplication {
@inject(Stopwatch)
protected readonly stopwatch: Stopwatch;

private _configured: Promise<void>;

constructor(
@inject(ContributionProvider) @named(BackendApplicationContribution)
protected readonly contributionsProvider: ContributionProvider<BackendApplicationContribution>,
Expand Down Expand Up @@ -198,7 +200,7 @@ export class BackendApplication {
}

protected async initialize(): Promise<void> {
for (const contribution of this.contributionsProvider.getContributions()) {
await Promise.all(this.contributionsProvider.getContributions().map(async contribution => {
if (contribution.initialize) {
try {
await this.measure(contribution.constructor.name + '.initialize',
Expand All @@ -208,18 +210,20 @@ export class BackendApplication {
console.error('Could not initialize contribution', error);
}
}
}
}));
}

get configured(): Promise<void> {
return this._configured;
}

@postConstruct()
protected init(): void {
this.configure();
this._configured = this.configure();
}

protected async configure(): Promise<void> {
// Do not await the initialization because contributions are expected to handle
// concurrent initialize/configure in undefined order if they provide both
this.initialize();
await this.initialize();

this.app.get('*.js', this.serveGzipped.bind(this, 'text/javascript'));
this.app.get('*.js.map', this.serveGzipped.bind(this, 'application/json'));
Expand All @@ -233,17 +237,16 @@ export class BackendApplication {
this.app.get('*.woff', this.serveGzipped.bind(this, 'font/woff'));
this.app.get('*.woff2', this.serveGzipped.bind(this, 'font/woff2'));

for (const contribution of this.contributionsProvider.getContributions()) {
await Promise.all(this.contributionsProvider.getContributions().map(async contribution => {
if (contribution.configure) {
try {
await this.measure(contribution.constructor.name + '.configure',
() => contribution.configure!(this.app)
);
await contribution.configure!(this.app);
} catch (error) {
console.error('Could not configure contribution', error);
}
}
}
}));
console.info('configured all backend app contributions');
}

use(...handlers: express.Handler[]): void {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/node/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('CliManager', () => {
value.resolve(args['foo'] as string);
}
});
await manager.initializeCli(['-f', 'bla']);
await manager.initializeCli(['-f', 'bla'], () => Promise.resolve(), () => Promise.resolve());
chai.assert.equal(await value.promise, 'bla');
});

Expand All @@ -59,14 +59,14 @@ describe('CliManager', () => {
value.resolve(args['bar'] as string);
}
});
await manager.initializeCli(['--foo']);
await manager.initializeCli(['--foo'], () => Promise.resolve(), () => Promise.resolve());
chai.assert.equal(await value.promise, 'my-default');
});

it('prints help and exits', async () =>
assertExits(async () => {
const manager = new TestCliManager();
await manager.initializeCli(['--help']);
await manager.initializeCli(['--help'], () => Promise.resolve(), () => Promise.resolve());
})
);
});
Expand Down
14 changes: 9 additions & 5 deletions packages/core/src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,26 @@ export class CliManager {
constructor(@inject(ContributionProvider) @named(CliContribution)
protected readonly contributionsProvider: ContributionProvider<CliContribution>) { }

async initializeCli(argv: string[]): Promise<void> {
async initializeCli<T>(argv: string[], postSetArguments: () => Promise<void>, defaultCommand: () => Promise<void>): Promise<void> {
const pack = require('../../package.json');
const version = pack.version;
const command = yargs.version(version);
command.exitProcess(this.isExit());
for (const contrib of this.contributionsProvider.getContributions()) {
contrib.configure(command);
}
const args = command
await command
msujew marked this conversation as resolved.
Show resolved Hide resolved
.detectLocale(false)
.showHelpOnFail(false, 'Specify --help for available options')
.help('help')
.middleware(async args => {
for (const contrib of this.contributionsProvider.getContributions()) {
await contrib.setArguments(args);
}
await postSetArguments();
})
.command('$0', false, () => { }, defaultCommand)
.parse(argv);
for (const contrib of this.contributionsProvider.getContributions()) {
await contrib.setArguments(args);
}
}

protected isExit(): boolean {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/node/messaging/ipc-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function checkParentAlive(): void {
} catch {
process.exit();
}
}, 5000);
}, 5000).unref(); // we don't want this timeout to keep the process alive
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ export interface PluginDeployerStartContext {
export const PluginDeployer = Symbol('PluginDeployer');
export interface PluginDeployer {

start(): void;
start(): Promise<void>;

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
return Array.from(this.deployedBackendPlugins.keys());
}

async getDeployedBackendPlugins(): Promise<DeployedPlugin[]> {
// await first deploy
await this.backendPluginsMetadataDeferred.promise;
// fetch the last deployed state
return Array.from(this.deployedBackendPlugins.values());
}

getDeployedPluginsById(pluginId: string): DeployedPlugin[] {
const matches: DeployedPlugin[] = [];
const handle = (plugins: Iterable<DeployedPlugin>): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class PluginDeployerContribution implements BackendApplicationContributio
@inject(PluginDeployer)
protected pluginDeployer: PluginDeployer;

initialize(): void {
this.pluginDeployer.start();
initialize(): Promise<void> {
return this.pluginDeployer.start();
}
}
4 changes: 2 additions & 2 deletions packages/plugin-ext/src/main/node/plugin-deployer-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ export class PluginDeployerImpl implements PluginDeployer {
@inject(ContributionProvider) @named(PluginDeployerParticipant)
protected readonly participants: ContributionProvider<PluginDeployerParticipant>;

public start(): void {
public start(): Promise<void> {
this.logger.debug('Starting the deployer with the list of resolvers', this.pluginResolvers);
this.doStart();
return this.doStart();
}

public async initResolvers(): Promise<Array<void>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { WebviewBackendSecurityWarnings } from './webview-backend-security-warni
import { PluginUninstallationManager } from './plugin-uninstallation-manager';
import { LocalizationServerImpl } from '@theia/core/lib/node/i18n/localization-server';
import { PluginLocalizationServer } from './plugin-localization-server';
import { PluginMgmtCliContribution } from './plugin-mgmt-cli-contribution';

export function bindMainBackend(bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind): void {
bind(PluginApiContribution).toSelf().inSingletonScope();
Expand Down Expand Up @@ -85,6 +86,9 @@ export function bindMainBackend(bind: interfaces.Bind, unbind: interfaces.Unbind
bind(PluginCliContribution).toSelf().inSingletonScope();
bind(CliContribution).toService(PluginCliContribution);

bind(PluginMgmtCliContribution).toSelf().inSingletonScope();
bind(CliContribution).toService(PluginMgmtCliContribution);

bind(WebviewBackendSecurityWarnings).toSelf().inSingletonScope();
bind(BackendApplicationContribution).toService(WebviewBackendSecurityWarnings);

Expand Down
Loading
Loading