mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
@@ -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)) {
|
||||
@@ -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);
|
||||
@@ -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>;
|
||||
|
||||
@@ -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")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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'); }
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user