Skip to content

Commit

Permalink
Added extensionDependency automatically downloading. Fixes: #4504
Browse files Browse the repository at this point in the history
Signed-off-by: Josh Pinkney <joshpinkney@gmail.com>
  • Loading branch information
JPinkney committed Jun 18, 2019
1 parent 3b0d180 commit 1acc628
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 43 deletions.
8 changes: 7 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 @@ -40,7 +41,8 @@ export class VsCodePluginScanner extends TheiaPluginScanner implements PluginSca
},
entryPoint: {
backend: plugin.main
}
},
extensionDependencies: this.createDeployableDependencies(plugin.extensionDependencies || [])
};
result.contributes = this.readContributions(plugin);
return result;
Expand All @@ -54,4 +56,8 @@ export class VsCodePluginScanner extends TheiaPluginScanner implements PluginSca
backendInitPath: __dirname + '/plugin-vscode-init.js'
};
}

createDeployableDependencies(dependencies: string[]): string[] {
return dependencies.map(dep => this.VSCODE_PREFIX + dep);
}
}
12 changes: 12 additions & 0 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface PluginPackage {
description: string;
contributes?: PluginPackageContribution;
packagePath: string;
extensionDependencies?: string[];
}
export namespace PluginPackage {
export function toPluginUrl(pck: PluginPackage, relativePath: string): string {
Expand Down Expand Up @@ -198,6 +199,12 @@ export interface PluginScanner {
* @returns {PluginLifecycle}
*/
getLifecycle(plugin: PluginPackage): PluginLifecycle;

/**
* Converts an array of extension dependencies
* to an array of deployable extension dependencies
*/
createDeployableDependencies(dependencies: string[]): string[]
}

export const PluginDeployer = Symbol('PluginDeployer');
Expand Down Expand Up @@ -242,6 +249,8 @@ export interface PluginDeployerResolverContext {

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

getPlugins(): PluginDeployerEntry[];

getOriginId(): string;

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

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

readDeployDependencies(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 readDeployDependencies(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 @@ -70,6 +70,7 @@ const INTERNAL_CONSOLE_OPTIONS_SCHEMA = {

@injectable()
export class TheiaPluginScanner implements PluginScanner {

private readonly _apiType: PluginEngine = 'theiaPlugin';

@inject(GrammarsReader)
Expand All @@ -94,7 +95,8 @@ export class TheiaPluginScanner implements PluginScanner {
entryPoint: {
frontend: plugin.theiaPlugin!.frontend,
backend: plugin.theiaPlugin!.backend
}
},
extensionDependencies: this.createDeployableDependencies(plugin.extensionDependencies || [])
};
result.contributes = this.readContributions(plugin);
return result;
Expand All @@ -110,6 +112,10 @@ export class TheiaPluginScanner implements PluginScanner {
};
}

createDeployableDependencies(dependencies: string[]): string[] {
return dependencies;
}

protected readContributions(rawPlugin: PluginPackage): PluginContribution | undefined {
if (!rawPlugin.contributes) {
return undefined;
Expand Down
103 changes: 62 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 All @@ -68,6 +63,11 @@ export class PluginDeployerImpl implements PluginDeployer {
@optional() @multiInject(PluginDeployerDirectoryHandler)
private pluginDeployerDirectoryHandlers: PluginDeployerDirectoryHandler[];

/**
* All plugins that have been deployed
*/
private deployedPlugins = new Set<string>();

public start(): void {
this.logger.debug('Starting the deployer with the list of resolvers', this.pluginResolvers);
this.doStart();
Expand Down Expand Up @@ -114,25 +114,53 @@ 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();
/**
* 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 (this.deployedPlugins.has(currentPlugin)) {
continue;
}

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

// 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);

// ok now ask for directory handlers
await this.applyDirectoryFileHandlers();
// add current plugin deployer entries first because dependencies to be installed first
futurePlugins.unshift(...pluginDeployerEntries);

this.deployedPlugins.add(currentPlugin);

// gather all dependencies needed for current plugin
for (const deployerEntry of pluginDeployerEntries) {
const deployDependencies = await this.pluginDeployerHandler.readDeployDependencies(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 +185,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 +205,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 +223,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 => {

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

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

await foundPluginResolver.resolve(context);
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

0 comments on commit 1acc628

Please sign in to comment.