mirror of https://github.com/coder/code-server.git
Implement multiple extension directories
This commit is contained in:
parent
97167e75ff
commit
286f9a8978
|
@ -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<ILocalExtension[]> {
|
||||
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<ILocalExtension[]> {
|
||||
+ 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<ILocalExtension[]> {
|
||||
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<void> {
|
||||
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<string> = new Set<string>();
|
||||
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<void> {
|
||||
- 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
|
||||
|
|
|
@ -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<IExtensionDescription[]> {
|
||||
const root = getPathFromAmdModule(require, "");
|
||||
|
||||
const translations = {}; // TODO: translations
|
||||
|
||||
// TODO: there is also this.environment.extensionDevelopmentLocationURI to look into.
|
||||
const scanBuiltin = async (): Promise<IExtensionDescription[]> => {
|
||||
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<IExtensionDescription[][]> => {
|
||||
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<IExtensionDescription[]> => {
|
||||
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<IExtensionDescription[][]> => {
|
||||
return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]);
|
||||
};
|
||||
|
||||
const scanInstalled = async (): Promise<IExtensionDescription[][]> => {
|
||||
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<string, IExtensionDescription>();
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -74,6 +74,11 @@ interface IMainCli {
|
|||
|
||||
const main = async (): Promise<void> => {
|
||||
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 = {
|
||||
|
|
Loading…
Reference in New Issue