Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)

* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79

* Fix breaks

* Extension management fixes

* Fix breaks in windows bundling

* Fix/skip failing tests

* Update distro

* Add clear to nuget.config

* Add hygiene task

* Bump distro

* Fix hygiene issue

* Add build to hygiene exclusion

* Update distro

* Update hygiene

* Hygiene exclusions

* Update tsconfig

* Bump distro for server breaks

* Update build config

* Update darwin path

* Add done calls to notebook tests

* Skip failing tests

* Disable smoke tests
This commit is contained in:
Karl Burtram
2021-02-09 16:15:05 -08:00
committed by GitHub
parent 6f192f9af5
commit ce612a3d96
1929 changed files with 68012 additions and 34564 deletions

View File

@@ -10,6 +10,7 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { URI } from 'vs/base/common/uri';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getUriFromAmdModule } from 'vs/base/common/amd';
interface IScannedBuiltinExtension {
extensionPath: string;
@@ -62,17 +63,14 @@ export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScanne
}
private _getBuiltinExtensionsUrl(environmentService: IWorkbenchEnvironmentService): URI | undefined {
if (environmentService.options?.builtinExtensionsServiceUrl) {
return URI.parse(environmentService.options?.builtinExtensionsServiceUrl);
}
let enableBuiltinExtensions: boolean;
if (environmentService.options && typeof environmentService.options._enableBuiltinExtensions !== 'undefined') {
enableBuiltinExtensions = environmentService.options._enableBuiltinExtensions;
} else {
enableBuiltinExtensions = environmentService.configuration.remoteAuthority ? false : true;
enableBuiltinExtensions = environmentService.remoteAuthority ? false : true;
}
if (enableBuiltinExtensions) {
return URI.parse(require.toUrl('../../../../../../extensions'));
return getUriFromAmdModule(require, '../../../../../../extensions');
}
return undefined;
}

View File

@@ -0,0 +1,328 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
// --- bisect service
export const IExtensionBisectService = createDecorator<IExtensionBisectService>('IExtensionBisectService');
export interface IExtensionBisectService {
readonly _serviceBrand: undefined;
isDisabledByBisect(extension: IExtension): boolean;
isActive: boolean;
disabledCount: number;
start(extensions: ILocalExtension[]): Promise<void>;
next(seeingBad: boolean): Promise<{ id: string, bad: boolean } | undefined>;
reset(): Promise<void>;
}
class BisectState {
static fromJSON(raw: string | undefined): BisectState | undefined {
if (!raw) {
return undefined;
}
try {
interface Raw extends BisectState { }
const data: Raw = JSON.parse(raw);
return new BisectState(data.extensions, data.low, data.high);
} catch {
return undefined;
}
}
readonly mid: number;
constructor(
readonly extensions: string[],
readonly low: number,
readonly high: number,
) {
this.mid = ((low + high) / 2) | 0;
}
}
class ExtensionBisectService implements IExtensionBisectService {
declare readonly _serviceBrand: undefined;
private static readonly _storageKey = 'extensionBisectState';
private readonly _state: BisectState | undefined;
private readonly _disabled = new Map<string, boolean>();
constructor(
@ILogService logService: ILogService,
@IStorageService private readonly _storageService: IStorageService,
) {
const raw = _storageService.get(ExtensionBisectService._storageKey, StorageScope.GLOBAL);
this._state = BisectState.fromJSON(raw);
if (this._state) {
const { mid, high } = this._state;
for (let i = 0; i < this._state.extensions.length; i++) {
const isDisabled = i >= mid && i < high;
this._disabled.set(this._state.extensions[i], isDisabled);
}
logService.warn('extension BISECT active', [...this._disabled]);
}
}
get isActive() {
return !!this._state;
}
get disabledCount() {
return this._state ? this._state.high - this._state.mid : -1;
}
isDisabledByBisect(extension: IExtension): boolean {
if (!this._state) {
return false;
}
const disabled = this._disabled.get(extension.identifier.id);
return disabled ?? false;
}
async start(extensions: ILocalExtension[]): Promise<void> {
if (this._state) {
throw new Error('invalid state');
}
const extensionIds = extensions.map(ext => ext.identifier.id);
const newState = new BisectState(extensionIds, 0, extensionIds.length);
this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(newState), StorageScope.GLOBAL, StorageTarget.MACHINE);
await this._storageService.flush();
}
async next(seeingBad: boolean): Promise<{ id: string; bad: boolean; } | undefined> {
if (!this._state) {
throw new Error('invalid state');
}
// check if there is only one left
if (this._state.low === this._state.high - 1) {
await this.reset();
return { id: this._state.extensions[this._state.low], bad: seeingBad };
}
// the second half is disabled so if there is still bad it must be
// in the first half
const nextState = new BisectState(
this._state.extensions,
seeingBad ? this._state.low : this._state.mid,
seeingBad ? this._state.mid : this._state.high,
);
this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(nextState), StorageScope.GLOBAL, StorageTarget.MACHINE);
await this._storageService.flush();
return undefined;
}
async reset(): Promise<void> {
this._storageService.remove(ExtensionBisectService._storageKey, StorageScope.GLOBAL);
await this._storageService.flush();
}
}
registerSingleton(IExtensionBisectService, ExtensionBisectService, true);
// --- bisect UI
class ExtensionBisectUi {
static ctxIsBisectActive = new RawContextKey('isExtensionBisectActive', false);
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@IExtensionBisectService private readonly _extensionBisectService: IExtensionBisectService,
@INotificationService private readonly _notificationService: INotificationService,
@ICommandService private readonly _commandService: ICommandService,
) {
if (_extensionBisectService.isActive) {
ExtensionBisectUi.ctxIsBisectActive.bindTo(contextKeyService).set(true);
this._showBisectPrompt();
}
}
private _showBisectPrompt(): void {
const goodPrompt: IPromptChoice = {
label: 'Good now',
run: () => this._commandService.executeCommand('extension.bisect.next', false)
};
const badPrompt: IPromptChoice = {
label: 'This is bad',
run: () => this._commandService.executeCommand('extension.bisect.next', true)
};
const stop: IPromptChoice = {
label: 'Stop Bisect',
run: () => this._commandService.executeCommand('extension.bisect.stop')
};
this._notificationService.prompt(
Severity.Info,
localize('bisect', "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", this._extensionBisectService.disabledCount),
[goodPrompt, badPrompt, stop],
{ sticky: true }
);
}
}
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
ExtensionBisectUi,
LifecyclePhase.Restored
);
registerAction2(class extends Action2 {
constructor() {
super({
id: 'extension.bisect.start',
title: localize('title.start', "Start Extension Bisect"),
category: localize('help', "Help"),
f1: true,
precondition: ExtensionBisectUi.ctxIsBisectActive.negate()
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const dialogService = accessor.get(IDialogService);
const hostService = accessor.get(IHostService);
const extensionManagement = accessor.get(IExtensionManagementService);
const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService);
const extensionsBisect = accessor.get(IExtensionBisectService);
const disabled = new Set(extensionEnablementService.getDisabledExtensions().map(id => id.id));
const extensions = (await extensionManagement.getInstalled(ExtensionType.User)).filter(ext => !disabled.has(ext.identifier.id));
const res = await dialogService.confirm({
message: localize('msg.start', "Extension Bisect"),
detail: localize('detail.start', "Extension Bisect will use binary search to find an extension that causes a problem. During the process the window reloads repeatedly (~{0} times). Each time you must confirm if you are still seeing problems.", 1 + Math.log2(extensions.length) | 0),
primaryButton: localize('msg2', "Start Extension Bisect")
});
if (res.confirmed) {
await extensionsBisect.start(extensions);
hostService.reload();
}
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'extension.bisect.next',
title: localize('title.isBad', "Continue Extension Bisect"),
category: localize('help', "Help"),
f1: true,
precondition: ExtensionBisectUi.ctxIsBisectActive
});
}
async run(accessor: ServicesAccessor, seeingBad: boolean | undefined): Promise<void> {
const dialogService = accessor.get(IDialogService);
const hostService = accessor.get(IHostService);
const bisectService = accessor.get(IExtensionBisectService);
const productService = accessor.get(IProductService);
const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService);
const issueService = accessor.get(IWorkbenchIssueService);
if (!bisectService.isActive) {
return;
}
if (seeingBad === undefined) {
seeingBad = await this._checkForBad(dialogService);
}
if (seeingBad === undefined) {
await bisectService.reset();
hostService.reload();
return;
}
const done = await bisectService.next(seeingBad);
if (!done) {
hostService.reload();
return;
}
if (done.bad) {
// DONE but nothing found
await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"), [], {
detail: localize('done.detail2', "Extension Bisect is done but no extension has been identified. This might be a problem with {0}", productService.nameShort)
});
} else {
// DONE and identified extension
const res = await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"),
[localize('report', "Report Issue & Continue"), localize('done', "Continue")],
// [],
{
detail: localize('done.detail', "Extension Bisect is done and has identified {0} as the extension causing the problem.", done.id),
checkbox: { label: localize('done.disbale', "Keep this extension disabled"), checked: true },
cancelId: 1
}
);
if (res.checkboxChecked) {
await extensionEnablementService.disableExtension({ id: done.id }, undefined);
}
if (res.choice === 0) {
await issueService.openReporter({ extensionId: done.id });
}
}
await bisectService.reset();
hostService.reload();
}
private async _checkForBad(dialogService: IDialogService) {
const options = {
cancelId: 2,
detail: localize('detail.next', "Are you still seeing the problem for which you have started extension bisect?")
};
const res = await dialogService.show(
Severity.Info,
localize('msg.next', "Extension Bisect"),
[localize('next.good', "Good now"), localize('next.bad', "This is bad"), localize('next.stop', "Stop Bisect")],
options
);
if (res.choice === options.cancelId) {
return undefined;
}
return res.choice === 1;
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'extension.bisect.stop',
title: localize('title.stop', "Stop Extension Bisect"),
category: localize('help', "Help"),
f1: true,
precondition: ExtensionBisectUi.ctxIsBisectActive
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const extensionsBisect = accessor.get(IExtensionBisectService);
const hostService = accessor.get(IHostService);
await extensionsBisect.reset();
hostService.reload();
}
});

View File

@@ -12,7 +12,7 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ExtensionType, IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -20,7 +20,11 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions';
import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect';
const SOURCE = 'IWorkbenchExtensionEnablementService';
@@ -42,24 +46,41 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IProductService private readonly productService: IProductService,
@IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService,
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
@IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@INotificationService private readonly notificationService: INotificationService,
@IHostService hostService: IHostService,
@IExtensionBisectService private readonly extensionBisectService: IExtensionBisectService,
) {
super();
this.storageManger = this._register(new StorageManager(storageService));
this._register(this.globalExtensionEnablementService.onDidChangeEnablement(({ extensions, source }) => this.onDidChangeExtensions(extensions, source)));
this._register(extensionManagementService.onDidUninstallExtension(this._onDidUninstallExtension, this));
// delay notification for extensions disabled until workbench restored
if (this.allUserExtensionsDisabled) {
this.lifecycleService.when(LifecyclePhase.Restored).then(() => {
this.notificationService.prompt(Severity.Info, localize('extensionsDisabled', "All installed extensions are temporarily disabled."), [{
label: localize('Reload', "Reload and Enable Extensions"),
run: () => hostService.reload({ disableExtensions: false })
}]);
});
}
}
private get hasWorkspace(): boolean {
return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
}
get allUserExtensionsDisabled(): boolean {
private get allUserExtensionsDisabled(): boolean {
return this.environmentService.disableExtensions === true;
}
getEnablementState(extension: IExtension): EnablementState {
if (this.extensionBisectService.isDisabledByBisect(extension)) {
return EnablementState.DisabledByEnvironemt;
}
if (this._isDisabledInEnv(extension)) {
return EnablementState.DisabledByEnvironemt;
}
@@ -84,12 +105,12 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
private throwErrorIfCannotChangeEnablement(extension: IExtension): void {
if (isLanguagePackExtension(extension.manifest)) {
throw new Error(localize('cannot disable language pack extension', "Cannot disable {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id));
throw new Error(localize('cannot disable language pack extension', "Cannot change enablement of {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id));
}
if (this.userDataAutoSyncService.isEnabled() && this.userDataSyncAccountService.account &&
if (this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncAccountService.account &&
isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) {
throw new Error(localize('cannot disable auth extension', "Cannot disable {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id));
throw new Error(localize('cannot disable auth extension', "Cannot change enablement {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id));
}
}
@@ -110,7 +131,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
throw new Error(localize('noWorkspace', "No workspace."));
}
if (isAuthenticaionProviderExtension(extension.manifest)) {
throw new Error(localize('cannot disable auth extension in workspace', "Cannot disable {0} extension in workspace because it contributes authentication providers", extension.manifest.displayName || extension.identifier.id));
throw new Error(localize('cannot disable auth extension in workspace', "Cannot change enablement of {0} extension in workspace because it contributes authentication providers", extension.manifest.displayName || extension.identifier.id));
}
}
@@ -170,7 +191,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
private _isDisabledInEnv(extension: IExtension): boolean {
if (this.allUserExtensionsDisabled) {
return extension.type === ExtensionType.User;
return !extension.isBuiltin;
}
const disabledExtensions = this.environmentService.disableExtensions;
if (Array.isArray(disabledExtensions)) {

View File

@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
class ExtensionUrlTrustService implements IExtensionUrlTrustService {
declare readonly _serviceBrand: undefined;
async isExtensionUrlTrusted(): Promise<boolean> {
return false;
}
}
registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService);

View File

@@ -5,13 +5,11 @@
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, ILocalExtension, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri';
import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IStringDictionary } from 'vs/base/common/collections';
export const IExtensionManagementServerService = createDecorator<IExtensionManagementServerService>('extensionManagementServerService');
import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; // {{ SQL CARBON EDIT }}
import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
export interface IExtensionManagementServer {
id: string;
@@ -19,6 +17,7 @@ export interface IExtensionManagementServer {
extensionManagementService: IExtensionManagementService;
}
export const IExtensionManagementServerService = createDecorator<IExtensionManagementServerService>('extensionManagementServerService');
export interface IExtensionManagementServerService {
readonly _serviceBrand: undefined;
readonly localExtensionManagementServer: IExtensionManagementServer | null;
@@ -27,6 +26,14 @@ export interface IExtensionManagementServerService {
getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null;
}
export const IWorkbenchExtensioManagementService = createDecorator<IWorkbenchExtensioManagementService>('extensionManagementService');
export interface IWorkbenchExtensioManagementService extends IExtensionManagementService {
readonly _serviceBrand: undefined;
installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]>;
updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise<ILocalExtension>;
getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null
}
export const enum EnablementState {
DisabledByExtensionKind,
DisabledByEnvironemt,
@@ -41,8 +48,6 @@ export const IWorkbenchExtensionEnablementService = createDecorator<IWorkbenchEx
export interface IWorkbenchExtensionEnablementService {
readonly _serviceBrand: undefined;
readonly allUserExtensionsDisabled: boolean;
/**
* Event to listen on for extension enablement changes
*/
@@ -111,48 +116,17 @@ export interface IExtensionRecommendation {
sources: ExtensionRecommendationSource[];
}
export const enum ExtensionRecommendationReason {
Workspace,
File,
Executable,
WorkspaceConfig,
DynamicWorkspace,
Experimental,
Application,
}
export interface IExtensionRecommendationReson {
export interface IExtensionRecommendationReason {
reasonId: ExtensionRecommendationReason;
reasonText: string;
}
export const IExtensionRecommendationsService = createDecorator<IExtensionRecommendationsService>('extensionRecommendationsService');
export interface IExtensionRecommendationsService {
readonly _serviceBrand: undefined;
getAllRecommendationsWithReason(): IStringDictionary<IExtensionRecommendationReson>;
getImportantRecommendations(): Promise<IExtensionRecommendation[]>;
getOtherRecommendations(): Promise<IExtensionRecommendation[]>;
getFileBasedRecommendations(): IExtensionRecommendation[];
getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>;
getConfigBasedRecommendations(): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>;
getWorkspaceRecommendations(): Promise<IExtensionRecommendation[]>;
getKeymapRecommendations(): IExtensionRecommendation[];
toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void;
getIgnoredRecommendations(): ReadonlyArray<string>;
onRecommendationChange: Event<RecommendationChangeNotification>;
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]>; // {{SQL CARBON EDIT}}
promptRecommendedExtensionsByScenario(scenarioType: string): void; // {{SQL CARBON EDIT}}
}
export const IWebExtensionsScannerService = createDecorator<IWebExtensionsScannerService>('IWebExtensionsScannerService');
export interface IWebExtensionsScannerService {
readonly _serviceBrand: undefined;
scanExtensions(type?: ExtensionType): Promise<IScannedExtension[]>;
scanAndTranslateExtensions(type?: ExtensionType): Promise<ITranslatedScannedExtension[]>;
scanAndTranslateSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<ITranslatedScannedExtension | null>;
canAddExtension(galleryExtension: IGalleryExtension): Promise<boolean>;
addExtension(galleryExtension: IGalleryExtension): Promise<IScannedExtension>;
removeExtension(identifier: IExtensionIdentifier, version?: string): Promise<void>;

View File

@@ -43,12 +43,13 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
extensionManagementService,
get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); }
};
} else if (isWeb) {
}
if (isWeb) {
const extensionManagementService = instantiationService.createInstance(WebExtensionManagementService);
this.webExtensionManagementServer = {
id: 'web',
extensionManagementService,
label: localize('web', "Web")
label: localize('browser', "Browser")
};
}
}

View File

@@ -5,9 +5,9 @@
import { Event, EventMultiplexer } from 'vs/base/common/event';
import {
IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED
ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, UninstallOptions
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -15,13 +15,17 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { CancellationToken } from 'vs/base/common/cancellation';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localize } from 'vs/nls';
import { prefersExecuteOnUI, canExecuteOnWorkspace, prefersExecuteOnWorkspace, canExecuteOnUI, prefersExecuteOnWeb, canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { prefersExecuteOnUI, getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IProductService } from 'vs/platform/product/common/productService';
import { Schemas } from 'vs/base/common/network';
import { IDownloadService } from 'vs/platform/download/common/download';
import { flatten } from 'vs/base/common/arrays';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { canceled } from 'vs/base/common/errors';
import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
export class ExtensionManagementService extends Disposable implements IExtensionManagementService {
export class ExtensionManagementService extends Disposable implements IWorkbenchExtensioManagementService {
declare readonly _serviceBrand: undefined;
@@ -38,6 +42,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
@IConfigurationService protected readonly configurationService: IConfigurationService,
@IProductService protected readonly productService: IProductService,
@IDownloadService protected readonly downloadService: IDownloadService,
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
@IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
@IDialogService private readonly dialogService: IDialogService,
) {
super();
if (this.extensionManagementServerService.localExtensionManagementServer) {
@@ -61,7 +68,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
return flatten(result);
}
async uninstall(extension: ILocalExtension): Promise<void> {
async uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void> {
const server = this.getServer(extension);
if (!server) {
return Promise.reject(`Invalid location ${extension.location.toString()}`);
@@ -70,7 +77,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
if (isLanguagePackExtension(extension.manifest)) {
return this.uninstallEverywhere(extension);
}
return this.uninstallInServer(extension, server);
return this.uninstallInServer(extension, server, options);
}
return server.extensionManagementService.uninstall(extension);
}
@@ -84,8 +91,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
const otherServers: IExtensionManagementServer[] = this.servers.filter(s => s !== server);
if (otherServers.length) {
for (const otherServer of otherServers) {
const installed = await otherServer.extensionManagementService.getInstalled(ExtensionType.User);
extension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0];
const installed = await otherServer.extensionManagementService.getInstalled();
extension = installed.filter(i => !i.isBuiltin && areSameExtensions(i.identifier, extension.identifier))[0];
if (extension) {
await otherServer.extensionManagementService.uninstall(extension);
}
@@ -94,7 +101,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
return promise;
}
private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise<void> {
private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, options?: UninstallOptions): Promise<void> {
if (server === this.extensionManagementServerService.localExtensionManagementServer) {
const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User);
const dependentNonUIExtensions = installedExtensions.filter(i => !prefersExecuteOnUI(i.manifest, this.productService, this.configurationService)
@@ -103,7 +110,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions)));
}
}
return server.extensionManagementService.uninstall(extension, force);
return server.extensionManagementService.uninstall(extension, options);
}
private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string {
@@ -136,6 +143,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
return Promise.reject(`Invalid location ${extension.location.toString()}`);
}
updateExtensionScope(extension: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension> {
const server = this.getServer(extension);
if (server) {
return server.extensionManagementService.updateExtensionScope(extension, isMachineScoped);
}
return Promise.reject(`Invalid location ${extension.location.toString()}`);
}
zip(extension: ILocalExtension): Promise<URI> {
const server = this.getServer(extension);
if (server) {
@@ -198,51 +213,62 @@ export class ExtensionManagementService extends Disposable implements IExtension
return false;
}
async installFromGallery(gallery: IGalleryExtension): Promise<ILocalExtension> {
// Only local server, install without any checks
if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise<ILocalExtension> {
const server = this.getServer(extension);
if (!server) {
return Promise.reject(`Invalid location ${extension.location.toString()}`);
}
const servers: IExtensionManagementServer[] = [];
// Update Language pack on all servers
if (isLanguagePackExtension(extension.manifest)) {
servers.push(...this.servers);
} else {
servers.push(server);
}
return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
}
async installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]> {
if (!installOptions) {
const isMachineScoped = await this.hasToFlagExtensionsMachineScoped(extensions);
installOptions = { isMachineScoped, isBuiltin: false };
}
return Promise.all(extensions.map(extension => this.installFromGallery(extension, installOptions)));
}
async installFromGallery(gallery: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
if (!manifest) {
return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name));
}
const servers: IExtensionManagementServer[] = [];
// Install Language pack on all servers
if (isLanguagePackExtension(manifest)) {
return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
servers.push(...this.servers);
} else {
const server = this.getExtensionManagementServerToInstall(manifest);
if (server) {
servers.push(server);
}
}
// 1. Install on preferred location
// Install UI preferred extension on local server
if (prefersExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install Workspace preferred extension on remote server
if (prefersExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install Web preferred extension on web server
if (prefersExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) {
return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// 2. Install on supported location
// Install UI supported extension on local server
if (canExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install Workspace supported extension on remote server
if (canExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install Web supported extension on web server
if (canExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) {
return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
if (servers.length) {
if (!installOptions) {
const isMachineScoped = await this.hasToFlagExtensionsMachineScoped([gallery]);
installOptions = { isMachineScoped, isBuiltin: false };
}
if (!installOptions.isMachineScoped) {
if (this.extensionManagementServerService.localExtensionManagementServer && !servers.includes(this.extensionManagementServerService.localExtensionManagementServer)) {
servers.push(this.extensionManagementServerService.localExtensionManagementServer);
}
}
return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local);
}
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
@@ -256,6 +282,58 @@ export class ExtensionManagementService extends Disposable implements IExtension
return Promise.reject(error);
}
getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null {
// Only local server
if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer;
}
const extensionKind = getExtensionKind(manifest, this.productService, this.configurationService);
for (const kind of extensionKind) {
if (kind === 'ui' && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer;
}
if (kind === 'workspace' && this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.extensionManagementServerService.remoteExtensionManagementServer;
}
if (kind === 'web' && this.extensionManagementServerService.webExtensionManagementServer) {
return this.extensionManagementServerService.webExtensionManagementServer;
}
}
// Local server can accept any extension. So return local server if not compatible server found.
return this.extensionManagementServerService.localExtensionManagementServer;
}
private async hasToFlagExtensionsMachineScoped(extensions: IGalleryExtension[]): Promise<boolean> {
if (!this.userDataAutoSyncEnablementService.isEnabled() || !this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions)) {
return false;
}
const result = await this.dialogService.show(
Severity.Info,
extensions.length === 1 ? localize('install extension', "Install Extension") : localize('install extensions', "Install Extensions"),
[
localize('install', "Install"),
localize('install and do no sync', "Install (Do not sync)"),
localize('cancel', "Cancel"),
],
{
cancelId: 2,
detail: extensions.length === 1
? localize('install single extension', "Would you like to install and synchronize '{0}' extension across your devices?", extensions[0].displayName)
: localize('install multiple extensions', "Would you like to install and synchronize extensions across your devices?")
}
);
switch (result.choice) {
case 0:
return false;
case 1:
return true;
}
throw canceled();
}
getExtensionsReport(): Promise<IReportedExtension[]> {
if (this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport();

View File

@@ -90,22 +90,23 @@ export class WebExtensionManagementService extends Disposable implements IExtens
}
private async toLocalExtension(scannedExtension: ITranslatedScannedExtension): Promise<ILocalExtension> {
return <ILocalExtension>{
return {
type: scannedExtension.type,
identifier: scannedExtension.identifier,
manifest: scannedExtension.packageJSON,
location: scannedExtension.location,
isMachineScoped: false,
publisherId: null,
publisherDisplayName: null
publisherDisplayName: null,
isBuiltin: scannedExtension.type === ExtensionType.System
};
}
zip(extension: ILocalExtension): Promise<URI> { throw new Error('unsupported'); }
unzip(zipLocation: URI): Promise<IExtensionIdentifier> { throw new Error('unsupported'); }
getManifest(vsix: URI): Promise<IExtensionManifest> { throw new Error('unsupported'); }
install(vsix: URI, isMachineScoped?: boolean): Promise<ILocalExtension> { throw new Error('unsupported'); }
install(vsix: URI): Promise<ILocalExtension> { throw new Error('unsupported'); }
reinstallFromGallery(extension: ILocalExtension): Promise<void> { throw new Error('unsupported'); }
getExtensionsReport(): Promise<IReportedExtension[]> { throw new Error('unsupported'); }
updateExtensionScope(): Promise<ILocalExtension> { throw new Error('unsupported'); }
}

View File

@@ -24,6 +24,8 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls';
import { localize } from 'vs/nls';
import * as semver from 'vs/base/common/semver/semver';
import { isArray } from 'vs/base/common/types';
interface IUserExtension {
identifier: IExtensionIdentifier;
@@ -124,25 +126,31 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
private async readDefaultUserWebExtensions(): Promise<IStaticExtension[]> {
const result: IStaticExtension[] = [];
const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions') || [];
for (const webExtension of defaultUserWebExtensions) {
const extensionLocation = URI.parse(webExtension.location);
const manifestLocation = joinPath(extensionLocation, 'package.json');
const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None);
if (!isSuccess(context)) {
this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation);
continue;
const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions');
if (isArray(defaultUserWebExtensions)) {
for (const webExtension of defaultUserWebExtensions) {
try {
const extensionLocation = URI.parse(webExtension.location);
const manifestLocation = joinPath(extensionLocation, 'package.json');
const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None);
if (!isSuccess(context)) {
this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation);
continue;
}
const content = await asText(context);
if (!content) {
this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation);
continue;
}
const packageJSON = JSON.parse(content);
result.push({
packageJSON,
extensionLocation,
});
} catch (error) {
this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', webExtension);
}
}
const content = await asText(context);
if (!content) {
this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation);
continue;
}
const packageJSON = JSON.parse(content);
result.push({
packageJSON,
extensionLocation,
});
}
return result;
}
@@ -170,6 +178,34 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
return Promise.all(extensions.map((ext) => this._translateScannedExtension(ext)));
}
async scanAndTranslateSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<ITranslatedScannedExtension | null> {
const extension = await this._scanSingleExtension(extensionLocation, extensionType);
if (extension) {
return this._translateScannedExtension(extension);
}
return null;
}
private async _scanSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<IScannedExtension | null> {
if (extensionType === ExtensionType.System) {
const systemExtensions = await this.systemExtensionsPromise;
return this._findScannedExtension(systemExtensions, extensionLocation);
}
const staticExtensions = await this.defaultExtensionsPromise;
const userExtensions = await this.scanUserExtensions();
return this._findScannedExtension(staticExtensions.concat(userExtensions), extensionLocation);
}
private _findScannedExtension(candidates: IScannedExtension[], extensionLocation: URI): IScannedExtension | null {
for (const candidate of candidates) {
if (candidate.location.toString() === extensionLocation.toString()) {
return candidate;
}
}
return null;
}
private async _translateScannedExtension(scannedExtension: IScannedExtension): Promise<ITranslatedScannedExtension> {
let manifest = scannedExtension.packageJSON;
if (scannedExtension.packageNLS) {
@@ -244,7 +280,6 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
}
private async scanUserExtensions(): Promise<IScannedExtension[]> {
const semver = await import('semver-umd');
let userExtensions = await this.readUserExtensions();
const byExtension: IUserExtension[][] = groupByExtension(userExtensions, e => e.identifier);
userExtensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]);

View File

@@ -11,13 +11,10 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { NativeRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/electron-browser/remoteExtensionManagementService';
import { NativeRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService';
import { ILabelService } from 'vs/platform/label/common/label';
import { IExtension } from 'vs/platform/extensions/common/extensions';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IProductService } from 'vs/platform/product/common/productService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export class ExtensionManagementServerService implements IExtensionManagementServerService {
@@ -32,17 +29,14 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
@ISharedProcessService sharedProcessService: ISharedProcessService,
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
@ILabelService labelService: ILabelService,
@IExtensionGalleryService galleryService: IExtensionGalleryService,
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService,
@ILogService logService: ILogService,
@IInstantiationService instantiationService: IInstantiationService,
) {
const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions'));
this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") };
const remoteAgentConnection = remoteAgentService.getConnection();
if (remoteAgentConnection) {
const extensionManagementService = new NativeRemoteExtensionManagementService(remoteAgentConnection.getChannel<IChannel>('extensions'), this.localExtensionManagementServer, logService, galleryService, configurationService, productService);
const extensionManagementService = instantiationService.createInstance(NativeRemoteExtensionManagementService, remoteAgentConnection.getChannel<IChannel>('extensions'), this.localExtensionManagementServer);
this.remoteExtensionManagementServer = {
id: 'remote',
extensionManagementService,

View File

@@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { generateUuid } from 'vs/base/common/uuid';
import { ILocalExtension, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri';
import { ExtensionManagementService as BaseExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDownloadService } from 'vs/platform/download/common/download';
import { IProductService } from 'vs/platform/product/common/productService';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { joinPath } from 'vs/base/common/resources';
import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
export class ExtensionManagementService extends BaseExtensionManagementService {
constructor(
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
@IConfigurationService configurationService: IConfigurationService,
@IProductService productService: IProductService,
@IDownloadService downloadService: IDownloadService,
@IUserDataAutoSyncEnablementService userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
@IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
@IDialogService dialogService: IDialogService,
) {
super(extensionManagementServerService, extensionGalleryService, configurationService, productService, downloadService, userDataAutoSyncEnablementService, userDataSyncResourceEnablementService, dialogService);
}
protected async installVSIX(vsix: URI, server: IExtensionManagementServer): Promise<ILocalExtension> {
if (vsix.scheme === Schemas.vscodeRemote && server === this.extensionManagementServerService.localExtensionManagementServer) {
const downloadedLocation = joinPath(this.environmentService.tmpDir, generateUuid());
await this.downloadService.download(vsix, downloadedLocation);
vsix = downloadedLocation;
}
return server.extensionManagementService.install(vsix);
}
}
registerSingleton(IWorkbenchExtensioManagementService, ExtensionManagementService);

View File

@@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
class ExtensionUrlTrustService {
declare readonly _serviceBrand: undefined;
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
return createChannelSender<IExtensionUrlTrustService>(mainProcessService.getChannel('extensionUrlTrust'));
}
}
registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService);

View File

@@ -3,9 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { tmpdir } from 'os';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation, InstallOptions } 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';
@@ -21,6 +20,7 @@ import { generateUuid } from 'vs/base/common/uuid';
import { joinPath } from 'vs/base/common/resources';
import { WebRemoteExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/remoteExtensionManagementService';
import { IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
export class NativeRemoteExtensionManagementService extends WebRemoteExtensionManagementService implements IExtensionManagementService {
@@ -32,7 +32,8 @@ export class NativeRemoteExtensionManagementService extends WebRemoteExtensionMa
@ILogService private readonly logService: ILogService,
@IExtensionGalleryService galleryService: IExtensionGalleryService,
@IConfigurationService configurationService: IConfigurationService,
@IProductService productService: IProductService
@IProductService productService: IProductService,
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService
) {
super(channel, galleryService, configurationService, productService);
this.localExtensionManagementService = localExtensionManagementServer.extensionManagementService;
@@ -44,19 +45,19 @@ export class NativeRemoteExtensionManagementService extends WebRemoteExtensionMa
return local;
}
async installFromGallery(extension: IGalleryExtension): Promise<ILocalExtension> {
const local = await this.doInstallFromGallery(extension);
async installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
const local = await this.doInstallFromGallery(extension, installOptions);
await this.installUIDependenciesAndPackedExtensions(local);
return local;
}
private async doInstallFromGallery(extension: IGalleryExtension): Promise<ILocalExtension> {
private async doInstallFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
if (this.configurationService.getValue<boolean>('remote.downloadExtensionsLocally')) {
this.logService.trace(`Download '${extension.identifier.id}' extension locally and install`);
return this.downloadCompatibleAndInstall(extension);
}
try {
const local = await super.installFromGallery(extension);
const local = await super.installFromGallery(extension, installOptions);
return local;
} catch (error) {
try {
@@ -87,7 +88,7 @@ export class NativeRemoteExtensionManagementService extends WebRemoteExtensionMa
}
private async downloadAndInstall(extension: IGalleryExtension, installed: ILocalExtension[]): Promise<ILocalExtension> {
const location = joinPath(URI.file(tmpdir()), generateUuid());
const location = joinPath(this.environmentService.tmpDir, generateUuid());
await this.galleryService.download(extension, location, installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0] ? InstallOperation.Update : InstallOperation.Install);
return super.install(location);
}

View File

@@ -1,28 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { tmpdir } from 'os';
import { generateUuid } from 'vs/base/common/uuid';
import { ILocalExtension, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri';
import { ExtensionManagementService as BaseExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { Schemas } from 'vs/base/common/network';
import * as path from 'vs/base/common/path';
export class ExtensionManagementService extends BaseExtensionManagementService {
protected async installVSIX(vsix: URI, server: IExtensionManagementServer): Promise<ILocalExtension> {
if (vsix.scheme === Schemas.vscodeRemote && server === this.extensionManagementServerService.localExtensionManagementServer) {
const downloadedLocation = URI.file(path.join(tmpdir(), generateUuid()));
await this.downloadService.download(vsix, downloadedLocation);
vsix = downloadedLocation;
}
return server.extensionManagementService.install(vsix);
}
}
registerSingleton(IExtensionManagementService, ExtensionManagementService);

View File

@@ -6,7 +6,7 @@ import * as assert from 'assert';
import * as sinon from 'sinon';
import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionEnablementService';
import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/browser/extensionEnablementService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { Emitter } from 'vs/base/common/event';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
@@ -19,10 +19,17 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { productService } from 'vs/workbench/test/browser/workbenchTestServices';
import { productService, TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices';
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
// import { IHostService } from 'vs/workbench/services/host/browser/host';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { mock } from 'vs/base/test/common/mock';
import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect';
function createStorageService(instantiationService: TestInstantiationService): IStorageService {
let service = instantiationService.get(IStorageService);
@@ -52,8 +59,12 @@ export class TestExtensionEnablementService extends ExtensionEnablementService {
instantiationService.get(IConfigurationService),
extensionManagementServerService,
productService,
instantiationService.get(IUserDataAutoSyncService) || instantiationService.stub(IUserDataAutoSyncService, <Partial<IUserDataAutoSyncService>>{ isEnabled() { return false; } }),
instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService)
instantiationService.get(IUserDataAutoSyncEnablementService) || instantiationService.stub(IUserDataAutoSyncEnablementService, <Partial<IUserDataAutoSyncEnablementService>>{ isEnabled() { return false; } }),
instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService),
instantiationService.get(ILifecycleService) || instantiationService.stub(ILifecycleService, new TestLifecycleService()),
instantiationService.get(INotificationService) || instantiationService.stub(INotificationService, new TestNotificationService()),
instantiationService.get(IHostService),
new class extends mock<IExtensionBisectService>() { isDisabledByBisect() { return false; } }
);
}
@@ -394,7 +405,7 @@ suite('ExtensionEnablementService Test', () => {
});
test('test canChangeEnablement return false for auth extension and user data sync account depends on it and auto sync is on', () => {
instantiationService.stub(IUserDataAutoSyncService, <Partial<IUserDataAutoSyncService>>{ isEnabled() { return true; } });
instantiationService.stub(IUserDataAutoSyncEnablementService, <Partial<IUserDataAutoSyncEnablementService>>{ isEnabled() { return true; } });
instantiationService.stub(IUserDataSyncAccountService, <Partial<IUserDataSyncAccountService>>{
account: { authenticationProviderId: 'a' }
});
@@ -613,5 +624,6 @@ function aLocalExtension2(id: string, manifest: any = {}, properties: any = {}):
type: ExtensionType.User,
...properties
};
properties.isBuiltin = properties.type === ExtensionType.System;
return <ILocalExtension>Object.create({ manifest, ...properties });
}