From 286f9a8978665e980e84b22a252adf375d6adb7b Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 12 Jul 2019 16:39:38 -0500 Subject: [PATCH] Implement multiple extension directories --- scripts/vscode.patch | 114 +++++++++++++++++++++++++++++++++++++++++++ src/channel.ts | 64 ++++++++++++------------ src/cli.ts | 5 ++ 3 files changed, 151 insertions(+), 32 deletions(-) diff --git a/scripts/vscode.patch b/scripts/vscode.patch index d9b01c45..4b9b245d 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -1,3 +1,36 @@ +diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts +index 443e430fcd..fdd9900598 100644 +--- a/src/vs/platform/environment/common/environment.ts ++++ b/src/vs/platform/environment/common/environment.ts +@@ -156,4 +156,7 @@ export interface IEnvironmentService { + + webviewEndpoint?: string; + readonly webviewResourceRoot: string; ++ ++ extraExtensionPaths: string[]; ++ extraBuiltinExtensionPaths: string[]; + } +diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts +index 55c3d8302a..b8568fa785 100644 +--- a/src/vs/platform/environment/node/environmentService.ts ++++ b/src/vs/platform/environment/node/environmentService.ts +@@ -276,6 +276,16 @@ export class EnvironmentService implements IEnvironmentService { + return 'vscode-resource:'; + } + ++ @memoize ++ get extraExtensionPaths(): string[] { ++ return (this._args['extra-extensions-dir'] || []).map((p: string) => parsePathArg(p, process)); ++ } ++ ++ @memoize ++ get extraBuiltinExtensionPaths(): string[] { ++ return (this._args['extra-builtin-extensions-dir'] || []).map((p: string) => parsePathArg(p, process)); ++ } ++ + constructor(private _args: ParsedArgs, private _execPath: string) { + if (!process.env['VSCODE_LOGS']) { + const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryIpc.ts b/src/vs/platform/extensionManagement/node/extensionGalleryIpc.ts new file mode 100644 index 0000000000..ef1db87989 @@ -100,6 +133,74 @@ index 0000000000..ef1db87989 + return this.channel.call('getCompatibleExtension', [ id, version ]); + } +} +diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts +index e09049c5b9..d93ffa527a 100644 +--- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts ++++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts +@@ -724,11 +724,15 @@ export class ExtensionManagementService extends Disposable implements IExtension + + private scanSystemExtensions(): Promise { + this.logService.trace('Started scanning system extensions'); +- const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System) +- .then(result => { +- this.logService.trace('Scanned system extensions:', result.length); +- return result; +- }); ++ const systemExtensionsPromise = Promise.all([ ++ this.scanExtensions(this.systemExtensionsPath, ExtensionType.System), ++ ...this.environmentService.extraBuiltinExtensionPaths ++ .map((path) => this.scanExtensions(path, ExtensionType.System)) ++ ]).then((results) => { ++ const result = results.reduce((flat, current) => flat.concat(current), []); ++ this.logService.info('Scanned system extensions:', result.length); ++ return result; ++ }); + if (this.environmentService.isBuilt) { + return systemExtensionsPromise; + } +@@ -750,9 +754,16 @@ export class ExtensionManagementService extends Disposable implements IExtension + .then(([systemExtensions, devSystemExtensions]) => [...systemExtensions, ...devSystemExtensions]); + } + ++ private scanAllUserExtensions(folderName: string, type: ExtensionType): Promise { ++ return Promise.all([ ++ this.scanExtensions(folderName, type), ++ ...this.environmentService.extraExtensionPaths.map((p) => this.scanExtensions(p, ExtensionType.User)) ++ ]).then((results) => results.reduce((flat, current) => flat.concat(current), [])); ++ } ++ + private scanUserExtensions(excludeOutdated: boolean): Promise { + this.logService.trace('Started scanning user extensions'); +- return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)]) ++ return Promise.all([this.getUninstalledExtensions(), this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User)]) + .then(([uninstalled, extensions]) => { + extensions = extensions.filter(e => !uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); + if (excludeOutdated) { +@@ -805,7 +816,7 @@ export class ExtensionManagementService extends Disposable implements IExtension + + private async removeUninstalledExtensions(): Promise { + const uninstalled = await this.getUninstalledExtensions(); +- const extensions = await this.scanExtensions(this.extensionsPath, ExtensionType.User); // All user extensions ++ const extensions = await this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User); // All user extensions + const installed: Set = new Set(); + for (const e of extensions) { + if (!uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]) { +@@ -824,7 +835,7 @@ export class ExtensionManagementService extends Disposable implements IExtension + } + + private removeOutdatedExtensions(): Promise { +- return this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions ++ return this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User) // All user extensions + .then(extensions => { + const toRemove: ILocalExtension[] = []; + +@@ -982,4 +993,4 @@ export class ExtensionManagementService extends Disposable implements IExtension + */ + this.telemetryService.publicLog(eventName, assign(extensionData, { success: !error, duration, errorcode })); + } +-} +\ No newline at end of file ++} diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 1986fb6642..7c66b644f2 100644 --- a/src/vs/workbench/browser/web.main.ts @@ -796,6 +897,19 @@ index c08a6e37c1..31640d7e66 100644 } return this._extensionAllowedBadgeProviders; } +diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts +index 3525569601..a91a5fce7d 100644 +--- a/src/vs/workbench/services/environment/browser/environmentService.ts ++++ b/src/vs/workbench/services/environment/browser/environmentService.ts +@@ -136,6 +136,8 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { + driverHandle?: string; + driverVerbose: boolean; + webviewEndpoint?: string; ++ extraExtensionPaths: string[]; ++ extraBuiltinExtensionPaths: string[]; + + get webviewResourceRoot(): string { + return this.webviewEndpoint ? this.webviewEndpoint + '/vscode-resource' : 'vscode-resource:'; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts index 611ab9aec9..4e4bea89be 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts diff --git a/src/channel.ts b/src/channel.ts index b463e5f0..72d16854 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -1,6 +1,5 @@ import * as path from "path"; -import { getPathFromAmdModule } from "vs/base/common/amd"; import { VSBuffer } from "vs/base/common/buffer"; import { Emitter, Event } from "vs/base/common/event"; import { IDisposable } from "vs/base/common/lifecycle"; @@ -207,46 +206,47 @@ export class ExtensionEnvironmentChannel implements IServerChannel { } private async scanExtensions(locale: string): Promise { - const root = getPathFromAmdModule(require, ""); - const translations = {}; // TODO: translations - // TODO: there is also this.environment.extensionDevelopmentLocationURI to look into. - const scanBuiltin = async (): Promise => { - const input = new ExtensionScannerInput( - pkg.version, product.commit, locale, !!process.env.VSCODE_DEV, - path.resolve(root, "../extensions"), - true, - false, - translations, - ); - const extensions = await ExtensionScanner.scanExtensions(input, this.log); - // TODO: there is more to do if process.env.VSCODE_DEV is true. - return extensions; + const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise => { + return Promise.all(paths.map((path) => { + return ExtensionScanner.scanExtensions(new ExtensionScannerInput( + pkg.version, + product.commit, + locale, + !!process.env.VSCODE_DEV, + path, + isBuiltin, + isUnderDevelopment, + translations, + ), this.log); + })); }; - const scanInstalled = async (): Promise => { - const input = new ExtensionScannerInput( - pkg.version, product.commit, locale, !!process.env.VSCODE_DEV, - this.environment.extensionsPath, false, true, translations, - ); - return ExtensionScanner.scanExtensions(input, this.log); + const scanBuiltin = async (): Promise => { + return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]); + }; + + const scanInstalled = async (): Promise => { + return scanMultiple(false, true, [this.environment.extensionsPath, ...this.environment.extraExtensionPaths]); }; return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => { // It's possible to get duplicates. const uniqueExtensions = new Map(); - allExtensions.forEach((extensions) => { - extensions.forEach((extension) => { - const id = ExtensionIdentifier.toKey(extension.identifier); - if (uniqueExtensions.has(id)) { - const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath; - const newPath = extension.extensionLocation.fsPath; - this.log.warn( - `Extension ${id} in ${oldPath} has been overridden ${newPath}`, - ); - } - uniqueExtensions.set(id, extension); + allExtensions.forEach((multipleExtensions) => { + multipleExtensions.forEach((extensions) => { + extensions.forEach((extension) => { + const id = ExtensionIdentifier.toKey(extension.identifier); + if (uniqueExtensions.has(id)) { + const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath; + const newPath = extension.extensionLocation.fsPath; + this.log.warn( + `Extension ${id} in ${oldPath} has been overridden ${newPath}`, + ); + } + uniqueExtensions.set(id, extension); + }); }); }); diff --git a/src/cli.ts b/src/cli.ts index 0b8b9f1a..a43b495c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -74,6 +74,11 @@ interface IMainCli { const main = async (): Promise => { const args = validatePaths(parseMainProcessArgv(process.argv)) as Args; + ["extra-extensions-dir", "extra-builtin-extensions-dir"].forEach((key) => { + if (typeof args[key] === "string") { + args[key] = [args[key]]; + } + }); if (!product.extensionsGallery) { product.extensionsGallery = {