This commit is contained in:
Sandeep Somavarapu 2024-02-01 15:21:07 +01:00 committed by GitHub
parent 69684cd43c
commit dcea438aca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 216 additions and 247 deletions

View file

@ -18,7 +18,7 @@ import {
IExtensionsControlManifest, StatisticType, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode,
InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService, InstallExtensionInfo, EXTENSION_INSTALL_DEP_PACK_CONTEXT, ExtensionGalleryError
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, areSameExtensions, ExtensionKey, getGalleryExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { areSameExtensions, ExtensionKey, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionManifest, isApplicationScopedExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
@ -204,25 +204,27 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
protected async installExtensions(extensions: InstallableExtension[]): Promise<InstallExtensionResult[]> {
const results: InstallExtensionResult[] = [];
await Promise.allSettled(extensions.map(async e => {
const installingExtensionsMap = new Map<string, { task: IInstallExtensionTask; manifest: IExtensionManifest }>();
const alreadyRequestedInstallations: Promise<any>[] = [];
const successResults: (InstallExtensionResult & { local: ILocalExtension; profileLocation: URI })[] = [];
const getInstallExtensionTaskKey = (extension: IGalleryExtension, profileLocation: URI) => `${ExtensionKey.create(extension).toString()}-${profileLocation.toString()}`;
const createInstallExtensionTask = (manifest: IExtensionManifest, extension: IGalleryExtension | URI, options: InstallExtensionTaskOptions): void => {
const installExtensionTask = this.createInstallExtensionTask(manifest, extension, options);
const key = URI.isUri(extension) ? extension.path : `${extension.identifier.id.toLowerCase()}-${options.profileLocation.toString()}`;
installingExtensionsMap.set(key, { task: installExtensionTask, manifest });
this._onInstallExtension.fire({ identifier: installExtensionTask.identifier, source: extension, profileLocation: options.profileLocation });
this.logService.info('Installing extension:', installExtensionTask.identifier.id);
// only cache gallery extensions tasks
if (!URI.isUri(extension)) {
this.installingExtensions.set(getInstallExtensionTaskKey(extension, options.profileLocation), { task: installExtensionTask, waitingTasks: [] });
}
};
try {
const result = await this.installExtension(e, (taskToWait: IInstallExtensionTask, taskToWaitFor: IInstallExtensionTask): boolean => {
if (extensions.some(e => adoptToGalleryExtensionId(taskToWaitFor.identifier.id) === getGalleryExtensionId(e.manifest.publisher, e.manifest.name))) {
return false;
}
return this.canWaitForTask(taskToWait, taskToWaitFor);
});
results.push(...result);
} catch (error) {
results.push({ identifier: { id: getGalleryExtensionId(e.manifest.publisher, e.manifest.name) }, operation: InstallOperation.Install, source: e.extension, error });
}
}));
this._onDidInstallExtensions.fire(results);
return results;
}
private async installExtension({ manifest, extension, options }: InstallableExtension, shouldWait: (taskToWait: IInstallExtensionTask, taskToWaitFor: IInstallExtensionTask) => boolean): Promise<InstallExtensionResult[]> {
// Start installing extensions
for (const { manifest, extension, options } of extensions) {
const isApplicationScoped = options.isApplicationScoped || options.isBuiltin || isApplicationScopedExtension(manifest);
const installExtensionTaskOptions: InstallExtensionTaskOptions = {
...options,
@ -230,53 +232,41 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
isApplicationScoped,
profileLocation: isApplicationScoped ? this.userDataProfilesService.defaultProfile.extensionsResource : options.profileLocation ?? this.getCurrentExtensionsManifestLocation()
};
const getInstallExtensionTaskKey = (extension: IGalleryExtension) => `${ExtensionKey.create(extension).toString()}${installExtensionTaskOptions.profileLocation ? `-${installExtensionTaskOptions.profileLocation.toString()}` : ''}`;
// only cache gallery extensions tasks
if (!URI.isUri(extension)) {
const installingExtension = this.installingExtensions.get(getInstallExtensionTaskKey(extension));
if (installingExtension) {
this.logService.info('Extensions is already requested to install', extension.identifier.id);
await installingExtension.task.waitUntilTaskIsFinished();
return [];
const existingInstallExtensionTask = !URI.isUri(extension) ? this.installingExtensions.get(getInstallExtensionTaskKey(extension, installExtensionTaskOptions.profileLocation)) : undefined;
if (existingInstallExtensionTask) {
this.logService.info('Extension is already requested to install', existingInstallExtensionTask.task.identifier.id);
alreadyRequestedInstallations.push(existingInstallExtensionTask.task.waitUntilTaskIsFinished());
} else {
createInstallExtensionTask(manifest, extension, installExtensionTaskOptions);
}
}
const allInstallExtensionTasks: { task: IInstallExtensionTask; manifest: IExtensionManifest }[] = [];
const alreadyRequestedInstallations: Promise<void>[] = [];
const installResults: (InstallExtensionResult & { local: ILocalExtension })[] = [];
const installExtensionTask = this.createInstallExtensionTask(manifest, extension, installExtensionTaskOptions);
if (!URI.isUri(extension)) {
this.installingExtensions.set(getInstallExtensionTaskKey(extension), { task: installExtensionTask, waitingTasks: [] });
}
this._onInstallExtension.fire({ identifier: installExtensionTask.identifier, source: extension, profileLocation: installExtensionTaskOptions.profileLocation });
this.logService.info('Installing extension:', installExtensionTask.identifier.id);
allInstallExtensionTasks.push({ task: installExtensionTask, manifest });
let installExtensionHasDependents: boolean = false;
try {
if (installExtensionTaskOptions.donotIncludePackAndDependencies) {
this.logService.info('Installing the extension without checking dependencies and pack', installExtensionTask.identifier.id);
// collect and start installing all dependencies and pack extensions
await Promise.all([...installingExtensionsMap.values()].map(async ({ task, manifest }) => {
if (task.options.donotIncludePackAndDependencies) {
this.logService.info('Installing the extension without checking dependencies and pack', task.identifier.id);
} else {
try {
const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(installExtensionTask.identifier, manifest, !!installExtensionTaskOptions.installOnlyNewlyAddedFromExtensionPack, !!installExtensionTaskOptions.installPreReleaseVersion, installExtensionTaskOptions.profileLocation);
const installed = await this.getInstalled(undefined, installExtensionTaskOptions.profileLocation);
const options: InstallExtensionTaskOptions = { ...installExtensionTaskOptions, donotIncludePackAndDependencies: true, context: { ...installExtensionTaskOptions.context, [EXTENSION_INSTALL_DEP_PACK_CONTEXT]: true } };
const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(task.identifier, manifest, !!task.options.installOnlyNewlyAddedFromExtensionPack, !!task.options.installPreReleaseVersion, task.options.profileLocation);
const installed = await this.getInstalled(undefined, task.options.profileLocation);
const options: InstallExtensionTaskOptions = { ...task.options, donotIncludePackAndDependencies: true, context: { ...task.options.context, [EXTENSION_INSTALL_DEP_PACK_CONTEXT]: true } };
for (const { gallery, manifest } of distinct(allDepsAndPackExtensionsToInstall, ({ gallery }) => gallery.identifier.id)) {
installExtensionHasDependents = installExtensionHasDependents || !!manifest.extensionDependencies?.some(id => areSameExtensions({ id }, installExtensionTask.identifier));
const key = getInstallExtensionTaskKey(gallery);
const existingInstallingExtension = this.installingExtensions.get(key);
if (installingExtensionsMap.has(`${gallery.identifier.id.toLowerCase()}-${options.profileLocation.toString()}`)) {
continue;
}
const existingInstallingExtension = this.installingExtensions.get(getInstallExtensionTaskKey(gallery, options.profileLocation));
if (existingInstallingExtension) {
if (shouldWait(installExtensionTask, existingInstallingExtension.task)) {
if (this.canWaitForTask(task, existingInstallingExtension.task)) {
const identifier = existingInstallingExtension.task.identifier;
this.logService.info('Waiting for already requested installing extension', identifier.id, installExtensionTask.identifier.id);
existingInstallingExtension.waitingTasks.push(installExtensionTask);
this.logService.info('Waiting for already requested installing extension', identifier.id, task.identifier.id);
existingInstallingExtension.waitingTasks.push(task);
// add promise that waits until the extension is completely installed, ie., onDidInstallExtensions event is triggered for this extension
alreadyRequestedInstallations.push(
Event.toPromise(
Event.filter(this.onDidInstallExtensions, results => results.some(result => areSameExtensions(result.identifier, identifier)))
).then(results => {
this.logService.info('Finished waiting for already requested installing extension', identifier.id, installExtensionTask.identifier.id);
this.logService.info('Finished waiting for already requested installing extension', identifier.id, task.identifier.id);
const result = results.find(result => areSameExtensions(result.identifier, identifier));
if (!result?.local) {
// Extension failed to install
@ -285,53 +275,33 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}));
}
} else if (!installed.some(({ identifier }) => areSameExtensions(identifier, gallery.identifier))) {
const task = this.createInstallExtensionTask(manifest, gallery, options);
this.installingExtensions.set(key, { task, waitingTasks: [installExtensionTask] });
this._onInstallExtension.fire({ identifier: task.identifier, source: gallery, profileLocation: installExtensionTaskOptions.profileLocation });
this.logService.info('Installing extension:', task.identifier.id, installExtensionTask.identifier.id);
allInstallExtensionTasks.push({ task, manifest });
createInstallExtensionTask(manifest, gallery, options);
}
}
} catch (error) {
// Installing through VSIX
if (URI.isUri(installExtensionTask.source)) {
if (URI.isUri(task.source)) {
// Ignore installing dependencies and packs
if (isNonEmptyArray(manifest.extensionDependencies)) {
this.logService.warn(`Cannot install dependencies of extension:`, installExtensionTask.identifier.id, error.message);
this.logService.warn(`Cannot install dependencies of extension:`, task.identifier.id, error.message);
}
if (isNonEmptyArray(manifest.extensionPack)) {
this.logService.warn(`Cannot install packed extensions of extension:`, installExtensionTask.identifier.id, error.message);
this.logService.warn(`Cannot install packed extensions of extension:`, task.identifier.id, error.message);
}
} else {
this.logService.error('Error while preparing to install dependencies and extension packs of the extension:', installExtensionTask.identifier.id);
this.logService.error('Error while preparing to install dependencies and extension packs of the extension:', task.identifier.id);
throw error;
}
}
}
const extensionsToInstallMap = allInstallExtensionTasks.reduce((result, { task, manifest }) => {
result.set(task.identifier.id.toLowerCase(), { task, manifest });
return result;
}, new Map<string, { task: IInstallExtensionTask; manifest: IExtensionManifest }>());
while (extensionsToInstallMap.size) {
let extensionsToInstall;
const extensionsWithoutDepsToInstall = [...extensionsToInstallMap.values()].filter(({ manifest }) => !manifest.extensionDependencies?.some(id => extensionsToInstallMap.has(id.toLowerCase())));
if (extensionsWithoutDepsToInstall.length) {
extensionsToInstall = extensionsToInstallMap.size === 1 ? extensionsWithoutDepsToInstall
/* If the main extension has no dependents remove it and install it at the end */
: extensionsWithoutDepsToInstall.filter(({ task }) => !(task === installExtensionTask && !installExtensionHasDependents));
} else {
this.logService.info('Found extensions with circular dependencies', extensionsWithoutDepsToInstall.map(({ task }) => task.identifier.id));
extensionsToInstall = [...extensionsToInstallMap.values()];
}
}));
// Install extensions in parallel and wait until all extensions are installed / failed
await this.joinAllSettled(extensionsToInstall.map(async ({ task }) => {
await this.joinAllSettled([...installingExtensionsMap.values()].map(async ({ task }) => {
const startTime = new Date().getTime();
try {
const local = await task.run();
await this.joinAllSettled(this.participants.map(participant => participant.postInstall(local, task.source, installExtensionTaskOptions, CancellationToken.None)));
await this.joinAllSettled(this.participants.map(participant => participant.postInstall(local, task.source, task.options, CancellationToken.None)));
if (!URI.isUri(task.source)) {
const isUpdate = task.operation === InstallOperation.Update;
const durationSinceUpdate = isUpdate ? undefined : (new Date().getTime() - task.source.lastUpdated) / 1000;
@ -349,53 +319,52 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
}
installResults.push({ local, identifier: task.identifier, operation: task.operation, source: task.source, context: task.options.context, profileLocation: task.profileLocation, applicationScoped: local.isApplicationScoped });
successResults.push({ local, identifier: task.identifier, operation: task.operation, source: task.source, context: task.options.context, profileLocation: task.profileLocation, applicationScoped: local.isApplicationScoped });
} catch (error) {
this.logService.error('Error while installing the extension', task.identifier.id, getErrorMessage(error));
throw error;
} finally { extensionsToInstallMap.delete(task.identifier.id.toLowerCase()); }
}));
}
}));
if (alreadyRequestedInstallations.length) {
await this.joinAllSettled(alreadyRequestedInstallations);
}
installResults.forEach(({ identifier }) => this.logService.info(`Extension installed successfully:`, identifier.id));
return installResults;
for (const result of successResults) {
this.logService.info(`Extension installed successfully:`, result.identifier.id);
results.push(result);
}
return results;
} catch (error) {
// cancel all tasks
allInstallExtensionTasks.forEach(({ task }) => task.cancel());
// rollback installed extensions
if (installResults.length) {
if (successResults.length) {
await Promise.allSettled(successResults.map(async ({ local, profileLocation }) => {
try {
const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true, profileLocation: installExtensionTaskOptions.profileLocation }).run()));
for (let index = 0; index < result.length; index++) {
const r = result[index];
const { identifier } = installResults[index];
if (r.status === 'fulfilled') {
this.logService.info('Rollback: Uninstalled extension', identifier.id);
} else {
this.logService.warn('Rollback: Error while uninstalling extension', identifier.id, getErrorMessage(r.reason));
}
}
await this.createUninstallExtensionTask(local, { versionOnly: true, profileLocation }).run();
this.logService.info('Rollback: Uninstalled extension', local.identifier.id);
} catch (error) {
// ignore error
this.logService.warn('Error while rolling back extensions', getErrorMessage(error), installResults.map(({ identifier }) => identifier.id));
this.logService.warn('Rollback: Error while uninstalling extension', local.identifier.id, getErrorMessage(error));
}
}));
}
return allInstallExtensionTasks.map(({ task }) => ({ identifier: task.identifier, operation: InstallOperation.Install, source: task.source, context: installExtensionTaskOptions.context, profileLocation: installExtensionTaskOptions.profileLocation, error }));
// cancel all tasks and collect error results
for (const { task } of installingExtensionsMap.values()) {
task.cancel();
results.push({ identifier: task.identifier, operation: InstallOperation.Install, source: task.source, context: task.options.context, profileLocation: task.profileLocation, error });
}
throw error;
} finally {
// Finally, remove all the tasks from the cache
for (const { task } of allInstallExtensionTasks) {
for (const { task } of installingExtensionsMap.values()) {
if (task.source && !URI.isUri(task.source)) {
this.installingExtensions.delete(getInstallExtensionTaskKey(task.source));
this.installingExtensions.delete(getInstallExtensionTaskKey(task.source, task.profileLocation));
}
}
if (results.length) {
this._onDidInstallExtensions.fire(results);
}
}
}

View file

@ -20,7 +20,7 @@ const notFound = (id: string) => localize('notFound', "Extension '{0}' not found
const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, e.g.: {0}", 'ms-dotnettools.csharp');
type InstallVSIXInfo = { vsix: URI; installOptions: InstallOptions };
type CLIInstallExtensionInfo = { id: string; version?: string; installOptions: InstallOptions };
type InstallGalleryExtensionInfo = { id: string; version?: string; installOptions: InstallOptions };
export class ExtensionManagementCLI {
@ -74,13 +74,12 @@ export class ExtensionManagementCLI {
const failed: string[] = [];
try {
const installedExtensionsManifests: IExtensionManifest[] = [];
if (extensions.length) {
this.logger.info(this.location ? localize('installingExtensionsOnLocation', "Installing extensions on {0}...", this.location) : localize('installingExtensions', "Installing extensions..."));
}
const installVSIXInfos: InstallVSIXInfo[] = [];
let installExtensionInfos: CLIInstallExtensionInfo[] = [];
const installExtensionInfos: InstallGalleryExtensionInfo[] = [];
const addInstallExtensionInfo = (id: string, version: string | undefined, isBuiltin: boolean) => {
installExtensionInfos.push({ id, version: version !== 'prerelease' ? version : undefined, installOptions: { ...installOptions, isBuiltin, installPreReleaseVersion: version === 'prerelease' || installOptions.installPreReleaseVersion } });
};
@ -106,10 +105,7 @@ export class ExtensionManagementCLI {
if (installVSIXInfos.length) {
await Promise.all(installVSIXInfos.map(async ({ vsix, installOptions }) => {
try {
const manifest = await this.installVSIX(vsix, installOptions, force, installed);
if (manifest) {
installedExtensionsManifests.push(manifest);
}
await this.installVSIX(vsix, installOptions, force, installed);
} catch (err) {
this.logger.error(err);
failed.push(vsix.toString());
@ -118,40 +114,8 @@ export class ExtensionManagementCLI {
}
if (installExtensionInfos.length) {
installExtensionInfos = installExtensionInfos.filter(({ id, version }) => {
const installedExtension = installed.find(i => areSameExtensions(i.identifier, { id }));
if (installedExtension) {
if (!force && (!version || (version === 'prerelease' && installedExtension.preRelease))) {
this.logger.info(localize('alreadyInstalled-checkAndUpdate', "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@<version>' to install a specific version, for example: '{2}@1.2.3'.", id, installedExtension.manifest.version, id));
return false;
}
if (version && installedExtension.manifest.version === version) {
this.logger.info(localize('alreadyInstalled', "Extension '{0}' is already installed.", `${id}@${version}`));
return false;
}
}
return true;
});
if (installExtensionInfos.length) {
const galleryExtensions = await this.getGalleryExtensions(installExtensionInfos);
await Promise.all(installExtensionInfos.map(async extensionInfo => {
const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase());
if (gallery) {
try {
const manifest = await this.installFromGallery(extensionInfo, gallery, installed);
if (manifest) {
installedExtensionsManifests.push(manifest);
}
} catch (err) {
this.logger.error(err.message || err.stack || err);
failed.push(extensionInfo.id);
}
} else {
this.logger.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`);
failed.push(extensionInfo.id);
}
}));
}
const failedGalleryExtensions = await this.installGalleryExtensions(installExtensionInfos, installed, force);
failed.push(...failedGalleryExtensions);
}
} catch (error) {
this.logger.error(localize('error while installing extensions', "Error while installing extensions: {0}", getErrorMessage(error)));
@ -205,7 +169,81 @@ export class ExtensionManagementCLI {
}
}
private async installVSIX(vsix: URI, installOptions: InstallOptions, force: boolean, installedExtensions: ILocalExtension[]): Promise<IExtensionManifest | null> {
private async installGalleryExtensions(installExtensionInfos: InstallGalleryExtensionInfo[], installed: ILocalExtension[], force: boolean): Promise<string[]> {
installExtensionInfos = installExtensionInfos.filter(({ id, version }) => {
const installedExtension = installed.find(i => areSameExtensions(i.identifier, { id }));
if (installedExtension) {
if (!force && (!version || (version === 'prerelease' && installedExtension.preRelease))) {
this.logger.info(localize('alreadyInstalled-checkAndUpdate', "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@<version>' to install a specific version, for example: '{2}@1.2.3'.", id, installedExtension.manifest.version, id));
return false;
}
if (version && installedExtension.manifest.version === version) {
this.logger.info(localize('alreadyInstalled', "Extension '{0}' is already installed.", `${id}@${version}`));
return false;
}
}
return true;
});
if (!installExtensionInfos.length) {
return [];
}
const failed: string[] = [];
const extensionsToInstall: InstallExtensionInfo[] = [];
const galleryExtensions = await this.getGalleryExtensions(installExtensionInfos);
await Promise.all(installExtensionInfos.map(async ({ id, version, installOptions }) => {
const gallery = galleryExtensions.get(id.toLowerCase());
if (!gallery) {
this.logger.error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`);
failed.push(id);
return;
}
try {
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
if (manifest && !this.validateExtensionKind(manifest)) {
return;
}
} catch (err) {
this.logger.error(err.message || err.stack || err);
failed.push(id);
return;
}
const installedExtension = installed.find(e => areSameExtensions(e.identifier, gallery.identifier));
if (installedExtension) {
if (gallery.version === installedExtension.manifest.version) {
this.logger.info(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id));
return;
}
this.logger.info(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, gallery.version));
}
if (installOptions.isBuiltin) {
this.logger.info(version ? localize('installing builtin with version', "Installing builtin extension '{0}' v{1}...", id, version) : localize('installing builtin ', "Installing builtin extension '{0}'...", id));
} else {
this.logger.info(version ? localize('installing with version', "Installing extension '{0}' v{1}...", id, version) : localize('installing', "Installing extension '{0}'...", id));
}
extensionsToInstall.push({
extension: gallery,
options: { ...installOptions, installGivenVersion: !!version },
});
}));
if (extensionsToInstall.length) {
const installationResult = await this.extensionManagementService.installGalleryExtensions(extensionsToInstall);
for (const extensionResult of installationResult) {
if (extensionResult.error) {
this.logger.error(localize('errorInstallingExtension', "Error while installing extension {0}: {1}", extensionResult.identifier.id, getErrorMessage(extensionResult.error)));
failed.push(extensionResult.identifier.id);
} else {
this.logger.info(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", extensionResult.identifier.id, extensionResult.local?.manifest.version));
}
}
}
return failed;
}
private async installVSIX(vsix: URI, installOptions: InstallOptions, force: boolean, installedExtensions: ILocalExtension[]): Promise<void> {
const manifest = await this.extensionManagementService.getManifest(vsix);
if (!manifest) {
@ -217,20 +255,17 @@ export class ExtensionManagementCLI {
try {
await this.extensionManagementService.install(vsix, installOptions);
this.logger.info(localize('successVsixInstall', "Extension '{0}' was successfully installed.", basename(vsix)));
return manifest;
} catch (error) {
if (isCancellationError(error)) {
this.logger.info(localize('cancelVsixInstall', "Cancelled installing extension '{0}'.", basename(vsix)));
return null;
} else {
throw error;
}
}
}
return null;
}
private async getGalleryExtensions(extensions: CLIInstallExtensionInfo[]): Promise<Map<string, IGalleryExtension>> {
private async getGalleryExtensions(extensions: InstallGalleryExtensionInfo[]): Promise<Map<string, IGalleryExtension>> {
const galleryExtensions = new Map<string, IGalleryExtension>();
const preRelease = extensions.some(e => e.installOptions.installPreReleaseVersion);
const targetPlatform = await this.extensionManagementService.getTargetPlatform();
@ -249,41 +284,6 @@ export class ExtensionManagementCLI {
return galleryExtensions;
}
private async installFromGallery({ id, version, installOptions }: CLIInstallExtensionInfo, galleryExtension: IGalleryExtension, installed: ILocalExtension[]): Promise<IExtensionManifest | null> {
const manifest = await this.extensionGalleryService.getManifest(galleryExtension, CancellationToken.None);
if (manifest && !this.validateExtensionKind(manifest)) {
return null;
}
const installedExtension = installed.find(e => areSameExtensions(e.identifier, galleryExtension.identifier));
if (installedExtension) {
if (galleryExtension.version === installedExtension.manifest.version) {
this.logger.info(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id));
return null;
}
this.logger.info(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, galleryExtension.version));
}
try {
if (installOptions.isBuiltin) {
this.logger.info(version ? localize('installing builtin with version', "Installing builtin extension '{0}' v{1}...", id, version) : localize('installing builtin ', "Installing builtin extension '{0}'...", id));
} else {
this.logger.info(version ? localize('installing with version', "Installing extension '{0}' v{1}...", id, version) : localize('installing', "Installing extension '{0}'...", id));
}
const local = await this.extensionManagementService.installFromGallery(galleryExtension, { ...installOptions, installGivenVersion: !!version });
this.logger.info(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, local.manifest.version));
return manifest;
} catch (error) {
if (isCancellationError(error)) {
this.logger.info(localize('cancelInstall', "Cancelled installing extension '{0}'.", id));
return null;
} else {
throw error;
}
}
}
protected validateExtensionKind(_manifest: IExtensionManifest): boolean {
return true;
}