Merge branch 'sandy081/platformSpecificExtensions'

This commit is contained in:
Sandeep Somavarapu 2021-08-23 11:12:05 +02:00
commit 755ceb254d
No known key found for this signature in database
GPG key ID: 1FED25EC4646638B
26 changed files with 465 additions and 286 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "code-oss-dev", "name": "code-oss-dev",
"version": "1.60.0", "version": "1.60.0",
"distro": "d2c625635aa2168bd2391205e7f34a5249af143b", "distro": "c81cac1012f4ca9187c2e838d5272354d251a617",
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"
}, },

View file

@ -36,6 +36,7 @@ export interface IProcessEnvironment {
*/ */
export interface INodeProcess { export interface INodeProcess {
platform: string; platform: string;
arch: string;
env: IProcessEnvironment; env: IProcessEnvironment;
nextTick?: (callback: (...args: any[]) => void) => void; nextTick?: (callback: (...args: any[]) => void) => void;
versions?: { versions?: {

View file

@ -5,7 +5,7 @@
import { globals, INodeProcess, isMacintosh, isWindows, setImmediate } from 'vs/base/common/platform'; import { globals, INodeProcess, isMacintosh, isWindows, setImmediate } from 'vs/base/common/platform';
let safeProcess: INodeProcess & { nextTick: (callback: (...args: any[]) => void) => void; }; let safeProcess: Omit<INodeProcess, 'arch'> & { nextTick: (callback: (...args: any[]) => void) => void; arch: string | undefined; };
declare const process: INodeProcess; declare const process: INodeProcess;
// Native sandbox environment // Native sandbox environment
@ -13,6 +13,7 @@ if (typeof globals.vscode !== 'undefined' && typeof globals.vscode.process !== '
const sandboxProcess: INodeProcess = globals.vscode.process; const sandboxProcess: INodeProcess = globals.vscode.process;
safeProcess = { safeProcess = {
get platform() { return sandboxProcess.platform; }, get platform() { return sandboxProcess.platform; },
get arch() { return sandboxProcess.arch; },
get env() { return sandboxProcess.env; }, get env() { return sandboxProcess.env; },
cwd() { return sandboxProcess.cwd(); }, cwd() { return sandboxProcess.cwd(); },
nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); } nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); }
@ -23,6 +24,7 @@ if (typeof globals.vscode !== 'undefined' && typeof globals.vscode.process !== '
else if (typeof process !== 'undefined') { else if (typeof process !== 'undefined') {
safeProcess = { safeProcess = {
get platform() { return process.platform; }, get platform() { return process.platform; },
get arch() { return process.arch; },
get env() { return process.env; }, get env() { return process.env; },
cwd() { return process.env['VSCODE_CWD'] || process.cwd(); }, cwd() { return process.env['VSCODE_CWD'] || process.cwd(); },
nextTick(callback: (...args: any[]) => void): void { return process.nextTick!(callback); } nextTick(callback: (...args: any[]) => void): void { return process.nextTick!(callback); }
@ -35,6 +37,7 @@ else {
// Supported // Supported
get platform() { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; }, get platform() { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; },
get arch() { return undefined; /* arch is undefined in web */ },
nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); }, nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); },
// Unsupported // Unsupported
@ -70,3 +73,10 @@ export const platform = safeProcess.platform;
* environments. * environments.
*/ */
export const nextTick = safeProcess.nextTick; export const nextTick = safeProcess.nextTick;
/**
* Provides safe access to the `arch` method in node.js, sandboxed or web
* environments.
* Note: `arch` is `undefined` in web
*/
export const arch = safeProcess.arch;

View file

@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { import {
DidUninstallExtensionEvent, ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOperation, InstallOptions, DidUninstallExtensionEvent, ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOperation, InstallOptions,
InstallVSIXOptions, INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, IReportedExtension, StatisticType, UninstallOptions InstallVSIXOptions, INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, IReportedExtension, StatisticType, CURRENT_TARGET_PLATFORM, UninstallOptions
} from 'vs/platform/extensionManagement/common/extensionManagement'; } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionIdentifierWithVersion, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { areSameExtensions, ExtensionIdentifierWithVersion, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
@ -87,7 +87,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
} }
try { try {
extension = await this.checkAndGetCompatibleVersion(extension); extension = await this.checkAndGetCompatibleVersion(extension, !options.installGivenVersion);
} catch (error) { } catch (error) {
this.logService.error(getErrorMessage(error)); this.logService.error(getErrorMessage(error));
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
@ -346,7 +346,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
if (identifiers.find(identifier => areSameExtensions(identifier, galleryExtension.identifier))) { if (identifiers.find(identifier => areSameExtensions(identifier, galleryExtension.identifier))) {
continue; continue;
} }
const compatibleExtension = await this.checkAndGetCompatibleVersion(galleryExtension); const compatibleExtension = await this.checkAndGetCompatibleVersion(galleryExtension, true);
if (!await this.canInstall(compatibleExtension)) { if (!await this.canInstall(compatibleExtension)) {
this.logService.info('Skipping the extension as it cannot be installed', compatibleExtension.identifier.id); this.logService.info('Skipping the extension as it cannot be installed', compatibleExtension.identifier.id);
continue; continue;
@ -367,12 +367,20 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
return allDependenciesAndPacks.filter(e => !installed.some(i => areSameExtensions(i.identifier, e.gallery.identifier))); return allDependenciesAndPacks.filter(e => !installed.some(i => areSameExtensions(i.identifier, e.gallery.identifier)));
} }
private async checkAndGetCompatibleVersion(extension: IGalleryExtension): Promise<IGalleryExtension> { private async checkAndGetCompatibleVersion(extension: IGalleryExtension, fetchCompatibleVersion: boolean): Promise<IGalleryExtension> {
if (await this.isMalicious(extension)) { if (await this.isMalicious(extension)) {
throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), INSTALL_ERROR_MALICIOUS); throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), INSTALL_ERROR_MALICIOUS);
} }
const compatibleExtension = await this.galleryService.getCompatibleExtension(extension); let compatibleExtension: IGalleryExtension | null = null;
if (await this.galleryService.isExtensionCompatible(extension, CURRENT_TARGET_PLATFORM)) {
compatibleExtension = extension;
}
if (!compatibleExtension && fetchCompatibleVersion) {
compatibleExtension = await this.galleryService.getCompatibleExtension(extension, CURRENT_TARGET_PLATFORM);
}
if (!compatibleExtension) { if (!compatibleExtension) {
throw new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Can't install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, product.version), INSTALL_ERROR_INCOMPATIBLE); throw new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Can't install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, product.version), INSTALL_ERROR_INCOMPATIBLE);
} }

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { distinct } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { canceled, getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; import { canceled, getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
import { getOrDefault } from 'vs/base/common/objects'; import { getOrDefault } from 'vs/base/common/objects';
@ -11,8 +12,8 @@ import { isWeb } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IHeaders, IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; import { IHeaders, IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { DefaultIconPath, IExtensionGalleryService, IExtensionIdentifier, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IReportedExtension, isIExtensionIdentifier, ITranslation, SortBy, SortOrder, StatisticType, WEB_EXTENSION_TAG } from 'vs/platform/extensionManagement/common/extensionManagement'; import { CURRENT_TARGET_PLATFORM, DefaultIconPath, IExtensionGalleryService, IExtensionIdentifier, IExtensionIdentifierWithVersion, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IReportedExtension, isIExtensionIdentifier, ITranslation, SortBy, SortOrder, StatisticType, TargetPlatform, toTargetPlatform, WEB_EXTENSION_TAG } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
@ -41,6 +42,7 @@ interface IRawGalleryExtensionVersion {
readonly fallbackAssetUri: string; readonly fallbackAssetUri: string;
readonly files: IRawGalleryExtensionFile[]; readonly files: IRawGalleryExtensionFile[];
readonly properties?: IRawGalleryExtensionProperty[]; readonly properties?: IRawGalleryExtensionProperty[];
readonly targetPlatform?: string;
} }
interface IRawGalleryExtensionStatistics { interface IRawGalleryExtensionStatistics {
@ -270,8 +272,8 @@ function getRepositoryAsset(version: IRawGalleryExtensionVersion): IGalleryExten
function getDownloadAsset(version: IRawGalleryExtensionVersion): IGalleryExtensionAsset { function getDownloadAsset(version: IRawGalleryExtensionVersion): IGalleryExtensionAsset {
return { return {
uri: `${version.fallbackAssetUri}/${AssetType.VSIX}?redirect=true`, uri: `${version.fallbackAssetUri}/${AssetType.VSIX}?redirect=true${version.targetPlatform ? `&targetPlatform=${version.targetPlatform}` : ''}`,
fallbackUri: `${version.fallbackAssetUri}/${AssetType.VSIX}` fallbackUri: `${version.fallbackAssetUri}/${AssetType.VSIX}${version.targetPlatform ? `?targetPlatform=${version.targetPlatform}` : ''}`
}; };
} }
@ -310,7 +312,75 @@ function getIsPreview(flags: string): boolean {
return flags.indexOf('preview') !== -1; return flags.indexOf('preview') !== -1;
} }
function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, index: number, query: Query, querySource?: string): IGalleryExtension { function getTargetPlatform(version: IRawGalleryExtensionVersion): TargetPlatform {
return version.targetPlatform ? toTargetPlatform(version.targetPlatform) : TargetPlatform.UNIVERSAL;
}
function getAllTargetPlatforms(rawGalleryExtension: IRawGalleryExtension): TargetPlatform[] {
const allTargetPlatforms = distinct(rawGalleryExtension.versions.map(getTargetPlatform));
// Is a web extension only if it has WEB_EXTENSION_TAG
const isWebExtension = !!rawGalleryExtension.tags?.includes(WEB_EXTENSION_TAG);
// Include Web Target Platform only if it is a web extension
const webTargetPlatformIndex = allTargetPlatforms.indexOf(TargetPlatform.WEB);
if (isWebExtension) {
if (webTargetPlatformIndex === -1) {
// Web extension but does not has web target platform -> add it
allTargetPlatforms.push(TargetPlatform.WEB);
}
} else {
if (webTargetPlatformIndex !== -1) {
// Not a web extension but has web target platform -> remove it
allTargetPlatforms.splice(webTargetPlatformIndex, 1);
}
}
return allTargetPlatforms;
}
function isNotWebExtensionInWebTargetPlatform(allTargetPlatforms: TargetPlatform[], productTargetPlatform: TargetPlatform): boolean {
// Not a web extension in web target platform
return productTargetPlatform === TargetPlatform.WEB && !allTargetPlatforms.includes(TargetPlatform.WEB);
}
function isTargetPlatformCompatible(extensionTargetPlatform: TargetPlatform, allTargetPlatforms: TargetPlatform[], productTargetPlatform: TargetPlatform): boolean {
// Not compatible when extension is not a web extension in web target platform
if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, productTargetPlatform)) {
return false;
}
// Compatible when extension target platform is universal
if (extensionTargetPlatform === TargetPlatform.UNIVERSAL) {
return true;
}
// Not compatible when extension target platform is unknown
if (extensionTargetPlatform === TargetPlatform.UNKNOWN) {
return false;
}
// Compatible when extension and product target platforms matches
if (extensionTargetPlatform === productTargetPlatform) {
return true;
}
// Fallback
switch (productTargetPlatform) {
case TargetPlatform.WIN32_X64: return extensionTargetPlatform === TargetPlatform.WIN32_IA32;
case TargetPlatform.WIN32_ARM64: return extensionTargetPlatform === TargetPlatform.WIN32_IA32;
default: return false;
}
}
function toExtensionWithLatestVersion(galleryExtension: IRawGalleryExtension, index: number, query: Query, querySource?: string): IGalleryExtension {
const allTargetPlatforms = getAllTargetPlatforms(galleryExtension);
let latestVersion = galleryExtension.versions[0];
latestVersion = galleryExtension.versions.find(version => version.version === latestVersion.version && isTargetPlatformCompatible(getTargetPlatform(version), allTargetPlatforms, CURRENT_TARGET_PLATFORM)) || latestVersion;
return toExtension(galleryExtension, latestVersion, allTargetPlatforms, index, query, querySource);
}
function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, allTargetPlatforms: TargetPlatform[], index: number, query: Query, querySource?: string): IGalleryExtension {
const assets = <IGalleryExtensionAssets>{ const assets = <IGalleryExtensionAssets>{
manifest: getVersionAsset(version, AssetType.Manifest), manifest: getVersionAsset(version, AssetType.Manifest),
readme: getVersionAsset(version, AssetType.Details), readme: getVersionAsset(version, AssetType.Details),
@ -341,14 +411,16 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
tags: galleryExtension.tags || [], tags: galleryExtension.tags || [],
releaseDate: Date.parse(galleryExtension.releaseDate), releaseDate: Date.parse(galleryExtension.releaseDate),
lastUpdated: Date.parse(galleryExtension.lastUpdated), lastUpdated: Date.parse(galleryExtension.lastUpdated),
webExtension: !!galleryExtension.tags?.includes(WEB_EXTENSION_TAG), allTargetPlatforms,
assets, assets,
properties: { properties: {
dependencies: getExtensions(version, PropertyType.Dependency), dependencies: getExtensions(version, PropertyType.Dependency),
extensionPack: getExtensions(version, PropertyType.ExtensionPack), extensionPack: getExtensions(version, PropertyType.ExtensionPack),
engine: getEngine(version), engine: getEngine(version),
localizedLanguages: getLocalizedLanguages(version), localizedLanguages: getLocalizedLanguages(version),
targetPlatform: getTargetPlatform(version),
}, },
preview: getIsPreview(galleryExtension.flags),
/* __GDPR__FRAGMENT__ /* __GDPR__FRAGMENT__
"GalleryExtensionTelemetryData2" : { "GalleryExtensionTelemetryData2" : {
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
@ -359,7 +431,6 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
index: ((query.pageNumber - 1) * query.pageSize) + index, index: ((query.pageNumber - 1) * query.pageSize) + index,
querySource querySource
}, },
preview: getIsPreview(galleryExtension.flags)
}; };
} }
@ -400,29 +471,48 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return !!this.extensionsGalleryUrl; return !!this.extensionsGalleryUrl;
} }
async getExtensions(names: string[], token: CancellationToken): Promise<IGalleryExtension[]> { async getExtensions(identifiers: ReadonlyArray<IExtensionIdentifier | IExtensionIdentifierWithVersion>, token: CancellationToken): Promise<IGalleryExtension[]> {
const result: IGalleryExtension[] = []; const result: IGalleryExtension[] = [];
let { total, firstPage: pageResult, getPage } = await this.query({ names, pageSize: names.length }, token); let query = new Query()
result.push(...pageResult); .withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties)
for (let pageIndex = 1; result.length < total; pageIndex++) { .withPage(1, identifiers.length)
pageResult = await getPage(pageIndex, token); .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
if (pageResult.length) { .withFilter(FilterType.ExtensionName, ...identifiers.map(({ id }) => id.toLowerCase()));
result.push(...pageResult);
if (identifiers.every(identifier => !(<IExtensionIdentifierWithVersion>identifier).version)) {
query = query.withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.IncludeLatestVersionOnly);
}
const { galleryExtensions } = await this.queryGallery(query, CancellationToken.None);
for (let index = 0; index < galleryExtensions.length; index++) {
const galleryExtension = galleryExtensions[index];
if (!galleryExtension.versions.length) {
continue;
}
const id = getGalleryExtensionId(galleryExtension.publisher.publisherName, galleryExtension.extensionName);
const version = (<IExtensionIdentifierWithVersion | undefined>identifiers.find(identifier => areSameExtensions(identifier, { id })))?.version;
if (version) {
const versionAsset = galleryExtension.versions.find(v => v.version === version);
if (versionAsset) {
result.push(toExtension(galleryExtension, versionAsset, getAllTargetPlatforms(galleryExtension), index, query));
}
} else { } else {
break; result.push(toExtensionWithLatestVersion(galleryExtension, index, query));
} }
} }
return result; return result;
} }
async getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> { async getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, targetPlatform: TargetPlatform): Promise<IGalleryExtension | null> {
return this.getCompatibleExtensionByEngine(arg1, version);
}
private async getCompatibleExtensionByEngine(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1; const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1;
if (extension && extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version, this.productService.date)) { if (extension) {
return extension; if (isNotWebExtensionInWebTargetPlatform(extension.allTargetPlatforms, targetPlatform)) {
return null;
}
if (await this.isExtensionCompatible(extension, targetPlatform)) {
return extension;
}
} }
const { id, uuid } = extension ? extension.identifier : <IExtensionIdentifier>arg1; const { id, uuid } = extension ? extension.identifier : <IExtensionIdentifier>arg1;
let query = new Query() let query = new Query()
@ -442,24 +532,53 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return null; return null;
} }
if (version) { const allTargetPlatforms = getAllTargetPlatforms(rawExtension);
const versionAsset = rawExtension.versions.filter(v => v.version === version)[0]; if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, targetPlatform)) {
if (versionAsset) {
const extension = toExtension(rawExtension, versionAsset, 0, query);
if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version, this.productService.date)) {
return extension;
}
}
return null; return null;
} }
const rawVersion = await this.getLastValidExtensionVersion(rawExtension, rawExtension.versions); for (let rawVersion of rawExtension.versions) {
if (rawVersion) { // set engine property if does not exist
return toExtension(rawExtension, rawVersion, 0, query); if (!getEngine(rawVersion)) {
const engine = await this.getEngine(rawVersion);
rawVersion = {
...rawVersion,
properties: [...(rawVersion.properties || []), { key: PropertyType.Engine, value: engine }]
};
}
if (await this.isRawExtensionVersionCompatible(rawVersion, allTargetPlatforms, targetPlatform)) {
return toExtension(rawExtension, rawVersion, allTargetPlatforms, 0, query);
}
} }
return null; return null;
} }
async isExtensionCompatible(extension: IGalleryExtension, targetPlatform: TargetPlatform): Promise<boolean> {
if (!isTargetPlatformCompatible(extension.properties.targetPlatform, extension.allTargetPlatforms, targetPlatform)) {
return false;
}
let engine = extension.properties.engine;
if (!engine) {
const manifest = await this.getManifest(extension, CancellationToken.None);
if (!manifest) {
throw new Error('Manifest was not found');
}
engine = manifest.engines.vscode;
}
return isEngineValid(engine, this.productService.version, this.productService.date);
}
private async isRawExtensionVersionCompatible(rawExtensionVersion: IRawGalleryExtensionVersion, allTargetPlatforms: TargetPlatform[], targetPlatform: TargetPlatform): Promise<boolean> {
if (!isTargetPlatformCompatible(getTargetPlatform(rawExtensionVersion), allTargetPlatforms, targetPlatform)) {
return false;
}
const engine = await this.getEngine(rawExtensionVersion);
return isEngineValid(engine, this.productService.version, this.productService.date);
}
query(token: CancellationToken): Promise<IPager<IGalleryExtension>>; query(token: CancellationToken): Promise<IPager<IGalleryExtension>>;
query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>; query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>;
async query(arg1: any, arg2?: any): Promise<IPager<IGalleryExtension>> { async query(arg1: any, arg2?: any): Promise<IPager<IGalleryExtension>> {
@ -522,14 +641,14 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
} }
const { galleryExtensions, total } = await this.queryGallery(query, token); const { galleryExtensions, total } = await this.queryGallery(query, token);
const extensions = galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, query, options.source)); const extensions = galleryExtensions.map((e, index) => toExtensionWithLatestVersion(e, index, query, options.source));
const getPage = async (pageIndex: number, ct: CancellationToken) => { const getPage = async (pageIndex: number, ct: CancellationToken) => {
if (ct.isCancellationRequested) { if (ct.isCancellationRequested) {
throw canceled(); throw canceled();
} }
const nextPageQuery = query.withPage(pageIndex + 1); const nextPageQuery = query.withPage(pageIndex + 1);
const { galleryExtensions } = await this.queryGallery(nextPageQuery, ct); const { galleryExtensions } = await this.queryGallery(nextPageQuery, ct);
return galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, nextPageQuery, options.source)); return galleryExtensions.map((e, index) => toExtensionWithLatestVersion(e, index, nextPageQuery, options.source));
}; };
return { firstPage: extensions, total, pageSize: query.pageSize, getPage } as IPager<IGalleryExtension>; return { firstPage: extensions, total, pageSize: query.pageSize, getPage } as IPager<IGalleryExtension>;
@ -635,8 +754,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : ''; const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : '';
const downloadAsset = operationParam ? { const downloadAsset = operationParam ? {
uri: `${extension.assets.download.uri}&${operationParam}=true`, uri: `${extension.assets.download.uri}${URI.parse(extension.assets.download.uri).query ? '&' : '?'}${operationParam}=true`,
fallbackUri: `${extension.assets.download.fallbackUri}?${operationParam}=true` fallbackUri: `${extension.assets.download.fallbackUri}${URI.parse(extension.assets.download.fallbackUri).query ? '&' : '?'}${operationParam}=true`
} : extension.assets.download; } : extension.assets.download;
const context = await this.getAsset(downloadAsset); const context = await this.getAsset(downloadAsset);
@ -662,6 +781,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return null; return null;
} }
private async getManifestFromRawExtensionVersion(rawExtensionVersion: IRawGalleryExtensionVersion, token: CancellationToken): Promise<IExtensionManifest | null> {
const manifestAsset = getVersionAsset(rawExtensionVersion, AssetType.Manifest);
if (!manifestAsset) {
throw new Error('Manifest was not found');
}
const headers = { 'Accept-Encoding': 'gzip' };
const context = await this.getAsset(manifestAsset, { headers });
return await asJson<IExtensionManifest>(context);
}
async getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null> { async getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null> {
const asset = extension.assets.coreTranslations.filter(t => t[0] === languageId.toUpperCase())[0]; const asset = extension.assets.coreTranslations.filter(t => t[0] === languageId.toUpperCase())[0];
if (asset) { if (asset) {
@ -681,7 +810,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return ''; return '';
} }
async getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]> { async getAllCompatibleVersions(extension: IGalleryExtension, targetPlatform: TargetPlatform): Promise<IGalleryExtensionVersion[]> {
let query = new Query() let query = new Query()
.withFlags(Flags.IncludeVersions, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties) .withFlags(Flags.IncludeVersions, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, 1) .withPage(1, 1)
@ -693,22 +822,23 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
query = query.withFilter(FilterType.ExtensionName, extension.identifier.id); query = query.withFilter(FilterType.ExtensionName, extension.identifier.id);
} }
const result: IGalleryExtensionVersion[] = [];
const { galleryExtensions } = await this.queryGallery(query, CancellationToken.None); const { galleryExtensions } = await this.queryGallery(query, CancellationToken.None);
if (galleryExtensions.length) { if (!galleryExtensions.length) {
if (compatible) { return [];
await Promise.all(galleryExtensions[0].versions.map(async v => { }
let engine: string | undefined;
try { const allTargetPlatforms = getAllTargetPlatforms(galleryExtensions[0]);
engine = await this.getEngine(v); if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, targetPlatform)) {
} catch (error) { /* Ignore error and skip version */ } return [];
if (engine && isEngineValid(engine, this.productService.version, this.productService.date)) { }
result.push({ version: v!.version, date: v!.lastUpdated });
} const result: IGalleryExtensionVersion[] = [];
})); for (const version of galleryExtensions[0].versions) {
} else { try {
result.push(...galleryExtensions[0].versions.map(v => ({ version: v.version, date: v.lastUpdated }))); if (await this.isRawExtensionVersionCompatible(version, allTargetPlatforms, targetPlatform)) {
} result.push({ version: version!.version, date: version!.lastUpdated });
}
} catch (error) { /* Ignore error and skip version */ }
} }
return result; return result;
} }
@ -751,63 +881,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
} }
} }
private async getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion | null> { private async getEngine(rawExtensionVersion: IRawGalleryExtensionVersion): Promise<string> {
const version = this.getLastValidExtensionVersionFromProperties(extension, versions); let engine = getEngine(rawExtensionVersion);
if (version) { if (!engine) {
return version; const manifest = await this.getManifestFromRawExtensionVersion(rawExtensionVersion, CancellationToken.None);
} if (!manifest) {
return this.getLastValidExtensionVersionRecursively(extension, versions); throw new Error('Manifest was not found');
}
private getLastValidExtensionVersionFromProperties(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): IRawGalleryExtensionVersion | null {
for (const version of versions) {
const engine = getEngine(version);
if (!engine) {
return null;
}
if (isEngineValid(engine, this.productService.version, this.productService.date)) {
return version;
} }
engine = manifest.engines.vscode;
} }
return null; return engine;
}
private async getEngine(version: IRawGalleryExtensionVersion): Promise<string> {
const engine = getEngine(version);
if (engine) {
return engine;
}
const manifestAsset = getVersionAsset(version, AssetType.Manifest);
if (!manifestAsset) {
throw new Error('Manifest was not found');
}
const headers = { 'Accept-Encoding': 'gzip' };
const context = await this.getAsset(manifestAsset, { headers });
const manifest = await asJson<IExtensionManifest>(context);
if (manifest) {
return manifest.engines.vscode;
}
throw new Error('Error while reading manifest');
}
private async getLastValidExtensionVersionRecursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion | null> {
if (!versions.length) {
return null;
}
const version = versions[0];
const engine = await this.getEngine(version);
if (!isEngineValid(engine, this.productService.version, this.productService.date)) {
return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1));
}
return {
...version,
properties: [...(version.properties || []), { key: PropertyType.Engine, value: engine }]
};
} }
async getExtensionsReport(): Promise<IReportedExtension[]> { async getExtensionsReport(): Promise<IReportedExtension[]> {

View file

@ -7,6 +7,8 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Event } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { FileAccess } from 'vs/base/common/network'; import { FileAccess } from 'vs/base/common/network';
import { IPager } from 'vs/base/common/paging'; import { IPager } from 'vs/base/common/paging';
import { isWeb, OperatingSystem, Platform, platform } from 'vs/base/common/platform';
import { arch } from 'vs/base/common/process';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { ExtensionType, IExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { ExtensionType, IExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
@ -16,11 +18,101 @@ export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z
export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN); export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
export const WEB_EXTENSION_TAG = '__web_extension'; export const WEB_EXTENSION_TAG = '__web_extension';
export const enum TargetPlatform {
WIN32_X64 = 'win32-x64',
WIN32_IA32 = 'win32-ia32',
WIN32_ARM64 = 'win32-arm64',
LINUX_X64 = 'linux-x64',
LINUX_ARM64 = 'linux-arm64',
LINUX_ARMHF = 'linux-armhf',
DARWIN_X64 = 'darwin-x64',
DARWIN_ARM64 = 'darwin-arm64',
WEB = 'web',
UNIVERSAL = 'universal',
UNKNOWN = 'unknown',
}
export function toTargetPlatform(targetPlatform: string): TargetPlatform {
switch (targetPlatform) {
case TargetPlatform.WIN32_X64: return TargetPlatform.WIN32_X64;
case TargetPlatform.WIN32_IA32: return TargetPlatform.WIN32_IA32;
case TargetPlatform.WIN32_ARM64: return TargetPlatform.WIN32_ARM64;
case TargetPlatform.LINUX_X64: return TargetPlatform.LINUX_X64;
case TargetPlatform.LINUX_ARM64: return TargetPlatform.LINUX_ARM64;
case TargetPlatform.LINUX_ARMHF: return TargetPlatform.LINUX_ARMHF;
case TargetPlatform.DARWIN_X64: return TargetPlatform.DARWIN_X64;
case TargetPlatform.DARWIN_ARM64: return TargetPlatform.DARWIN_ARM64;
case TargetPlatform.WEB: return TargetPlatform.WEB;
case TargetPlatform.UNIVERSAL: return TargetPlatform.UNIVERSAL;
default: return TargetPlatform.UNKNOWN;
}
}
export function getTargetPlatformFromOS(os: OperatingSystem, arch: string): TargetPlatform {
let platform: Platform;
switch (os) {
case OperatingSystem.Windows: platform = Platform.Windows; break;
case OperatingSystem.Linux: platform = Platform.Linux; break;
case OperatingSystem.Macintosh: platform = Platform.Mac; break;
}
return getTargetPlatform(platform, arch);
}
export function getTargetPlatform(platform: Platform, arch: string | undefined): TargetPlatform {
switch (platform) {
case Platform.Windows:
if (arch === 'x64') {
return TargetPlatform.WIN32_X64;
}
if (arch === 'ia32') {
return TargetPlatform.WIN32_IA32;
}
if (arch === 'arm64') {
return TargetPlatform.WIN32_ARM64;
}
return TargetPlatform.UNKNOWN;
case Platform.Linux:
if (arch === 'x64') {
return TargetPlatform.LINUX_X64;
}
if (arch === 'arm64') {
return TargetPlatform.LINUX_ARM64;
}
if (arch === 'arm') {
return TargetPlatform.LINUX_ARMHF;
}
return TargetPlatform.UNKNOWN;
case Platform.Mac:
if (arch === 'x64') {
return TargetPlatform.DARWIN_X64;
}
if (arch === 'arm64') {
return TargetPlatform.DARWIN_ARM64;
}
return TargetPlatform.UNKNOWN;
case Platform.Web: return TargetPlatform.WEB;
}
}
export const CURRENT_TARGET_PLATFORM = isWeb ? TargetPlatform.WEB : getTargetPlatform(platform, arch);
export interface IGalleryExtensionProperties { export interface IGalleryExtensionProperties {
dependencies?: string[]; dependencies?: string[];
extensionPack?: string[]; extensionPack?: string[];
engine?: string; engine?: string;
localizedLanguages?: string[]; localizedLanguages?: string[];
targetPlatform: TargetPlatform;
} }
export interface IGalleryExtensionAsset { export interface IGalleryExtensionAsset {
@ -88,11 +180,11 @@ export interface IGalleryExtension {
tags: readonly string[]; tags: readonly string[];
releaseDate: number; releaseDate: number;
lastUpdated: number; lastUpdated: number;
preview: boolean;
allTargetPlatforms: TargetPlatform[];
assets: IGalleryExtensionAssets; assets: IGalleryExtensionAssets;
properties: IGalleryExtensionProperties; properties: IGalleryExtensionProperties;
telemetryData: any; telemetryData: any;
preview: boolean;
webExtension: boolean;
} }
export interface IGalleryMetadata { export interface IGalleryMetadata {
@ -161,17 +253,18 @@ export interface IExtensionGalleryService {
isEnabled(): boolean; isEnabled(): boolean;
query(token: CancellationToken): Promise<IPager<IGalleryExtension>>; query(token: CancellationToken): Promise<IPager<IGalleryExtension>>;
query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>; query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>;
getExtensions(ids: string[], token: CancellationToken): Promise<IGalleryExtension[]>; getExtensions(identifiers: ReadonlyArray<IExtensionIdentifier | IExtensionIdentifierWithVersion>, token: CancellationToken): Promise<IGalleryExtension[]>;
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void>; download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void>;
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>; reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>;
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string>; getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest | null>; getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest | null>;
getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise<string>; getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null>; getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null>;
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]>;
getExtensionsReport(): Promise<IReportedExtension[]>; getExtensionsReport(): Promise<IReportedExtension[]>;
getCompatibleExtension(extension: IGalleryExtension): Promise<IGalleryExtension | null>; isExtensionCompatible(extension: IGalleryExtension, targetPlatform: TargetPlatform): Promise<boolean>;
getCompatibleExtension(id: IExtensionIdentifier, version?: string): Promise<IGalleryExtension | null>; getCompatibleExtension(extension: IGalleryExtension, targetPlatform: TargetPlatform): Promise<IGalleryExtension | null>;
getCompatibleExtension(id: IExtensionIdentifier, targetPlatform: TargetPlatform): Promise<IGalleryExtension | null>;
getAllCompatibleVersions(extension: IGalleryExtension, targetPlatform: TargetPlatform): Promise<IGalleryExtensionVersion[]>;
} }
export interface InstallExtensionEvent { export interface InstallExtensionEvent {
@ -202,8 +295,8 @@ export class ExtensionManagementError extends Error {
} }
} }
export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, donotIncludePackAndDependencies?: boolean }; export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, donotIncludePackAndDependencies?: boolean, installGivenVersion?: boolean };
export type InstallVSIXOptions = InstallOptions & { installOnlyNewlyAddedFromExtensionPack?: boolean }; export type InstallVSIXOptions = Omit<InstallOptions, 'installGivenVersion'> & { installOnlyNewlyAddedFromExtensionPack?: boolean };
export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean }; export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean };
export interface IExtensionManagementParticipant { export interface IExtensionManagementParticipant {

View file

@ -199,23 +199,11 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
} }
private async getGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<Map<string, IGalleryExtension>> { private async getGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<Map<string, IGalleryExtension>> {
const extensionIds = extensions.filter(({ version }) => version === undefined).map(({ id }) => id);
const extensionsWithIdAndVersion = extensions.filter(({ version }) => version !== undefined);
const galleryExtensions = new Map<string, IGalleryExtension>(); const galleryExtensions = new Map<string, IGalleryExtension>();
await Promise.all([ const result = await this.extensionGalleryService.getExtensions(extensions, CancellationToken.None);
(async () => { for (const extension of result) {
const result = await this.extensionGalleryService.getExtensions(extensionIds, CancellationToken.None); galleryExtensions.set(extension.identifier.id.toLowerCase(), extension);
result.forEach(extension => galleryExtensions.set(extension.identifier.id.toLowerCase(), extension)); }
})(),
Promise.all(extensionsWithIdAndVersion.map(async ({ id, version }) => {
const extension = await this.extensionGalleryService.getCompatibleExtension({ id }, version);
if (extension) {
galleryExtensions.set(extension.identifier.id.toLowerCase(), extension);
}
}))
]);
return galleryExtensions; return galleryExtensions;
} }
@ -241,7 +229,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
output.log(version ? localize('installing with version', "Installing extension '{0}' v{1}...", id, version) : localize('installing', "Installing extension '{0}'...", id)); output.log(version ? localize('installing with version', "Installing extension '{0}' v{1}...", id, version) : localize('installing', "Installing extension '{0}'...", id));
} }
await this.extensionManagementService.installFromGallery(galleryExtension, installOptions); await this.extensionManagementService.installFromGallery(galleryExtension, { ...installOptions, installGivenVersion: !!version });
output.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, galleryExtension.version)); output.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, galleryExtension.version));
return manifest; return manifest;
} catch (error) { } catch (error) {

View file

@ -19,6 +19,7 @@ export interface IRemoteAgentEnvironment {
workspaceStorageHome: URI; workspaceStorageHome: URI;
userHome: URI; userHome: URI;
os: OperatingSystem; os: OperatingSystem;
arch: string;
marks: performance.PerformanceMark[]; marks: performance.PerformanceMark[];
useHostProxy: boolean; useHostProxy: boolean;
} }

View file

@ -14,7 +14,7 @@ import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension, CURRENT_TARGET_PLATFORM } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { areSameExtensions, getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
@ -375,7 +375,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
} }
// User Extension Sync: Install/Update, Enablement & State // User Extension Sync: Install/Update, Enablement & State
const extension = await this.extensionGalleryService.getCompatibleExtension(e.identifier); const extension = await this.extensionGalleryService.getCompatibleExtension(e.identifier, CURRENT_TARGET_PLATFORM);
/* Update extension state only if /* Update extension state only if
* extension is installed and version is same as synced version or * extension is installed and version is same as synced version or

View file

@ -285,7 +285,7 @@ CommandsRegistry.registerCommand({
const extensionGalleryService = accessor.get(IExtensionGalleryService); const extensionGalleryService = accessor.get(IExtensionGalleryService);
try { try {
if (typeof arg === 'string') { if (typeof arg === 'string') {
const extension = await extensionGalleryService.getCompatibleExtension({ id: arg }); const [extension] = await extensionGalleryService.getExtensions([{ id: arg }], CancellationToken.None);
if (extension) { if (extension) {
await extensionManagementService.installFromGallery(extension); await extensionManagementService.installFromGallery(extension);
} else { } else {

View file

@ -14,7 +14,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { dispose } from 'vs/base/common/lifecycle'; import { dispose } from 'vs/base/common/lifecycle';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, InstallOperation, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
@ -42,7 +42,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IQuickPickItem, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IQuickPickItem, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { alert } from 'vs/base/browser/ui/aria/aria'; import { alert } from 'vs/base/browser/ui/aria/aria';
import { coalesce } from 'vs/base/common/arrays';
import { IWorkbenchThemeService, IWorkbenchTheme, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IWorkbenchThemeService, IWorkbenchTheme, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ILabelService } from 'vs/platform/label/common/label'; import { ILabelService } from 'vs/platform/label/common/label';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@ -981,36 +980,31 @@ export class InstallAnotherVersionAction extends ExtensionAction {
} }
update(): void { update(): void {
this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && this.extension.state === ExtensionState.Installed; this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && !!this.extension.server && this.extension.state === ExtensionState.Installed;
} }
override run(): Promise<any> { override async run(): Promise<any> {
if (!this.enabled) { if (!this.enabled) {
return Promise.resolve(); return;
} }
return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true }) const allVersions = await this.extensionGalleryService.getAllCompatibleVersions(this.extension!.gallery!, await this.extension!.server!.getTargetPlatform());
.then(async pick => { const versionEntries = allVersions.map((v, i) => ({ id: v.version, label: v.version, description: `${getRelativeDateLabel(new Date(Date.parse(v.date)))}${v.version === this.extension!.version ? ` (${localize('current', "Current")})` : ''}`, latest: i === 0 }));
if (pick) { const pick = await this.quickInputService.pick(versionEntries, { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true });
if (this.extension!.version === pick.id) { if (pick) {
return Promise.resolve(); if (this.extension!.version === pick.id) {
} return;
try { }
if (pick.latest) { try {
await this.extensionsWorkbenchService.install(this.extension!); if (pick.latest) {
} else { await this.extensionsWorkbenchService.install(this.extension!);
await this.extensionsWorkbenchService.installVersion(this.extension!, pick.id); } else {
} await this.extensionsWorkbenchService.installVersion(this.extension!, pick.id);
} catch (error) {
this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, pick.latest ? this.extension!.latestVersion : pick.id, InstallOperation.Install, error).run();
}
} }
return null; } catch (error) {
}); this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, pick.latest ? this.extension!.latestVersion : pick.id, InstallOperation.Install, error).run();
} }
}
private getVersionEntries(): Promise<(IQuickPickItem & { latest: boolean, id: string })[]> { return null;
return this.extensionGalleryService.getAllVersions(this.extension!.gallery!, true)
.then(allVersions => allVersions.map((v, i) => ({ id: v.version, label: v.version, description: `${getRelativeDateLabel(new Date(Date.parse(v.date)))}${v.version === this.extension!.version ? ` (${localize('current', "Current")})` : ''}`, latest: i === 0 })));
} }
} }
@ -2011,7 +2005,7 @@ export class ExtensionStatusAction extends ExtensionAction {
&& !this.extensionManagementServerService.remoteExtensionManagementServer) { && !this.extensionManagementServerService.remoteExtensionManagementServer) {
const productName = isWeb ? localize({ key: 'vscode web', comment: ['VS Code Web is the name of the product'] }, "VS Code Web") : this.productService.nameLong; const productName = isWeb ? localize({ key: 'vscode web', comment: ['VS Code Web is the name of the product'] }, "VS Code Web") : this.productService.nameLong;
let message; let message;
if (this.extension.gallery.webExtension) { if (this.extension.gallery.allTargetPlatforms.includes(TargetPlatform.WEB)) {
message = new MarkdownString(localize('user disabled', "You have configured the '{0}' extension to be disabled in {1}. To enable it, please open user settings and remove it from `remote.extensionKind` setting.", this.extension.displayName || this.extension.identifier.id, productName)); message = new MarkdownString(localize('user disabled', "You have configured the '{0}' extension to be disabled in {1}. To enable it, please open user settings and remove it from `remote.extensionKind` setting.", this.extension.displayName || this.extension.identifier.id, productName));
} else { } else {
message = new MarkdownString(`${localize('not web tooltip', "The '{0}' extension is not available in {1}.", this.extension.displayName || this.extension.identifier.id, productName)} [${localize('learn more', "Learn More")}](https://aka.ms/vscode-remote-codespaces#_why-is-an-extension-not-installable-in-the-browser)`); message = new MarkdownString(`${localize('not web tooltip', "The '{0}' extension is not available in {1}.", this.extension.displayName || this.extension.identifier.id, productName)} [${localize('learn more', "Learn More")}](https://aka.ms/vscode-remote-codespaces#_why-is-an-extension-not-installable-in-the-browser)`);
@ -2283,12 +2277,8 @@ export class InstallSpecificVersionOfExtensionAction extends Action {
constructor( constructor(
id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL, id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@IQuickInputService private readonly quickInputService: IQuickInputService, @IQuickInputService private readonly quickInputService: IQuickInputService,
@INotificationService private readonly notificationService: INotificationService,
@IHostService private readonly hostService: IHostService,
@IInstantiationService private readonly instantiationService: IInstantiationService, @IInstantiationService private readonly instantiationService: IInstantiationService,
@IExtensionService private readonly extensionService: IExtensionService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
) { ) {
super(id, label); super(id, label);
@ -2301,68 +2291,38 @@ export class InstallSpecificVersionOfExtensionAction extends Action {
override async run(): Promise<any> { override async run(): Promise<any> {
const extensionPick = await this.quickInputService.pick(this.getExtensionEntries(), { placeHolder: localize('selectExtension', "Select Extension"), matchOnDetail: true }); const extensionPick = await this.quickInputService.pick(this.getExtensionEntries(), { placeHolder: localize('selectExtension', "Select Extension"), matchOnDetail: true });
if (extensionPick && extensionPick.extension) { if (extensionPick && extensionPick.extension) {
const versionPick = await this.quickInputService.pick(extensionPick.versions.map(v => ({ id: v.version, label: v.version, description: `${getRelativeDateLabel(new Date(Date.parse(v.date)))}${v.version === extensionPick.extension.version ? ` (${localize('current', "Current")})` : ''}` })), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true }); const action = this.instantiationService.createInstance(InstallAnotherVersionAction);
if (versionPick) { action.extension = extensionPick.extension;
if (extensionPick.extension.version !== versionPick.id) { await action.run();
await this.install(extensionPick.extension, versionPick.id); await this.instantiationService.createInstance(SearchExtensionsAction, extensionPick.extension.identifier.id).run();
}
}
} }
} }
private isEnabled(extension: IExtension): boolean { private isEnabled(extension: IExtension): boolean {
return !!extension.gallery && !!extension.local && this.extensionEnablementService.isEnabled(extension.local); const action = this.instantiationService.createInstance(InstallAnotherVersionAction);
action.extension = extension;
return action.enabled && !!extension.local && this.extensionEnablementService.isEnabled(extension.local);
} }
private async getExtensionEntries(): Promise<(IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] })[]> { private async getExtensionEntries(): Promise<IExtensionPickItem[]> {
const installed = await this.extensionsWorkbenchService.queryLocal(); const installed = await this.extensionsWorkbenchService.queryLocal();
const versionsPromises: Promise<{ extension: IExtension, versions: IGalleryExtensionVersion[] } | null>[] = []; const entries: IExtensionPickItem[] = [];
for (const extension of installed) { for (const extension of installed) {
if (this.isEnabled(extension)) { if (this.isEnabled(extension)) {
versionsPromises.push(this.extensionGalleryService.getAllVersions(extension.gallery!, true) entries.push({
.then(versions => (versions.length ? { extension, versions } : null)));
}
}
const extensions = await Promise.all(versionsPromises);
return coalesce(extensions)
.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName))
.map(({ extension, versions }) => {
return {
id: extension.identifier.id, id: extension.identifier.id,
label: extension.displayName || extension.identifier.id, label: extension.displayName || extension.identifier.id,
description: extension.identifier.id, description: extension.identifier.id,
extension, extension,
versions });
} as (IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] }); }
}); }
} return entries.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName));
private install(extension: IExtension, version: string): Promise<void> {
return this.instantiationService.createInstance(SearchExtensionsAction, '@installed ').run()
.then(() => {
return this.extensionsWorkbenchService.installVersion(extension, version)
.then(extension => {
const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
const message = requireReload ? localize('InstallAnotherVersionExtensionAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.identifier.id)
: localize('InstallAnotherVersionExtensionAction.success', "Installing the extension {0} is completed.", extension.identifier.id);
const actions = requireReload ? [{
label: localize('InstallAnotherVersionExtensionAction.reloadNow', "Reload Now"),
run: () => this.hostService.reload()
}] : [];
this.notificationService.prompt(
Severity.Info,
message,
actions,
{ sticky: true }
);
}, error => this.notificationService.error(error));
});
} }
} }
interface IExtensionPickItem extends IQuickPickItem { interface IExtensionPickItem extends IQuickPickItem {
extension?: IExtension; extension: IExtension;
} }
export abstract class AbstractInstallExtensionsInServerAction extends Action { export abstract class AbstractInstallExtensionsInServerAction extends Action {
@ -2489,9 +2449,10 @@ export class InstallLocalExtensionsInRemoteAction extends AbstractInstallExtensi
protected async installExtensions(localExtensionsToInstall: IExtension[]): Promise<void> { protected async installExtensions(localExtensionsToInstall: IExtension[]): Promise<void> {
const galleryExtensions: IGalleryExtension[] = []; const galleryExtensions: IGalleryExtension[] = [];
const vsixs: URI[] = []; const vsixs: URI[] = [];
const remoteTargetPlatform = await this.extensionManagementServerService.remoteExtensionManagementServer!.getTargetPlatform();
await Promises.settled(localExtensionsToInstall.map(async extension => { await Promises.settled(localExtensionsToInstall.map(async extension => {
if (this.extensionGalleryService.isEnabled()) { if (this.extensionGalleryService.isEnabled()) {
const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, extension.version); const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, remoteTargetPlatform);
if (gallery) { if (gallery) {
galleryExtensions.push(gallery); galleryExtensions.push(gallery);
return; return;
@ -2537,9 +2498,10 @@ export class InstallRemoteExtensionsInLocalAction extends AbstractInstallExtensi
protected async installExtensions(extensions: IExtension[]): Promise<void> { protected async installExtensions(extensions: IExtension[]): Promise<void> {
const galleryExtensions: IGalleryExtension[] = []; const galleryExtensions: IGalleryExtension[] = [];
const vsixs: URI[] = []; const vsixs: URI[] = [];
const localTargetPlatform = await this.extensionManagementServerService.localExtensionManagementServer!.getTargetPlatform();
await Promises.settled(extensions.map(async extension => { await Promises.settled(extensions.map(async extension => {
if (this.extensionGalleryService.isEnabled()) { if (this.extensionGalleryService.isEnabled()) {
const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, extension.version); const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, localTargetPlatform);
if (gallery) { if (gallery) {
galleryExtensions.push(gallery); galleryExtensions.push(gallery);
return; return;

View file

@ -14,7 +14,7 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { import {
IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,
InstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier, InstallOperation, DefaultIconPath, InstallOptions, WEB_EXTENSION_TAG, InstallExtensionResult InstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier, InstallOperation, DefaultIconPath, InstallOptions, WEB_EXTENSION_TAG, InstallExtensionResult, TargetPlatform
} from 'vs/platform/extensionManagement/common/extensionManagement'; } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, groupByExtension, ExtensionIdentifierWithVersion, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, groupByExtension, ExtensionIdentifierWithVersion, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
@ -399,7 +399,7 @@ class Extensions extends Disposable {
} }
// Loading the compatible version only there is an engine property // Loading the compatible version only there is an engine property
// Otherwise falling back to old way so that we will not make many roundtrips // Otherwise falling back to old way so that we will not make many roundtrips
const compatible = gallery.properties.engine ? await this.galleryService.getCompatibleExtension(gallery) : gallery; const compatible = gallery.properties.engine ? await this.galleryService.getCompatibleExtension(gallery, await this.server.getTargetPlatform()) : gallery;
if (!compatible) { if (!compatible) {
return false; return false;
} }
@ -418,7 +418,7 @@ class Extensions extends Disposable {
if (!this.galleryService.isEnabled()) { if (!this.galleryService.isEnabled()) {
return; return;
} }
const compatible = await this.galleryService.getCompatibleExtension(extension.identifier); const compatible = await this.galleryService.getCompatibleExtension(extension.identifier, await this.server.getTargetPlatform());
if (compatible) { if (compatible) {
extension.gallery = compatible; extension.gallery = compatible;
this._onChange.fire({ extension }); this._onChange.fire({ extension });
@ -994,7 +994,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
if (this.extensionManagementServerService.webExtensionManagementServer) { if (this.extensionManagementServerService.webExtensionManagementServer) {
const configuredExtensionKind = this.extensionManifestPropertiesService.getUserConfiguredExtensionKind(extension.gallery.identifier); const configuredExtensionKind = this.extensionManifestPropertiesService.getUserConfiguredExtensionKind(extension.gallery.identifier);
return configuredExtensionKind ? configuredExtensionKind.includes('web') : extension.gallery.webExtension; return configuredExtensionKind ? configuredExtensionKind.includes('web') : extension.gallery.allTargetPlatforms.includes(TargetPlatform.WEB);
} }
return false; return false;
@ -1037,29 +1037,27 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}, () => this.extensionManagementService.uninstall(toUninstall).then(() => undefined)); }, () => this.extensionManagementService.uninstall(toUninstall).then(() => undefined));
} }
installVersion(extension: IExtension, version: string): Promise<IExtension> { async installVersion(extension: IExtension, version: string): Promise<IExtension> {
if (!(extension instanceof Extension)) { if (!(extension instanceof Extension)) {
return Promise.resolve(extension); return extension;
} }
if (!extension.gallery) { if (!extension.gallery) {
return Promise.reject(new Error('Missing gallery')); throw new Error('Missing gallery');
} }
return this.galleryService.getCompatibleExtension(extension.gallery.identifier, version) const [gallery] = await this.galleryService.getExtensions([{ id: extension.gallery.identifier.id, version }], CancellationToken.None);
.then(gallery => { if (!gallery) {
if (!gallery) { throw new Error(nls.localize('not found', "Unable to install extension '{0}' because the requested version '{1}' is not found.", extension.gallery!.identifier.id, version));
return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.gallery!.identifier.id, version))); }
}
return this.installWithProgress(async () => { return this.installWithProgress(async () => {
const installed = await this.installFromGallery(extension, gallery); const installed = await this.installFromGallery(extension, gallery, { installGivenVersion: true });
if (extension.latestVersion !== version) { if (extension.latestVersion !== version) {
this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version)); this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version));
} }
return installed; return installed;
} }, gallery.displayName);
, gallery.displayName);
});
} }
reinstall(extension: IExtension): Promise<IExtension> { reinstall(extension: IExtension): Promise<IExtension> {
@ -1138,7 +1136,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
this._onChange.fire(extension); this._onChange.fire(extension);
try { try {
if (extension.state === ExtensionState.Installed && extension.local) { if (extension.state === ExtensionState.Installed && extension.local) {
await this.extensionManagementService.updateFromGallery(gallery, extension.local); await this.extensionManagementService.updateFromGallery(gallery, extension.local, installOptions);
} else { } else {
await this.extensionManagementService.installFromGallery(gallery, installOptions); await this.extensionManagementService.installFromGallery(gallery, installOptions);
} }

View file

@ -10,7 +10,7 @@ import * as ExtensionsActions from 'vs/workbench/contrib/extensions/browser/exte
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import { import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension,
DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult, CURRENT_TARGET_PLATFORM
} from 'vs/platform/extensionManagement/common/extensionManagement'; } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
@ -110,7 +110,7 @@ async function setupTest() {
instantiationService.stub(IRemoteAgentService, RemoteAgentService); instantiationService.stub(IRemoteAgentService, RemoteAgentService);
const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local', getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); } };
instantiationService.stub(IExtensionManagementServerService, <Partial<IExtensionManagementServerService>>{ instantiationService.stub(IExtensionManagementServerService, <Partial<IExtensionManagementServerService>>{
get localExtensionManagementServer(): IExtensionManagementServer { get localExtensionManagementServer(): IExtensionManagementServer {
return localExtensionManagementServer; return localExtensionManagementServer;
@ -2348,7 +2348,8 @@ function aSingleRemoteExtensionManagementServerService(instantiationService: Tes
const remoteExtensionManagementServer: IExtensionManagementServer = { const remoteExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-remote', id: 'vscode-remote',
label: 'remote', label: 'remote',
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService() extensionManagementService: remoteExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}; };
return { return {
_serviceBrand: undefined, _serviceBrand: undefined,
@ -2368,12 +2369,14 @@ function aMultiExtensionManagementServerService(instantiationService: TestInstan
const localExtensionManagementServer: IExtensionManagementServer = { const localExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-local', id: 'vscode-local',
label: 'local', label: 'local',
extensionManagementService: localExtensionManagementService || createExtensionManagementService() extensionManagementService: localExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}; };
const remoteExtensionManagementServer: IExtensionManagementServer = { const remoteExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-remote', id: 'vscode-remote',
label: 'remote', label: 'remote',
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService() extensionManagementService: remoteExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}; };
return { return {
_serviceBrand: undefined, _serviceBrand: undefined,

View file

@ -11,7 +11,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import { import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,
DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy, InstallExtensionResult DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy, InstallExtensionResult, CURRENT_TARGET_PLATFORM
} from 'vs/platform/extensionManagement/common/extensionManagement'; } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
@ -100,7 +100,7 @@ suite('ExtensionsListView Tests', () => {
instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IContextKeyService, new MockContextKeyService());
instantiationService.stub(IMenuService, new TestMenuService()); instantiationService.stub(IMenuService, new TestMenuService());
const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local', getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); } };
instantiationService.stub(IExtensionManagementServerService, <Partial<IExtensionManagementServerService>>{ instantiationService.stub(IExtensionManagementServerService, <Partial<IExtensionManagementServerService>>{
get localExtensionManagementServer(): IExtensionManagementServer { get localExtensionManagementServer(): IExtensionManagementServer {
return localExtensionManagementServer; return localExtensionManagementServer;

View file

@ -11,7 +11,7 @@ import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurat
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import { import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension,
DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult, CURRENT_TARGET_PLATFORM
} from 'vs/platform/extensionManagement/common/extensionManagement'; } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
@ -104,7 +104,8 @@ suite('ExtensionsWorkbenchServiceTest', () => {
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({
id: 'local', id: 'local',
label: 'local', label: 'local',
extensionManagementService: instantiationService.get(IExtensionManagementService) extensionManagementService: instantiationService.get(IExtensionManagementService),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}, null, null)); }, null, null));
instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
@ -1471,12 +1472,14 @@ suite('ExtensionsWorkbenchServiceTest', () => {
const localExtensionManagementServer: IExtensionManagementServer = { const localExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-local', id: 'vscode-local',
label: 'local', label: 'local',
extensionManagementService: localExtensionManagementService || createExtensionManagementService() extensionManagementService: localExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}; };
const remoteExtensionManagementServer: IExtensionManagementServer = { const remoteExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-remote', id: 'vscode-remote',
label: 'remote', label: 'remote',
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService() extensionManagementService: remoteExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}; };
return { return {
_serviceBrand: undefined, _serviceBrand: undefined,

View file

@ -6,14 +6,15 @@
import { Event } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { createDecorator, refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator, refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtension, ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IExtension, ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, ILocalExtension, InstallOptions, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, ILocalExtension, InstallOptions, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections'; import { IStringDictionary } from 'vs/base/common/collections';
export interface IExtensionManagementServer { export interface IExtensionManagementServer {
id: string; readonly id: string;
label: string; readonly label: string;
extensionManagementService: IExtensionManagementService; readonly extensionManagementService: IExtensionManagementService;
getTargetPlatform(): Promise<TargetPlatform>;
} }
export const IExtensionManagementServerService = createDecorator<IExtensionManagementServerService>('extensionManagementServerService'); export const IExtensionManagementServerService = createDecorator<IExtensionManagementServerService>('extensionManagementServerService');
@ -40,8 +41,8 @@ export interface IWorkbenchExtensionManagementService extends IExtensionManageme
installWebExtension(location: URI): Promise<ILocalExtension>; installWebExtension(location: URI): Promise<ILocalExtension>;
installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]>; installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]>;
updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise<ILocalExtension>; updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions): Promise<ILocalExtension>;
getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null;
} }
export const enum EnablementState { export const enum EnablementState {

View file

@ -16,7 +16,7 @@ import { WebExtensionManagementService } from 'vs/workbench/services/extensionMa
import { IExtension } from 'vs/platform/extensions/common/extensions'; import { IExtension } from 'vs/platform/extensions/common/extensions';
import { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService'; import { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getTargetPlatformFromOS, IExtensionGalleryService, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IProductService } from 'vs/platform/product/common/productService'; import { IProductService } from 'vs/platform/product/common/productService';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
@ -40,10 +40,18 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
const remoteAgentConnection = remoteAgentService.getConnection(); const remoteAgentConnection = remoteAgentService.getConnection();
if (remoteAgentConnection) { if (remoteAgentConnection) {
const extensionManagementService = new WebRemoteExtensionManagementService(remoteAgentConnection.getChannel<IChannel>('extensions'), galleryService, configurationService, productService, extensionManifestPropertiesService); const extensionManagementService = new WebRemoteExtensionManagementService(remoteAgentConnection.getChannel<IChannel>('extensions'), galleryService, configurationService, productService, extensionManifestPropertiesService);
const remoteEnvironemntPromise = remoteAgentService.getEnvironment();
this.remoteExtensionManagementServer = { this.remoteExtensionManagementServer = {
id: 'remote', id: 'remote',
extensionManagementService, extensionManagementService,
get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); } get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); },
async getTargetPlatform() {
const remoteEnvironment = await remoteEnvironemntPromise;
if (remoteEnvironment) {
return getTargetPlatformFromOS(remoteEnvironment.os, remoteEnvironment.arch);
}
throw new Error('Cannot get remote environment');
}
}; };
} }
if (isWeb) { if (isWeb) {
@ -51,7 +59,8 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
this.webExtensionManagementServer = { this.webExtensionManagementServer = {
id: 'web', id: 'web',
extensionManagementService, extensionManagementService,
label: localize('browser', "Browser") label: localize('browser', "Browser"),
getTargetPlatform() { return Promise.resolve(TargetPlatform.WEB); }
}; };
} }
} }

View file

@ -230,7 +230,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
return false; return false;
} }
async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise<ILocalExtension> { async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
const server = this.getServer(extension); const server = this.getServer(extension);
if (!server) { if (!server) {
return Promise.reject(`Invalid location ${extension.location.toString()}`); return Promise.reject(`Invalid location ${extension.location.toString()}`);
@ -245,7 +245,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
servers.push(server); servers.push(server);
} }
return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local);
} }
async installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]> { async installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]> {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionType, IExtensionIdentifier, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { ExtensionType, IExtensionIdentifier, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, InstallOptions, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
@ -44,7 +44,7 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
} }
async canInstall(gallery: IGalleryExtension): Promise<boolean> { async canInstall(gallery: IGalleryExtension): Promise<boolean> {
const compatibleExtension = await this.galleryService.getCompatibleExtension(gallery); const compatibleExtension = await this.galleryService.getCompatibleExtension(gallery, TargetPlatform.WEB);
if (!compatibleExtension) { if (!compatibleExtension) {
return false; return false;
} }

View file

@ -155,7 +155,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
} }
if (extensionIds.length) { if (extensionIds.length) {
const galleryExtensions = await this.galleryService.getExtensions(extensionIds, CancellationToken.None); const galleryExtensions = await this.galleryService.getExtensions(extensionIds.map(id => ({ id })), CancellationToken.None);
const missingExtensions = extensionIds.filter(id => !galleryExtensions.find(({ identifier }) => areSameExtensions(identifier, { id }))); const missingExtensions = extensionIds.filter(id => !galleryExtensions.find(({ identifier }) => areSameExtensions(identifier, { id })));
if (missingExtensions.length) { if (missingExtensions.length) {
this.logService.info('Cannot find static extensions from gallery', missingExtensions); this.logService.info('Cannot find static extensions from gallery', missingExtensions);

View file

@ -15,6 +15,7 @@ import { NativeRemoteExtensionManagementService } from 'vs/workbench/services/ex
import { ILabelService } from 'vs/platform/label/common/label'; import { ILabelService } from 'vs/platform/label/common/label';
import { IExtension } from 'vs/platform/extensions/common/extensions'; import { IExtension } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { getTargetPlatformFromOS, CURRENT_TARGET_PLATFORM } from 'vs/platform/extensionManagement/common/extensionManagement';
export class ExtensionManagementServerService implements IExtensionManagementServerService { export class ExtensionManagementServerService implements IExtensionManagementServerService {
@ -33,14 +34,22 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
) { ) {
const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions')); const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions'));
this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local"), getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); } };
const remoteAgentConnection = remoteAgentService.getConnection(); const remoteAgentConnection = remoteAgentService.getConnection();
if (remoteAgentConnection) { if (remoteAgentConnection) {
const extensionManagementService = instantiationService.createInstance(NativeRemoteExtensionManagementService, remoteAgentConnection.getChannel<IChannel>('extensions'), this.localExtensionManagementServer); const extensionManagementService = instantiationService.createInstance(NativeRemoteExtensionManagementService, remoteAgentConnection.getChannel<IChannel>('extensions'), this.localExtensionManagementServer);
const remoteEnvironemntPromise = remoteAgentService.getEnvironment();
this.remoteExtensionManagementServer = { this.remoteExtensionManagementServer = {
id: 'remote', id: 'remote',
extensionManagementService, extensionManagementService,
get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); } get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); },
async getTargetPlatform() {
const remoteEnvironment = await remoteEnvironemntPromise;
if (remoteEnvironment) {
return getTargetPlatformFromOS(remoteEnvironment.os, remoteEnvironment.arch);
}
throw new Error('Cannot get remote environment');
}
}; };
} }
} }

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation, InstallOptions, InstallVSIXOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation, InstallOptions, InstallVSIXOptions, getTargetPlatformFromOS } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
@ -22,23 +22,22 @@ import { IExtensionManagementServer } from 'vs/workbench/services/extensionManag
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { Promises } from 'vs/base/common/async'; import { Promises } from 'vs/base/common/async';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class NativeRemoteExtensionManagementService extends WebRemoteExtensionManagementService implements IExtensionManagementService { export class NativeRemoteExtensionManagementService extends WebRemoteExtensionManagementService implements IExtensionManagementService {
private readonly localExtensionManagementService: IExtensionManagementService;
constructor( constructor(
channel: IChannel, channel: IChannel,
localExtensionManagementServer: IExtensionManagementServer, private readonly localExtensionManagementServer: IExtensionManagementServer,
@ILogService private readonly logService: ILogService, @ILogService private readonly logService: ILogService,
@IExtensionGalleryService galleryService: IExtensionGalleryService, @IExtensionGalleryService galleryService: IExtensionGalleryService,
@IConfigurationService configurationService: IConfigurationService, @IConfigurationService configurationService: IConfigurationService,
@IProductService productService: IProductService, @IProductService productService: IProductService,
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService, @IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
) { ) {
super(channel, galleryService, configurationService, productService, extensionManifestPropertiesService); super(channel, galleryService, configurationService, productService, extensionManifestPropertiesService);
this.localExtensionManagementService = localExtensionManagementServer.extensionManagementService;
} }
override async install(vsix: URI, options?: InstallVSIXOptions): Promise<ILocalExtension> { override async install(vsix: URI, options?: InstallVSIXOptions): Promise<ILocalExtension> {
@ -85,7 +84,11 @@ export class NativeRemoteExtensionManagementService extends WebRemoteExtensionMa
} }
private async downloadCompatibleAndInstall(extension: IGalleryExtension, installed: ILocalExtension[], installOptions: InstallOptions): Promise<ILocalExtension> { private async downloadCompatibleAndInstall(extension: IGalleryExtension, installed: ILocalExtension[], installOptions: InstallOptions): Promise<ILocalExtension> {
const compatible = await this.galleryService.getCompatibleExtension(extension); const remoteEnvironment = await this.remoteAgentService.getEnvironment();
if (!remoteEnvironment) {
return Promise.reject(new Error('Cannot get the remote environment'));
}
const compatible = await this.galleryService.getCompatibleExtension(extension, getTargetPlatformFromOS(remoteEnvironment.os, remoteEnvironment.arch));
if (!compatible) { if (!compatible) {
return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.version))); return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.version)));
} }
@ -99,11 +102,11 @@ export class NativeRemoteExtensionManagementService extends WebRemoteExtensionMa
private async installUIDependenciesAndPackedExtensions(local: ILocalExtension): Promise<void> { private async installUIDependenciesAndPackedExtensions(local: ILocalExtension): Promise<void> {
const uiExtensions = await this.getAllUIDependenciesAndPackedExtensions(local.manifest, CancellationToken.None); const uiExtensions = await this.getAllUIDependenciesAndPackedExtensions(local.manifest, CancellationToken.None);
const installed = await this.localExtensionManagementService.getInstalled(); const installed = await this.localExtensionManagementServer.extensionManagementService.getInstalled();
const toInstall = uiExtensions.filter(e => installed.every(i => !areSameExtensions(i.identifier, e.identifier))); const toInstall = uiExtensions.filter(e => installed.every(i => !areSameExtensions(i.identifier, e.identifier)));
if (toInstall.length) { if (toInstall.length) {
this.logService.info(`Installing UI dependencies and packed extensions of '${local.identifier.id}' locally`); this.logService.info(`Installing UI dependencies and packed extensions of '${local.identifier.id}' locally`);
await Promises.settled(toInstall.map(d => this.localExtensionManagementService.installFromGallery(d))); await Promises.settled(toInstall.map(d => this.localExtensionManagementServer.extensionManagementService.installFromGallery(d)));
} }
} }
@ -131,7 +134,7 @@ export class NativeRemoteExtensionManagementService extends WebRemoteExtensionMa
return Promise.resolve(); return Promise.resolve();
} }
const extensions = await this.galleryService.getExtensions(toGet, token); const extensions = await this.galleryService.getExtensions(toGet.map(id => ({ id })), token);
const manifests = await Promise.all(extensions.map(e => this.galleryService.getManifest(e, token))); const manifests = await Promise.all(extensions.map(e => this.galleryService.getManifest(e, token)));
const extensionsManifests: IExtensionManifest[] = []; const extensionsManifests: IExtensionManifest[] = [];
for (let idx = 0; idx < extensions.length; idx++) { for (let idx = 0; idx < extensions.length; idx++) {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as assert from 'assert'; import * as assert from 'assert';
import * as sinon from 'sinon'; import * as sinon from 'sinon';
import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, InstallExtensionEvent, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, CURRENT_TARGET_PLATFORM } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/browser/extensionEnablementService'; import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/browser/extensionEnablementService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
@ -64,7 +64,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService {
onDidInstallExtensions: new Emitter<readonly InstallExtensionResult[]>().event, onDidInstallExtensions: new Emitter<readonly InstallExtensionResult[]>().event,
onUninstallExtension: new Emitter<IExtensionIdentifier>().event, onUninstallExtension: new Emitter<IExtensionIdentifier>().event,
onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event, onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event,
} },
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}, null, null)); }, null, null));
const workbenchExtensionManagementService = instantiationService.get(IWorkbenchExtensionManagementService) || instantiationService.stub(IWorkbenchExtensionManagementService, instantiationService.createInstance(ExtensionManagementService)); const workbenchExtensionManagementService = instantiationService.get(IWorkbenchExtensionManagementService) || instantiationService.stub(IWorkbenchExtensionManagementService, instantiationService.createInstance(ExtensionManagementService));
const workspaceTrustManagementService = instantiationService.get(IWorkspaceTrustManagementService) || instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService()); const workspaceTrustManagementService = instantiationService.get(IWorkspaceTrustManagementService) || instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
@ -128,7 +129,8 @@ suite('ExtensionEnablementService Test', () => {
onDidInstallExtensions: didInstallEvent.event, onDidInstallExtensions: didInstallEvent.event,
onDidUninstallExtension: didUninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event,
getInstalled: () => Promise.resolve(installed) getInstalled: () => Promise.resolve(installed)
} },
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}, null, null)); }, null, null));
instantiationService.stub(IWorkbenchExtensionManagementService, instantiationService.createInstance(ExtensionManagementService)); instantiationService.stub(IWorkbenchExtensionManagementService, instantiationService.createInstance(ExtensionManagementService));
testObject = new TestExtensionEnablementService(instantiationService); testObject = new TestExtensionEnablementService(instantiationService);
@ -920,7 +922,8 @@ function anExtensionManagementServer(authority: string, instantiationService: Te
return { return {
id: authority, id: authority,
label: authority, label: authority,
extensionManagementService: instantiationService.get(IExtensionManagementService) extensionManagementService: instantiationService.get(IExtensionManagementService),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}; };
} }

View file

@ -27,6 +27,7 @@ import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/commo
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { CancellationToken } from 'vs/base/common/cancellation';
const FIVE_MINUTES = 5 * 60 * 1000; const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_SECONDS = 30 * 1000; const THIRTY_SECONDS = 30 * 1000;
@ -258,7 +259,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
let galleryExtension: IGalleryExtension | undefined; let galleryExtension: IGalleryExtension | undefined;
try { try {
galleryExtension = await this.galleryService.getCompatibleExtension(extensionIdentifier) ?? undefined; galleryExtension = (await this.galleryService.getExtensions([extensionIdentifier], CancellationToken.None))[0] ?? undefined;
} catch (err) { } catch (err) {
return; return;
} }

View file

@ -43,6 +43,7 @@ import { updateProxyConfigurationsScope } from 'vs/platform/request/common/reque
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { CancellationToken } from 'vs/base/common/cancellation';
export class ExtensionService extends AbstractExtensionService implements IExtensionService { export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@ -526,7 +527,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
label: nls.localize('install', 'Install and Reload'), label: nls.localize('install', 'Install and Reload'),
run: async () => { run: async () => {
sendTelemetry('install'); sendTelemetry('install');
const galleryExtension = await this._extensionGalleryService.getCompatibleExtension({ id: resolverExtensionId }); const [galleryExtension] = await this._extensionGalleryService.getExtensions([{ id: resolverExtensionId }], CancellationToken.None);
if (galleryExtension) { if (galleryExtension) {
await this._extensionManagementService.installFromGallery(galleryExtension); await this._extensionManagementService.installFromGallery(galleryExtension);
await this._hostService.reload(); await this._hostService.reload();

View file

@ -42,6 +42,7 @@ export interface IRemoteAgentEnvironmentDTO {
workspaceStorageHome: UriComponents; workspaceStorageHome: UriComponents;
userHome: UriComponents; userHome: UriComponents;
os: platform.OperatingSystem; os: platform.OperatingSystem;
arch: string;
marks: performance.PerformanceMark[]; marks: performance.PerformanceMark[];
useHostProxy: boolean; useHostProxy: boolean;
} }
@ -67,6 +68,7 @@ export class RemoteExtensionEnvironmentChannelClient {
workspaceStorageHome: URI.revive(data.workspaceStorageHome), workspaceStorageHome: URI.revive(data.workspaceStorageHome),
userHome: URI.revive(data.userHome), userHome: URI.revive(data.userHome),
os: data.os, os: data.os,
arch: data.arch,
marks: data.marks, marks: data.marks,
useHostProxy: data.useHostProxy useHostProxy: data.useHostProxy
}; };