Merge pull request #115302 from microsoft/sandy081/remoteCLI

Remote CLI: Only manage remote extensions
This commit is contained in:
Martin Aeschlimann 2021-01-28 16:00:39 +01:00 committed by GitHub
commit c8a90a4826
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 17 deletions

View file

@ -17,7 +17,6 @@ import { Schemas } from 'vs/base/common/network';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id);
const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, e.g.: {0}", 'ms-dotnettools.csharp');
@ -52,6 +51,9 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
@ILocalizationsService private readonly localizationsService: ILocalizationsService
) { }
protected get location(): string | undefined {
return undefined;
}
public async listExtensions(showVersions: boolean, category?: string, output: CLIOutput = console): Promise<void> {
let extensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
@ -75,6 +77,10 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
});
return;
}
if (this.location) {
output.log(localize('listFromLocation', "Extensions installed on {0}:", this.location));
}
extensions = extensions.sort((e1, e2) => e1.identifier.id.localeCompare(e2.identifier.id));
let lastId: string | undefined = undefined;
for (let extension of extensions) {
@ -89,7 +95,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
const failed: string[] = [];
const installedExtensionsManifests: IExtensionManifest[] = [];
if (extensions.length) {
output.log(localize('installingExtensions', "Installing extensions..."));
output.log(this.location ? localize('installingExtensionsOnLocation', "Installing extensions on {0}...", this.location) : localize('installingExtensions', "Installing extensions..."));
}
const installed = await this.extensionManagementService.getInstalled(ExtensionType.User);
@ -176,7 +182,11 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
private async installVSIX(vsix: URI, force: boolean, output: CLIOutput): Promise<IExtensionManifest | null> {
const manifest = await this.extensionManagementService.getManifest(vsix);
const valid = await this.validate(manifest, force, output);
if (!manifest) {
throw new Error('Invalid vsix');
}
const valid = await this.validateVSIX(manifest, force, output);
if (valid) {
try {
await this.extensionManagementService.install(vsix);
@ -217,6 +227,10 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
private async installFromGallery({ id, version, installOptions }: InstallExtensionInfo, galleryExtension: IGalleryExtension, installed: ILocalExtension[], force: boolean, output: CLIOutput): Promise<IExtensionManifest | null> {
const manifest = await this.extensionGalleryService.getManifest(galleryExtension, CancellationToken.None);
if (manifest && !this.validateExtensionKind(manifest, output)) {
return null;
}
const installedExtension = installed.find(e => areSameExtensions(e.identifier, galleryExtension.identifier));
if (installedExtension) {
if (galleryExtension.version === installedExtension.manifest.version) {
@ -232,6 +246,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
} else {
output.log(localize('installing', "Installing extension '{0}' v{1}...", id, galleryExtension.version));
}
await this.extensionManagementService.installFromGallery(galleryExtension, installOptions);
output.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, galleryExtension.version));
return manifest;
@ -245,11 +260,11 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
}
}
private async validate(manifest: IExtensionManifest, force: boolean, output: CLIOutput): Promise<boolean> {
if (!manifest) {
throw new Error('Invalid vsix');
}
protected validateExtensionKind(_manifest: IExtensionManifest, output: CLIOutput): boolean {
return true;
}
private async validateVSIX(manifest: IExtensionManifest, force: boolean, output: CLIOutput): Promise<boolean> {
const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
const newer = installedExtensions.find(local => areSameExtensions(extensionIdentifier, local.identifier) && gt(local.manifest.version, manifest.version));
@ -259,7 +274,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
return false;
}
return true;
return this.validateExtensionKind(manifest, output);
}
public async uninstallExtensions(extensions: (string | URI)[], force: boolean, output: CLIOutput = console): Promise<void> {
@ -277,7 +292,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
const installed = await this.extensionManagementService.getInstalled();
const extensionsToUninstall = installed.filter(e => areSameExtensions(e.identifier, { id }));
if (!extensionsToUninstall.length) {
throw new Error(`${notInstalled(id)}\n${useId}`);
throw new Error(`${this.notInstalled(id)}\n${useId}`);
}
if (extensionsToUninstall.some(e => e.type === ExtensionType.System)) {
output.log(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be uninstalled", id));
@ -293,7 +308,12 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
uninstalledExtensions.push(extensionToUninstall);
}
output.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id));
if (this.location) {
output.log(localize('successUninstallFromLocation', "Extension '{0}' was successfully uninstalled from {1}!", id, this.location));
} else {
output.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id));
}
}
if (uninstalledExtensions.some(e => isLanguagePackExtension(e.manifest))) {
@ -319,4 +339,9 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
private updateLocalizationsCache(): Promise<boolean> {
return this.localizationsService.update();
}
private notInstalled(id: string) {
return this.location ? localize('notInstalleddOnLocation', "Extension '{0}' is not installed on {1}.", id, this.location) : localize('notInstalled', "Extension '{0}' is not installed.", id);
}
}

View file

@ -3,15 +3,28 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Schemas } from 'vs/base/common/network';
import { isString } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CLIOutput, IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService';
import { getExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IProductService } from 'vs/platform/product/common/productService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IExtensionManifest } from 'vs/workbench/workbench.web.api';
// this class contains the command that the CLI server is reying on
// this class contains the commands that the CLI server is reying on
CommandsRegistry.registerCommand('_remoteCLI.openExternal', function (accessor: ServicesAccessor, uri: UriComponents, options: { allowTunneling?: boolean }) {
// TODO: discuss martin, ben where to put this
@ -28,7 +41,14 @@ interface ManageExtensionsArgs {
CommandsRegistry.registerCommand('_remoteCLI.manageExtensions', async function (accessor: ServicesAccessor, args: ManageExtensionsArgs) {
const cliService = accessor.get(IExtensionManagementCLIService);
const instantiationService = accessor.get(IInstantiationService);
const extensionManagementServerService = accessor.get(IExtensionManagementServerService);
const remoteExtensionManagementService = extensionManagementServerService.remoteExtensionManagementServer?.extensionManagementService;
if (!remoteExtensionManagementService) {
return;
}
const cliService = instantiationService.createChild(new ServiceCollection([IExtensionManagementService, remoteExtensionManagementService])).createInstance(RemoteExtensionCLIManagementService);
const lines: string[] = [];
const output = { log: lines.push.bind(lines), error: lines.push.bind(lines) };
@ -55,3 +75,34 @@ CommandsRegistry.registerCommand('_remoteCLI.manageExtensions', async function (
return lines.join('\n');
});
class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService {
private _location: string | undefined;
constructor(
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
@IProductService private readonly productService: IProductService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
@ILocalizationsService localizationsService: ILocalizationsService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService envService: IWorkbenchEnvironmentService
) {
super(extensionManagementService, extensionGalleryService, localizationsService);
const remoteAuthority = envService.remoteAuthority;
this._location = remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority) : undefined;
}
protected get location(): string | undefined {
return this._location;
}
protected validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean {
if (!canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) {
output.log(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", getExtensionId(manifest.publisher, manifest.name)));
return false;
}
return true;
}
}

View file

@ -93,9 +93,8 @@ import 'vs/workbench/services/outline/browser/outlineService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService';
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { IExtensionGalleryService, IExtensionManagementCLIService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IListService, ListService } from 'vs/platform/list/browser/listService';
@ -127,7 +126,6 @@ registerSingleton(IIgnoredExtensionsManagementService, IgnoredExtensionsManageme
registerSingleton(IGlobalExtensionEnablementService, GlobalExtensionEnablementService);
registerSingleton(IExtensionsStorageSyncService, ExtensionsStorageSyncService);
registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true);
registerSingleton(IExtensionManagementCLIService, ExtensionManagementCLIService);
registerSingleton(IContextViewService, ContextViewService, true);
registerSingleton(IListService, ListService, true);
registerSingleton(IEditorWorkerService, EditorWorkerServiceImpl);