support enabling extensions through environment
This commit is contained in:
parent
69a882951c
commit
f7a979540d
8 changed files with 145 additions and 23 deletions
|
@ -62,6 +62,7 @@ export interface IEnvironmentService {
|
|||
debugExtensionHost: IExtensionHostDebugParams;
|
||||
isExtensionDevelopment: boolean;
|
||||
disableExtensions: boolean | string[];
|
||||
enableExtensions?: readonly string[];
|
||||
extensionDevelopmentLocationURI?: URI[];
|
||||
extensionDevelopmentKind?: ExtensionKind[];
|
||||
extensionTestsLocationURI?: URI;
|
||||
|
|
|
@ -1838,7 +1838,8 @@ export class ExtensionStatusLabelAction extends Action implements IExtensionCont
|
|||
|
||||
constructor(
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
|
||||
) {
|
||||
super('extensions.action.statusLabel', '', ExtensionStatusLabelAction.DISABLED_CLASS, false);
|
||||
}
|
||||
|
@ -1896,8 +1897,8 @@ export class ExtensionStatusLabelAction extends Action implements IExtensionCont
|
|||
}
|
||||
|
||||
if (currentEnablementState !== null) {
|
||||
const currentlyEnabled = currentEnablementState === EnablementState.EnabledGlobally || currentEnablementState === EnablementState.EnabledWorkspace;
|
||||
const enabled = this.enablementState === EnablementState.EnabledGlobally || this.enablementState === EnablementState.EnabledWorkspace;
|
||||
const currentlyEnabled = this.extensionEnablementService.isEnabledEnablementState(currentEnablementState);
|
||||
const enabled = this.extensionEnablementService.isEnabledEnablementState(this.enablementState);
|
||||
if (!currentlyEnabled && enabled) {
|
||||
return canAddExtension() ? localize('enabled', "Enabled") : null;
|
||||
}
|
||||
|
@ -2108,6 +2109,18 @@ export class ExtensionStatusIconAction extends ExtensionAction {
|
|||
return;
|
||||
}
|
||||
|
||||
// Extension is disabled by environment
|
||||
if (this.extension.enablementState === EnablementState.DisabledByEnvironment) {
|
||||
this.updateStatusMessage(localize('disabled by environment', "This extension is disabled by the environment."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Extension is enabled by environment
|
||||
if (this.extension.enablementState === EnablementState.EnabledByEnvironment) {
|
||||
this.updateStatusMessage(localize('enabled by environment', "This extension is enabled because it is required in the current environment."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Extension is disabled by virtual workspace
|
||||
if (this.extension.enablementState === EnablementState.DisabledByVirtualWorkspace) {
|
||||
const details = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.virtualWorkspaces);
|
||||
|
|
|
@ -1230,7 +1230,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
|||
if (i === extension) {
|
||||
return false;
|
||||
}
|
||||
if (!(i.enablementState === EnablementState.EnabledWorkspace || i.enablementState === EnablementState.EnabledGlobally)) {
|
||||
if (!this.extensionEnablementService.isEnabledEnablementState(i.enablementState)) {
|
||||
return false;
|
||||
}
|
||||
if (extensionsToDisable.indexOf(i) !== -1) {
|
||||
|
|
|
@ -226,6 +226,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
|
|||
|
||||
get disableExtensions() { return this.payload?.get('disableExtensions') === 'true'; }
|
||||
|
||||
get enableExtensions() { return this.options.enableExtensions; }
|
||||
|
||||
@memoize
|
||||
get webviewExternalEndpoint(): string {
|
||||
const endpoint = this.options.webviewEndpoint
|
||||
|
|
|
@ -114,36 +114,26 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
canChangeEnablement(extension: IExtension): boolean {
|
||||
try {
|
||||
this.throwErrorIfCannotChangeEnablement(extension);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.getEnablementState(extension)) {
|
||||
case EnablementState.DisabledByEnvironment:
|
||||
case EnablementState.DisabledByVirtualWorkspace:
|
||||
case EnablementState.DisabledByExtensionKind:
|
||||
return false;
|
||||
case EnablementState.DisabledByExtensionDependency:
|
||||
// Can be changed only when all its dependencies enablements can be changed
|
||||
return getExtensionDependencies(this.extensionsManager.extensions, extension).every(e => this.canChangeEnablement(e));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
canChangeWorkspaceEnablement(extension: IExtension): boolean {
|
||||
if (!this.canChangeEnablement(extension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this.throwErrorIfCannotChangeWorkspaceEnablement(extension);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private throwErrorIfCannotChangeEnablement(extension: IExtension): void {
|
||||
private throwErrorIfCannotChangeEnablement(extension: IExtension, donotCheckDependencies?: boolean): void {
|
||||
if (isLanguagePackExtension(extension.manifest)) {
|
||||
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));
|
||||
}
|
||||
|
@ -152,6 +142,32 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) {
|
||||
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));
|
||||
}
|
||||
|
||||
if (this._isEnabledInEnv(extension)) {
|
||||
throw new Error(localize('cannot change enablement environment', "Cannot change enablement of {0} extension because it is enabled in environment", extension.manifest.displayName || extension.identifier.id));
|
||||
}
|
||||
|
||||
switch (this.getEnablementState(extension)) {
|
||||
case EnablementState.DisabledByEnvironment:
|
||||
throw new Error(localize('cannot change disablement environment', "Cannot change enablement of {0} extension because it is disabled in environment", extension.manifest.displayName || extension.identifier.id));
|
||||
case EnablementState.DisabledByVirtualWorkspace:
|
||||
throw new Error(localize('cannot change enablement virtual workspace', "Cannot change enablement of {0} extension because it does not support virtual workspaces", extension.manifest.displayName || extension.identifier.id));
|
||||
case EnablementState.DisabledByExtensionDependency:
|
||||
if (donotCheckDependencies) {
|
||||
break;
|
||||
}
|
||||
// Can be changed only when all its dependencies enablements can be changed
|
||||
for (const dependency of getExtensionDependencies(this.extensionsManager.extensions, extension)) {
|
||||
if (this.isEnabled(dependency)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
this.throwErrorIfCannotChangeEnablement(dependency, true);
|
||||
} catch (error) {
|
||||
throw new Error(localize('cannot change enablement dependency', "Cannot enable '{0}' extension because it depends on '{1}' extension that cannot be enabled", extension.manifest.displayName || extension.identifier.id, dependency.manifest.displayName || dependency.identifier.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private throwErrorIfCannotChangeWorkspaceEnablement(extension: IExtension): void {
|
||||
|
@ -227,7 +243,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
}
|
||||
|
||||
isEnabledEnablementState(enablementState: EnablementState): boolean {
|
||||
return enablementState === EnablementState.EnabledWorkspace || enablementState === EnablementState.EnabledGlobally;
|
||||
return enablementState === EnablementState.EnabledByEnvironment || enablementState === EnablementState.EnabledWorkspace || enablementState === EnablementState.EnabledGlobally;
|
||||
}
|
||||
|
||||
isDisabledGlobally(extension: IExtension): boolean {
|
||||
|
@ -267,6 +283,10 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
enablementState = EnablementState.DisabledByExtensionDependency;
|
||||
}
|
||||
|
||||
else if (!this.isEnabledEnablementState(enablementState) && this._isEnabledInEnv(extension)) {
|
||||
enablementState = EnablementState.EnabledByEnvironment;
|
||||
}
|
||||
|
||||
computedEnablementStates.set(extension, enablementState);
|
||||
return enablementState;
|
||||
}
|
||||
|
@ -289,6 +309,14 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
return false;
|
||||
}
|
||||
|
||||
private _isEnabledInEnv(extension: IExtension): boolean {
|
||||
const enabledExtensions = this.environmentService.enableExtensions;
|
||||
if (Array.isArray(enabledExtensions)) {
|
||||
return enabledExtensions.some(id => areSameExtensions({ id }, extension.identifier));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _isDisabledByVirtualWorkspace(extension: IExtension, workspaceType: WorkspaceType): boolean {
|
||||
// Not a virtual workspace
|
||||
if (!workspaceType.virtual) {
|
||||
|
|
|
@ -48,6 +48,7 @@ export const enum EnablementState {
|
|||
DisabledByTrustRequirement,
|
||||
DisabledByExtensionKind,
|
||||
DisabledByEnvironment,
|
||||
EnabledByEnvironment,
|
||||
DisabledByVirtualWorkspace,
|
||||
DisabledByExtensionDependency,
|
||||
DisabledGlobally,
|
||||
|
|
|
@ -497,6 +497,78 @@ suite('ExtensionEnablementService Test', () => {
|
|||
assert.deepStrictEqual(testObject.getEnablementState(extension), EnablementState.DisabledByEnvironment);
|
||||
});
|
||||
|
||||
test('test extension is enabled globally when enabled in environment', async () => {
|
||||
const extension = aLocalExtension('pub.a');
|
||||
installed.push(extension);
|
||||
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { enableExtensions: <readonly string[]>['pub.a'] } as IWorkbenchEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
|
||||
assert.ok(testObject.isEnabled(extension));
|
||||
assert.deepStrictEqual(testObject.getEnablementState(extension), EnablementState.EnabledGlobally);
|
||||
});
|
||||
|
||||
test('test extension is enabled workspace when enabled in environment', async () => {
|
||||
const extension = aLocalExtension('pub.a');
|
||||
installed.push(extension);
|
||||
|
||||
testObject.setEnablement([extension], EnablementState.EnabledWorkspace);
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { enableExtensions: <readonly string[]>['pub.a'] } as IWorkbenchEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
|
||||
assert.ok(testObject.isEnabled(extension));
|
||||
assert.deepStrictEqual(testObject.getEnablementState(extension), EnablementState.EnabledWorkspace);
|
||||
});
|
||||
|
||||
test('test extension is enabled by environment when disabled globally', async () => {
|
||||
const extension = aLocalExtension('pub.a');
|
||||
installed.push(extension);
|
||||
|
||||
testObject.setEnablement([extension], EnablementState.DisabledGlobally);
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { enableExtensions: <readonly string[]>['pub.a'] } as IWorkbenchEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
|
||||
assert.ok(testObject.isEnabled(extension));
|
||||
assert.deepStrictEqual(testObject.getEnablementState(extension), EnablementState.EnabledByEnvironment);
|
||||
});
|
||||
|
||||
test('test extension is enabled by environment when disabled workspace', async () => {
|
||||
const extension = aLocalExtension('pub.a');
|
||||
installed.push(extension);
|
||||
|
||||
testObject.setEnablement([extension], EnablementState.DisabledWorkspace);
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { enableExtensions: <readonly string[]>['pub.a'] } as IWorkbenchEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
|
||||
assert.ok(testObject.isEnabled(extension));
|
||||
assert.deepStrictEqual(testObject.getEnablementState(extension), EnablementState.EnabledByEnvironment);
|
||||
});
|
||||
|
||||
test('test extension is disabled by environment when also enabled in environment', async () => {
|
||||
const extension = aLocalExtension('pub.a');
|
||||
installed.push(extension);
|
||||
|
||||
testObject.setEnablement([extension], EnablementState.DisabledWorkspace);
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { disableExtensions: true, enableExtensions: <readonly string[]>['pub.a'] } as IWorkbenchEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
|
||||
assert.ok(!testObject.isEnabled(extension));
|
||||
assert.deepStrictEqual(testObject.getEnablementState(extension), EnablementState.DisabledByEnvironment);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return false when the extension is enabled in environment', () => {
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { enableExtensions: <readonly string[]>['pub.a'] } as IWorkbenchEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
assert.strictEqual(testObject.canChangeEnablement(aLocalExtension('pub.a')), false);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return false for system extension when extension is disabled in environment', () => {
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { enableExtensions: <readonly string[]>['pub.a'] } as IWorkbenchEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
const extension = aLocalExtension('pub.a', undefined, ExtensionType.System);
|
||||
assert.ok(!testObject.canChangeEnablement(extension));
|
||||
});
|
||||
|
||||
test('test extension does not support vitrual workspace is not enabled in virtual workspace', async () => {
|
||||
const extension = aLocalExtension2('pub.a', { capabilities: { virtualWorkspaces: false } });
|
||||
instantiationService.stub(IWorkspaceContextService, 'getWorkspace', <IWorkspace>{ folders: [{ uri: URI.file('worskapceA').with(({ scheme: 'virtual' })) }] });
|
||||
|
@ -613,11 +685,11 @@ suite('ExtensionEnablementService Test', () => {
|
|||
assert.deepStrictEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return false when the local workspace extension is disabled by kind', () => {
|
||||
test('test canChangeEnablement return true when the local workspace extension is disabled by kind', () => {
|
||||
instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService));
|
||||
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['workspace'] }, { location: URI.file(`pub.a`) });
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
assert.strictEqual(testObject.canChangeEnablement(localWorkspaceExtension), false);
|
||||
assert.strictEqual(testObject.canChangeEnablement(localWorkspaceExtension), true);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return true for local ui extension', () => {
|
||||
|
@ -659,11 +731,11 @@ suite('ExtensionEnablementService Test', () => {
|
|||
assert.deepStrictEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return false when the remote ui extension is disabled by kind', () => {
|
||||
test('test canChangeEnablement return true when the remote ui extension is disabled by kind', () => {
|
||||
instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService));
|
||||
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['ui'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
assert.strictEqual(testObject.canChangeEnablement(localWorkspaceExtension), false);
|
||||
assert.strictEqual(testObject.canChangeEnablement(localWorkspaceExtension), true);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return true for remote workspace extension', () => {
|
||||
|
|
|
@ -343,6 +343,11 @@ interface IWorkbenchConstructionOptions {
|
|||
*/
|
||||
readonly additionalBuiltinExtensions?: readonly (ExtensionId | UriComponents)[];
|
||||
|
||||
/**
|
||||
* List of extensions to be enabled.
|
||||
*/
|
||||
readonly enableExtensions?: readonly ExtensionId[];
|
||||
|
||||
/**
|
||||
* [TEMPORARY]: This will be removed soon.
|
||||
* Enable inlined extensions.
|
||||
|
|
Loading…
Reference in a new issue