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

[plugin] Added extensionDependency automatically downloading. Fixes: #4504 #5379

Merged
merged 1 commit into from
Aug 18, 2019
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
12 changes: 11 additions & 1 deletion packages/plugin-ext-vscode/src/node/scanner-vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { TheiaPluginScanner } from '@theia/plugin-ext/lib/hosted/node/scanners/s
@injectable()
export class VsCodePluginScanner extends TheiaPluginScanner implements PluginScanner {
private readonly VSCODE_TYPE: PluginEngine = 'vscode';
private readonly VSCODE_PREFIX: string = 'vscode:extension/';

get apiType(): PluginEngine {
return this.VSCODE_TYPE;
Expand All @@ -41,7 +42,8 @@ export class VsCodePluginScanner extends TheiaPluginScanner implements PluginSca
},
entryPoint: {
backend: plugin.main
}
},
extensionDependencies: this.getDeployableDependencies(plugin.extensionDependencies || [])
};
result.contributes = this.readContributions(plugin);
return result;
Expand All @@ -55,4 +57,12 @@ export class VsCodePluginScanner extends TheiaPluginScanner implements PluginSca
backendInitPath: __dirname + '/plugin-vscode-init.js'
};
}

/**
* Converts an array of extension dependencies
* to an array of deployable extension dependencies
*/
private getDeployableDependencies(dependencies: string[]): string[] {
return dependencies.map(dep => this.VSCODE_PREFIX + dep);
}
}
5 changes: 5 additions & 0 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ export interface PluginDeployerResolverContext {

addPlugin(pluginId: string, path: string): void;

getPlugins(): PluginDeployerEntry[];

getOriginId(): string;

}
Expand Down Expand Up @@ -375,6 +377,7 @@ export interface PluginModel {
backend?: string;
};
contributes?: PluginContribution;
extensionDependencies?: string[];
}

/**
Expand Down Expand Up @@ -607,6 +610,8 @@ export const PluginDeployerHandler = Symbol('PluginDeployerHandler');
export interface PluginDeployerHandler {
deployFrontendPlugins(frontendPlugins: PluginDeployerEntry[]): Promise<void>;
deployBackendPlugins(backendPlugins: PluginDeployerEntry[]): Promise<void>;

getPluginDependencies(pluginToBeInstalled: PluginDeployerEntry): Promise<string[]>
}

export const HostedPluginServer = Symbol('HostedPluginServer');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler {
return this.currentBackendPluginsMetadata;
}

async getPluginDependencies(plugin: PluginDeployerEntry): Promise<string[]> {
const metadata = await this.reader.getPluginMetadata(plugin.path());
if (metadata) {
if (metadata.model.extensionDependencies) {
return metadata.model.extensionDependencies;
}
}
return [];
}

async deployFrontendPlugins(frontendPlugins: PluginDeployerEntry[]): Promise<void> {
for (const plugin of frontendPlugins) {
const metadata = await this.reader.getPluginMetadata(plugin.path());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const INTERNAL_CONSOLE_OPTIONS_SCHEMA = {

@injectable()
export class TheiaPluginScanner implements PluginScanner {

private readonly _apiType: PluginEngine = 'theiaPlugin';

@inject(GrammarsReader)
Expand All @@ -97,7 +98,8 @@ export class TheiaPluginScanner implements PluginScanner {
entryPoint: {
frontend: plugin.theiaPlugin!.frontend,
backend: plugin.theiaPlugin!.backend
}
},
extensionDependencies: plugin.extensionDependencies || []
};
result.contributes = this.readContributions(plugin);
return result;
Expand Down
100 changes: 59 additions & 41 deletions packages/plugin-ext/src/main/node/plugin-deployer-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ export class PluginDeployerImpl implements PluginDeployer {
@inject(PluginCliContribution)
protected readonly cliContribution: PluginCliContribution;

/**
* Deployer entries.
*/
private pluginDeployerEntries: PluginDeployerEntry[];

/**
* Inject all plugin resolvers found at runtime.
*/
Expand Down Expand Up @@ -114,25 +109,55 @@ export class PluginDeployerImpl implements PluginDeployer {
}

protected async deployMultipleEntries(pluginEntries: string[]): Promise<void> {
// resolve plugins
this.pluginDeployerEntries = await this.resolvePlugins(pluginEntries);

// now that we have plugins check if we have File Handler for them
await this.applyFileHandlers();
const deployedPlugins = new Set<string>();

/**
* Iterate over all the plugins, resolving them one at a time and adding
* in extension dependencies
*/
const futurePlugins = [];
while (pluginEntries.length !== 0) {
const currentPlugin = pluginEntries.pop() as string;
if (deployedPlugins.has(currentPlugin)) {
continue;
}

// resolve plugins
const pluginDeployerEntries = await this.resolvePlugin(currentPlugin);

// ok now ask for directory handlers
await this.applyDirectoryFileHandlers();
// now that we have plugins check if we have File Handler for them
await this.applyFileHandlers(pluginDeployerEntries);

// ok now ask for directory handlers
await this.applyDirectoryFileHandlers(pluginDeployerEntries);

// add current plugin deployer entries first because dependencies to be installed first
futurePlugins.unshift(...pluginDeployerEntries);

deployedPlugins.add(currentPlugin);

// gather all dependencies needed for current plugin
for (const deployerEntry of pluginDeployerEntries) {
const deployDependencies = await this.pluginDeployerHandler.getPluginDependencies(deployerEntry);
pluginEntries.push(...deployDependencies);
}

}

await this.deployPlugins(futurePlugins);

return Promise.resolve();

await this.deployPlugins();
}

/**
* deploy all plugins that have been accepted
*/
async deployPlugins(): Promise<any> {
const acceptedPlugins = this.pluginDeployerEntries.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted());
const acceptedFrontendPlugins = this.pluginDeployerEntries.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted(PluginDeployerEntryType.FRONTEND));
const acceptedBackendPlugins = this.pluginDeployerEntries.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted(PluginDeployerEntryType.BACKEND));
async deployPlugins(pluginsToDeploy: PluginDeployerEntry[]): Promise<any> {
const acceptedPlugins = pluginsToDeploy.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted());
const acceptedFrontendPlugins = pluginsToDeploy.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted(PluginDeployerEntryType.FRONTEND));
const acceptedBackendPlugins = pluginsToDeploy.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted(PluginDeployerEntryType.BACKEND));

this.logger.debug('the accepted plugins are', acceptedPlugins);
this.logger.debug('the acceptedFrontendPlugins plugins are', acceptedFrontendPlugins);
Expand All @@ -157,10 +182,10 @@ export class PluginDeployerImpl implements PluginDeployer {
/**
* If there are some single files, try to see if we can work on these files (like unpacking it, etc)
*/
public async applyFileHandlers(): Promise<any> {
public async applyFileHandlers(pluginDeployerEntries: PluginDeployerEntry[]): Promise<any> {
const waitPromises: Array<Promise<any>> = [];

this.pluginDeployerEntries.filter(pluginDeployerEntry => pluginDeployerEntry.isResolved()).map(pluginDeployerEntry => {
pluginDeployerEntries.filter(pluginDeployerEntry => pluginDeployerEntry.isResolved()).map(pluginDeployerEntry => {
this.pluginDeployerFileHandlers.map(pluginFileHandler => {
const proxyPluginDeployerEntry = new ProxyPluginDeployerEntry(pluginFileHandler, (pluginDeployerEntry) as PluginDeployerEntryImpl);
if (pluginFileHandler.accept(proxyPluginDeployerEntry)) {
Expand All @@ -177,10 +202,10 @@ export class PluginDeployerImpl implements PluginDeployer {
/**
* Check for all registered directories to see if there are some plugins that can be accepted to be deployed.
*/
public async applyDirectoryFileHandlers(): Promise<any> {
public async applyDirectoryFileHandlers(pluginDeployerEntries: PluginDeployerEntry[]): Promise<any> {
const waitPromises: Array<Promise<any>> = [];

this.pluginDeployerEntries.filter(pluginDeployerEntry => pluginDeployerEntry.isResolved()).map(pluginDeployerEntry => {
pluginDeployerEntries.filter(pluginDeployerEntry => pluginDeployerEntry.isResolved()).map(pluginDeployerEntry => {
this.pluginDeployerDirectoryHandlers.map(pluginDirectoryHandler => {
const proxyPluginDeployerEntry = new ProxyPluginDeployerEntry(pluginDirectoryHandler, (pluginDeployerEntry) as PluginDeployerEntryImpl);
if (pluginDirectoryHandler.accept(proxyPluginDeployerEntry)) {
Expand All @@ -195,32 +220,25 @@ export class PluginDeployerImpl implements PluginDeployer {
}

/**
* Check a given set of plugin IDs to see if there are some resolvers that can handle them. If there is a matching resolver, then we resolve the plugin
* Check a plugin ID see if there are some resolvers that can handle it. If there is a matching resolver, then we resolve the plugin
*/
public async resolvePlugins(pluginIdList: string[]): Promise<PluginDeployerEntry[]> {
public async resolvePlugin(pluginId: string): Promise<PluginDeployerEntry[]> {
const pluginDeployerEntries: PluginDeployerEntry[] = [];
const foundPluginResolver = this.pluginResolvers.find(pluginResolver => pluginResolver.accept(pluginId));
// there is a resolver for the input
if (foundPluginResolver) {

// check if accepted ?
const promises = pluginIdList.map(async pluginId => {
// create context object
const context = new PluginDeployerResolverContextImpl(foundPluginResolver, pluginId);

const foundPluginResolver = this.pluginResolvers.find(pluginResolver => pluginResolver.accept(pluginId));
// there is a resolver for the input
if (foundPluginResolver) {
await foundPluginResolver.resolve(context);

// create context object
const context = new PluginDeployerResolverContextImpl(foundPluginResolver, pluginId);

await foundPluginResolver.resolve(context);

context.getPlugins().forEach(entry => pluginDeployerEntries.push(entry));
} else {
// log it for now
this.logger.error('No plugin resolver found for the entry', pluginId);
pluginDeployerEntries.push(new PluginDeployerEntryImpl(pluginId, pluginId));
}
// you can do other stuff with the `item` here
});
await Promise.all(promises);
context.getPlugins().forEach(entry => pluginDeployerEntries.push(entry));
} else {
// log it for now
this.logger.error('No plugin resolver found for the entry', pluginId);
pluginDeployerEntries.push(new PluginDeployerEntryImpl(pluginId, pluginId));
}

return pluginDeployerEntries;
}
Expand Down