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",
"version": "1.60.0",
"distro": "d2c625635aa2168bd2391205e7f34a5249af143b",
"distro": "c81cac1012f4ca9187c2e838d5272354d251a617",
"author": {
"name": "Microsoft Corporation"
},

View file

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

View file

@ -5,7 +5,7 @@
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;
// Native sandbox environment
@ -13,6 +13,7 @@ if (typeof globals.vscode !== 'undefined' && typeof globals.vscode.process !== '
const sandboxProcess: INodeProcess = globals.vscode.process;
safeProcess = {
get platform() { return sandboxProcess.platform; },
get arch() { return sandboxProcess.arch; },
get env() { return sandboxProcess.env; },
cwd() { return sandboxProcess.cwd(); },
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') {
safeProcess = {
get platform() { return process.platform; },
get arch() { return process.arch; },
get env() { return process.env; },
cwd() { return process.env['VSCODE_CWD'] || process.cwd(); },
nextTick(callback: (...args: any[]) => void): void { return process.nextTick!(callback); }
@ -35,6 +37,7 @@ else {
// Supported
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); },
// Unsupported
@ -70,3 +73,10 @@ export const platform = safeProcess.platform;
* environments.
*/
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 {
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';
import { areSameExtensions, ExtensionIdentifierWithVersion, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
@ -87,7 +87,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
try {
extension = await this.checkAndGetCompatibleVersion(extension);
extension = await this.checkAndGetCompatibleVersion(extension, !options.installGivenVersion);
} catch (error) {
this.logService.error(getErrorMessage(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))) {
continue;
}
const compatibleExtension = await this.checkAndGetCompatibleVersion(galleryExtension);
const compatibleExtension = await this.checkAndGetCompatibleVersion(galleryExtension, true);
if (!await this.canInstall(compatibleExtension)) {
this.logService.info('Skipping the extension as it cannot be installed', compatibleExtension.identifier.id);
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)));
}
private async checkAndGetCompatibleVersion(extension: IGalleryExtension): Promise<IGalleryExtension> {
private async checkAndGetCompatibleVersion(extension: IGalleryExtension, fetchCompatibleVersion: boolean): Promise<IGalleryExtension> {
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);
}
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) {
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.
*--------------------------------------------------------------------------------------------*/
import { distinct } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { canceled, getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
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 { IHeaders, IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request';
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 { adoptToGalleryExtensionId, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
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, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IFileService } from 'vs/platform/files/common/files';
@ -41,6 +42,7 @@ interface IRawGalleryExtensionVersion {
readonly fallbackAssetUri: string;
readonly files: IRawGalleryExtensionFile[];
readonly properties?: IRawGalleryExtensionProperty[];
readonly targetPlatform?: string;
}
interface IRawGalleryExtensionStatistics {
@ -270,8 +272,8 @@ function getRepositoryAsset(version: IRawGalleryExtensionVersion): IGalleryExten
function getDownloadAsset(version: IRawGalleryExtensionVersion): IGalleryExtensionAsset {
return {
uri: `${version.fallbackAssetUri}/${AssetType.VSIX}?redirect=true`,
fallbackUri: `${version.fallbackAssetUri}/${AssetType.VSIX}`
uri: `${version.fallbackAssetUri}/${AssetType.VSIX}?redirect=true${version.targetPlatform ? `&targetPlatform=${version.targetPlatform}` : ''}`,
fallbackUri: `${version.fallbackAssetUri}/${AssetType.VSIX}${version.targetPlatform ? `?targetPlatform=${version.targetPlatform}` : ''}`
};
}
@ -310,7 +312,75 @@ function getIsPreview(flags: string): boolean {
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>{
manifest: getVersionAsset(version, AssetType.Manifest),
readme: getVersionAsset(version, AssetType.Details),
@ -341,14 +411,16 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
tags: galleryExtension.tags || [],
releaseDate: Date.parse(galleryExtension.releaseDate),
lastUpdated: Date.parse(galleryExtension.lastUpdated),
webExtension: !!galleryExtension.tags?.includes(WEB_EXTENSION_TAG),
allTargetPlatforms,
assets,
properties: {
dependencies: getExtensions(version, PropertyType.Dependency),
extensionPack: getExtensions(version, PropertyType.ExtensionPack),
engine: getEngine(version),
localizedLanguages: getLocalizedLanguages(version),
targetPlatform: getTargetPlatform(version),
},
preview: getIsPreview(galleryExtension.flags),
/* __GDPR__FRAGMENT__
"GalleryExtensionTelemetryData2" : {
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
@ -359,7 +431,6 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
index: ((query.pageNumber - 1) * query.pageSize) + index,
querySource
},
preview: getIsPreview(galleryExtension.flags)
};
}
@ -400,29 +471,48 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return !!this.extensionsGalleryUrl;
}
async getExtensions(names: string[], token: CancellationToken): Promise<IGalleryExtension[]> {
async getExtensions(identifiers: ReadonlyArray<IExtensionIdentifier | IExtensionIdentifierWithVersion>, token: CancellationToken): Promise<IGalleryExtension[]> {
const result: IGalleryExtension[] = [];
let { total, firstPage: pageResult, getPage } = await this.query({ names, pageSize: names.length }, token);
result.push(...pageResult);
for (let pageIndex = 1; result.length < total; pageIndex++) {
pageResult = await getPage(pageIndex, token);
if (pageResult.length) {
result.push(...pageResult);
let query = new Query()
.withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, identifiers.length)
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
.withFilter(FilterType.ExtensionName, ...identifiers.map(({ id }) => id.toLowerCase()));
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 {
break;
result.push(toExtensionWithLatestVersion(galleryExtension, index, query));
}
}
return result;
}
async getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
return this.getCompatibleExtensionByEngine(arg1, version);
}
private async getCompatibleExtensionByEngine(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
async getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, targetPlatform: TargetPlatform): Promise<IGalleryExtension | null> {
const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1;
if (extension && extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version, this.productService.date)) {
return extension;
if (extension) {
if (isNotWebExtensionInWebTargetPlatform(extension.allTargetPlatforms, targetPlatform)) {
return null;
}
if (await this.isExtensionCompatible(extension, targetPlatform)) {
return extension;
}
}
const { id, uuid } = extension ? extension.identifier : <IExtensionIdentifier>arg1;
let query = new Query()
@ -442,24 +532,53 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return null;
}
if (version) {
const versionAsset = rawExtension.versions.filter(v => v.version === version)[0];
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;
}
}
const allTargetPlatforms = getAllTargetPlatforms(rawExtension);
if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, targetPlatform)) {
return null;
}
const rawVersion = await this.getLastValidExtensionVersion(rawExtension, rawExtension.versions);
if (rawVersion) {
return toExtension(rawExtension, rawVersion, 0, query);
for (let rawVersion of rawExtension.versions) {
// set engine property if does not exist
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;
}
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(options: IQueryOptions, token: CancellationToken): 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 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) => {
if (ct.isCancellationRequested) {
throw canceled();
}
const nextPageQuery = query.withPage(pageIndex + 1);
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>;
@ -635,8 +754,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : '';
const downloadAsset = operationParam ? {
uri: `${extension.assets.download.uri}&${operationParam}=true`,
fallbackUri: `${extension.assets.download.fallbackUri}?${operationParam}=true`
uri: `${extension.assets.download.uri}${URI.parse(extension.assets.download.uri).query ? '&' : '?'}${operationParam}=true`,
fallbackUri: `${extension.assets.download.fallbackUri}${URI.parse(extension.assets.download.fallbackUri).query ? '&' : '?'}${operationParam}=true`
} : extension.assets.download;
const context = await this.getAsset(downloadAsset);
@ -662,6 +781,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
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> {
const asset = extension.assets.coreTranslations.filter(t => t[0] === languageId.toUpperCase())[0];
if (asset) {
@ -681,7 +810,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return '';
}
async getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]> {
async getAllCompatibleVersions(extension: IGalleryExtension, targetPlatform: TargetPlatform): Promise<IGalleryExtensionVersion[]> {
let query = new Query()
.withFlags(Flags.IncludeVersions, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, 1)
@ -693,22 +822,23 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
query = query.withFilter(FilterType.ExtensionName, extension.identifier.id);
}
const result: IGalleryExtensionVersion[] = [];
const { galleryExtensions } = await this.queryGallery(query, CancellationToken.None);
if (galleryExtensions.length) {
if (compatible) {
await Promise.all(galleryExtensions[0].versions.map(async v => {
let engine: string | undefined;
try {
engine = await this.getEngine(v);
} catch (error) { /* Ignore error and skip version */ }
if (engine && isEngineValid(engine, this.productService.version, this.productService.date)) {
result.push({ version: v!.version, date: v!.lastUpdated });
}
}));
} else {
result.push(...galleryExtensions[0].versions.map(v => ({ version: v.version, date: v.lastUpdated })));
}
if (!galleryExtensions.length) {
return [];
}
const allTargetPlatforms = getAllTargetPlatforms(galleryExtensions[0]);
if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, targetPlatform)) {
return [];
}
const result: IGalleryExtensionVersion[] = [];
for (const version of galleryExtensions[0].versions) {
try {
if (await this.isRawExtensionVersionCompatible(version, allTargetPlatforms, targetPlatform)) {
result.push({ version: version!.version, date: version!.lastUpdated });
}
} catch (error) { /* Ignore error and skip version */ }
}
return result;
}
@ -751,63 +881,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
}
private async getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion | null> {
const version = this.getLastValidExtensionVersionFromProperties(extension, versions);
if (version) {
return version;
}
return this.getLastValidExtensionVersionRecursively(extension, versions);
}
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;
private async getEngine(rawExtensionVersion: IRawGalleryExtensionVersion): Promise<string> {
let engine = getEngine(rawExtensionVersion);
if (!engine) {
const manifest = await this.getManifestFromRawExtensionVersion(rawExtensionVersion, CancellationToken.None);
if (!manifest) {
throw new Error('Manifest was not found');
}
engine = manifest.engines.vscode;
}
return null;
}
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 }]
};
return engine;
}
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 { FileAccess } from 'vs/base/common/network';
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 { localize } from 'vs/nls';
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 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 {
dependencies?: string[];
extensionPack?: string[];
engine?: string;
localizedLanguages?: string[];
targetPlatform: TargetPlatform;
}
export interface IGalleryExtensionAsset {
@ -88,11 +180,11 @@ export interface IGalleryExtension {
tags: readonly string[];
releaseDate: number;
lastUpdated: number;
preview: boolean;
allTargetPlatforms: TargetPlatform[];
assets: IGalleryExtensionAssets;
properties: IGalleryExtensionProperties;
telemetryData: any;
preview: boolean;
webExtension: boolean;
}
export interface IGalleryMetadata {
@ -161,17 +253,18 @@ export interface IExtensionGalleryService {
isEnabled(): boolean;
query(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>;
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>;
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest | null>;
getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null>;
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]>;
getExtensionsReport(): Promise<IReportedExtension[]>;
getCompatibleExtension(extension: IGalleryExtension): Promise<IGalleryExtension | null>;
getCompatibleExtension(id: IExtensionIdentifier, version?: string): Promise<IGalleryExtension | null>;
isExtensionCompatible(extension: IGalleryExtension, targetPlatform: TargetPlatform): Promise<boolean>;
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 {
@ -202,8 +295,8 @@ export class ExtensionManagementError extends Error {
}
}
export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, donotIncludePackAndDependencies?: boolean };
export type InstallVSIXOptions = InstallOptions & { installOnlyNewlyAddedFromExtensionPack?: boolean };
export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, donotIncludePackAndDependencies?: boolean, installGivenVersion?: boolean };
export type InstallVSIXOptions = Omit<InstallOptions, 'installGivenVersion'> & { installOnlyNewlyAddedFromExtensionPack?: boolean };
export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean };
export interface IExtensionManagementParticipant {

View file

@ -199,23 +199,11 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
}
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>();
await Promise.all([
(async () => {
const result = await this.extensionGalleryService.getExtensions(extensionIds, CancellationToken.None);
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);
}
}))
]);
const result = await this.extensionGalleryService.getExtensions(extensions, CancellationToken.None);
for (const extension of result) {
galleryExtensions.set(extension.identifier.id.toLowerCase(), extension);
}
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));
}
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));
return manifest;
} catch (error) {

View file

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

View file

@ -14,7 +14,7 @@ import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
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 { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
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
const extension = await this.extensionGalleryService.getCompatibleExtension(e.identifier);
const extension = await this.extensionGalleryService.getCompatibleExtension(e.identifier, CURRENT_TARGET_PLATFORM);
/* Update extension state only if
* extension is installed and version is same as synced version or

View file

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

View file

@ -14,7 +14,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
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 { 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 { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
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 { CancellationToken } from 'vs/base/common/cancellation';
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 { ILabelService } from 'vs/platform/label/common/label';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@ -981,36 +980,31 @@ export class InstallAnotherVersionAction extends ExtensionAction {
}
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) {
return Promise.resolve();
return;
}
return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true })
.then(async pick => {
if (pick) {
if (this.extension!.version === pick.id) {
return Promise.resolve();
}
try {
if (pick.latest) {
await this.extensionsWorkbenchService.install(this.extension!);
} 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();
}
const allVersions = await this.extensionGalleryService.getAllCompatibleVersions(this.extension!.gallery!, await this.extension!.server!.getTargetPlatform());
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 }));
const pick = await this.quickInputService.pick(versionEntries, { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true });
if (pick) {
if (this.extension!.version === pick.id) {
return;
}
try {
if (pick.latest) {
await this.extensionsWorkbenchService.install(this.extension!);
} else {
await this.extensionsWorkbenchService.installVersion(this.extension!, pick.id);
}
return null;
});
}
private getVersionEntries(): Promise<(IQuickPickItem & { latest: boolean, id: string })[]> {
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 })));
} catch (error) {
this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, pick.latest ? this.extension!.latestVersion : pick.id, InstallOperation.Install, error).run();
}
}
return null;
}
}
@ -2011,7 +2005,7 @@ export class ExtensionStatusAction extends ExtensionAction {
&& !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;
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));
} 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)`);
@ -2283,12 +2277,8 @@ export class InstallSpecificVersionOfExtensionAction extends Action {
constructor(
id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@INotificationService private readonly notificationService: INotificationService,
@IHostService private readonly hostService: IHostService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IExtensionService private readonly extensionService: IExtensionService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
) {
super(id, label);
@ -2301,68 +2291,38 @@ export class InstallSpecificVersionOfExtensionAction extends Action {
override async run(): Promise<any> {
const extensionPick = await this.quickInputService.pick(this.getExtensionEntries(), { placeHolder: localize('selectExtension', "Select Extension"), matchOnDetail: true });
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 });
if (versionPick) {
if (extensionPick.extension.version !== versionPick.id) {
await this.install(extensionPick.extension, versionPick.id);
}
}
const action = this.instantiationService.createInstance(InstallAnotherVersionAction);
action.extension = extensionPick.extension;
await action.run();
await this.instantiationService.createInstance(SearchExtensionsAction, extensionPick.extension.identifier.id).run();
}
}
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 versionsPromises: Promise<{ extension: IExtension, versions: IGalleryExtensionVersion[] } | null>[] = [];
const entries: IExtensionPickItem[] = [];
for (const extension of installed) {
if (this.isEnabled(extension)) {
versionsPromises.push(this.extensionGalleryService.getAllVersions(extension.gallery!, true)
.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 {
entries.push({
id: extension.identifier.id,
label: extension.displayName || extension.identifier.id,
description: extension.identifier.id,
extension,
versions
} as (IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] });
});
}
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));
});
});
}
}
return entries.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName));
}
}
interface IExtensionPickItem extends IQuickPickItem {
extension?: IExtension;
extension: IExtension;
}
export abstract class AbstractInstallExtensionsInServerAction extends Action {
@ -2489,9 +2449,10 @@ export class InstallLocalExtensionsInRemoteAction extends AbstractInstallExtensi
protected async installExtensions(localExtensionsToInstall: IExtension[]): Promise<void> {
const galleryExtensions: IGalleryExtension[] = [];
const vsixs: URI[] = [];
const remoteTargetPlatform = await this.extensionManagementServerService.remoteExtensionManagementServer!.getTargetPlatform();
await Promises.settled(localExtensionsToInstall.map(async extension => {
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) {
galleryExtensions.push(gallery);
return;
@ -2537,9 +2498,10 @@ export class InstallRemoteExtensionsInLocalAction extends AbstractInstallExtensi
protected async installExtensions(extensions: IExtension[]): Promise<void> {
const galleryExtensions: IGalleryExtension[] = [];
const vsixs: URI[] = [];
const localTargetPlatform = await this.extensionManagementServerService.localExtensionManagementServer!.getTargetPlatform();
await Promises.settled(extensions.map(async extension => {
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) {
galleryExtensions.push(gallery);
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 {
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';
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';
@ -399,7 +399,7 @@ class Extensions extends Disposable {
}
// Loading the compatible version only there is an engine property
// 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) {
return false;
}
@ -418,7 +418,7 @@ class Extensions extends Disposable {
if (!this.galleryService.isEnabled()) {
return;
}
const compatible = await this.galleryService.getCompatibleExtension(extension.identifier);
const compatible = await this.galleryService.getCompatibleExtension(extension.identifier, await this.server.getTargetPlatform());
if (compatible) {
extension.gallery = compatible;
this._onChange.fire({ extension });
@ -994,7 +994,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
if (this.extensionManagementServerService.webExtensionManagementServer) {
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;
@ -1037,29 +1037,27 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}, () => 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)) {
return Promise.resolve(extension);
return extension;
}
if (!extension.gallery) {
return Promise.reject(new Error('Missing gallery'));
throw new Error('Missing gallery');
}
return this.galleryService.getCompatibleExtension(extension.gallery.identifier, version)
.then(gallery => {
if (!gallery) {
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 () => {
const installed = await this.installFromGallery(extension, gallery);
if (extension.latestVersion !== version) {
this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version));
}
return installed;
}
, gallery.displayName);
});
const [gallery] = await this.galleryService.getExtensions([{ id: extension.gallery.identifier.id, version }], CancellationToken.None);
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 this.installWithProgress(async () => {
const installed = await this.installFromGallery(extension, gallery, { installGivenVersion: true });
if (extension.latestVersion !== version) {
this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version));
}
return installed;
}, gallery.displayName);
}
reinstall(extension: IExtension): Promise<IExtension> {
@ -1138,7 +1136,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
this._onChange.fire(extension);
try {
if (extension.state === ExtensionState.Installed && extension.local) {
await this.extensionManagementService.updateFromGallery(gallery, extension.local);
await this.extensionManagementService.updateFromGallery(gallery, extension.local, installOptions);
} else {
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 {
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';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
@ -110,7 +110,7 @@ async function setupTest() {
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>>{
get localExtensionManagementServer(): IExtensionManagementServer {
return localExtensionManagementServer;
@ -2348,7 +2348,8 @@ function aSingleRemoteExtensionManagementServerService(instantiationService: Tes
const remoteExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-remote',
label: 'remote',
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService()
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
};
return {
_serviceBrand: undefined,
@ -2368,12 +2369,14 @@ function aMultiExtensionManagementServerService(instantiationService: TestInstan
const localExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-local',
label: 'local',
extensionManagementService: localExtensionManagementService || createExtensionManagementService()
extensionManagementService: localExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
};
const remoteExtensionManagementServer: IExtensionManagementServer = {
id: 'vscode-remote',
label: 'remote',
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService()
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService(),
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
};
return {
_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 {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,
DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy, InstallExtensionResult
DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy, InstallExtensionResult, CURRENT_TARGET_PLATFORM
} from 'vs/platform/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';
@ -100,7 +100,7 @@ suite('ExtensionsListView Tests', () => {
instantiationService.stub(IContextKeyService, new MockContextKeyService());
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>>{
get localExtensionManagementServer(): IExtensionManagementServer {
return localExtensionManagementServer;

View file

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

View file

@ -6,14 +6,15 @@
import { Event } from 'vs/base/common/event';
import { createDecorator, refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation';
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 { IStringDictionary } from 'vs/base/common/collections';
export interface IExtensionManagementServer {
id: string;
label: string;
extensionManagementService: IExtensionManagementService;
readonly id: string;
readonly label: string;
readonly extensionManagementService: IExtensionManagementService;
getTargetPlatform(): Promise<TargetPlatform>;
}
export const IExtensionManagementServerService = createDecorator<IExtensionManagementServerService>('extensionManagementServerService');
@ -40,8 +41,8 @@ export interface IWorkbenchExtensionManagementService extends IExtensionManageme
installWebExtension(location: URI): Promise<ILocalExtension>;
installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]>;
updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise<ILocalExtension>;
getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null
updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions): Promise<ILocalExtension>;
getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null;
}
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 { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService';
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 { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
@ -40,10 +40,18 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
const remoteAgentConnection = remoteAgentService.getConnection();
if (remoteAgentConnection) {
const extensionManagementService = new WebRemoteExtensionManagementService(remoteAgentConnection.getChannel<IChannel>('extensions'), galleryService, configurationService, productService, extensionManifestPropertiesService);
const remoteEnvironemntPromise = remoteAgentService.getEnvironment();
this.remoteExtensionManagementServer = {
id: 'remote',
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) {
@ -51,7 +59,8 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
this.webExtensionManagementServer = {
id: 'web',
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;
}
async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise<ILocalExtension> {
async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
const server = this.getServer(extension);
if (!server) {
return Promise.reject(`Invalid location ${extension.location.toString()}`);
@ -245,7 +245,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
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[]> {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
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 { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
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> {
const compatibleExtension = await this.galleryService.getCompatibleExtension(gallery);
const compatibleExtension = await this.galleryService.getCompatibleExtension(gallery, TargetPlatform.WEB);
if (!compatibleExtension) {
return false;
}

View file

@ -155,7 +155,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
}
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 })));
if (missingExtensions.length) {
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 { IExtension } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { getTargetPlatformFromOS, CURRENT_TARGET_PLATFORM } from 'vs/platform/extensionManagement/common/extensionManagement';
export class ExtensionManagementServerService implements IExtensionManagementServerService {
@ -33,14 +34,22 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
) {
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();
if (remoteAgentConnection) {
const extensionManagementService = instantiationService.createInstance(NativeRemoteExtensionManagementService, remoteAgentConnection.getChannel<IChannel>('extensions'), this.localExtensionManagementServer);
const remoteEnvironemntPromise = remoteAgentService.getEnvironment();
this.remoteExtensionManagementServer = {
id: 'remote',
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 { 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 { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
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 { Promises } from 'vs/base/common/async';
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 {
private readonly localExtensionManagementService: IExtensionManagementService;
constructor(
channel: IChannel,
localExtensionManagementServer: IExtensionManagementServer,
private readonly localExtensionManagementServer: IExtensionManagementServer,
@ILogService private readonly logService: ILogService,
@IExtensionGalleryService galleryService: IExtensionGalleryService,
@IConfigurationService configurationService: IConfigurationService,
@IProductService productService: IProductService,
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
) {
super(channel, galleryService, configurationService, productService, extensionManifestPropertiesService);
this.localExtensionManagementService = localExtensionManagementServer.extensionManagementService;
}
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> {
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) {
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> {
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)));
if (toInstall.length) {
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();
}
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 extensionsManifests: IExtensionManifest[] = [];
for (let idx = 0; idx < extensions.length; idx++) {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
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 { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/browser/extensionEnablementService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
@ -64,7 +64,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService {
onDidInstallExtensions: new Emitter<readonly InstallExtensionResult[]>().event,
onUninstallExtension: new Emitter<IExtensionIdentifier>().event,
onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event,
}
},
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}, null, null));
const workbenchExtensionManagementService = instantiationService.get(IWorkbenchExtensionManagementService) || instantiationService.stub(IWorkbenchExtensionManagementService, instantiationService.createInstance(ExtensionManagementService));
const workspaceTrustManagementService = instantiationService.get(IWorkspaceTrustManagementService) || instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
@ -128,7 +129,8 @@ suite('ExtensionEnablementService Test', () => {
onDidInstallExtensions: didInstallEvent.event,
onDidUninstallExtension: didUninstallEvent.event,
getInstalled: () => Promise.resolve(installed)
}
},
getTargetPlatform() { return Promise.resolve(CURRENT_TARGET_PLATFORM); }
}, null, null));
instantiationService.stub(IWorkbenchExtensionManagementService, instantiationService.createInstance(ExtensionManagementService));
testObject = new TestExtensionEnablementService(instantiationService);
@ -920,7 +922,8 @@ function anExtensionManagementServer(authority: string, instantiationService: Te
return {
id: 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 { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { CancellationToken } from 'vs/base/common/cancellation';
const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_SECONDS = 30 * 1000;
@ -258,7 +259,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
let galleryExtension: IGalleryExtension | undefined;
try {
galleryExtension = await this.galleryService.getCompatibleExtension(extensionIdentifier) ?? undefined;
galleryExtension = (await this.galleryService.getExtensions([extensionIdentifier], CancellationToken.None))[0] ?? undefined;
} catch (err) {
return;
}

View file

@ -43,6 +43,7 @@ import { updateProxyConfigurationsScope } from 'vs/platform/request/common/reque
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { CancellationToken } from 'vs/base/common/cancellation';
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'),
run: async () => {
sendTelemetry('install');
const galleryExtension = await this._extensionGalleryService.getCompatibleExtension({ id: resolverExtensionId });
const [galleryExtension] = await this._extensionGalleryService.getExtensions([{ id: resolverExtensionId }], CancellationToken.None);
if (galleryExtension) {
await this._extensionManagementService.installFromGallery(galleryExtension);
await this._hostService.reload();

View file

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