提交 ad6be18d 编写于 作者: S Sandeep Somavarapu

#12764 Support installing specific extension version

上级 2d931dc4
......@@ -54,7 +54,6 @@ const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser';
const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled';
const INSTALL_ERROR_DOWNLOADING = 'downloading';
const INSTALL_ERROR_VALIDATING = 'validating';
const INSTALL_ERROR_GALLERY = 'gallery';
const INSTALL_ERROR_LOCAL = 'local';
const INSTALL_ERROR_EXTRACTING = 'extracting';
const INSTALL_ERROR_RENAMING = 'renaming';
......@@ -284,56 +283,74 @@ export class ExtensionManagementService extends Disposable implements IExtension
}));
}
installFromGallery(extension: IGalleryExtension): Promise<void> {
this.logService.trace('ExtensionManagementService#installFromGallery', extension.identifier.id);
let cancellablePromise = this.installingExtensions.get(extension.identifier.id);
async installFromGallery(extension: IGalleryExtension): Promise<void> {
const startTime = new Date().getTime();
this.logService.info('Installing extension:', extension.name);
this._onInstallExtension.fire({ identifier: extension.identifier, gallery: extension });
const onDidInstallExtensionSuccess = (extension: IGalleryExtension, operation: InstallOperation, local: ILocalExtension) => {
this.logService.info(`Extensions installed successfully:`, extension.identifier.id);
this._onDidInstallExtension.fire({ identifier: { id: getLocalExtensionIdFromGallery(extension, extension.version), uuid: extension.identifier.uuid }, gallery: extension, local, operation });
this.reportTelemetry(this.getTelemetryEvent(operation), getGalleryExtensionTelemetryData(extension), new Date().getTime() - startTime, void 0);
};
const onDidInstallExtensionFailure = (extension: IGalleryExtension, operation: InstallOperation, error) => {
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode);
this._onDidInstallExtension.fire({ identifier: extension.identifier, gallery: extension, operation, error: errorCode });
this.reportTelemetry(this.getTelemetryEvent(operation), getGalleryExtensionTelemetryData(extension), new Date().getTime() - startTime, error);
if (error instanceof Error) {
error.name = errorCode;
}
};
try {
extension = await this.checkAndGetCompatibleVersion(extension);
} catch (error) {
onDidInstallExtensionFailure(extension, InstallOperation.Install, error);
return Promise.reject(error);
}
const key = getLocalExtensionId(extension.identifier.id, extension.version);
let cancellablePromise = this.installingExtensions.get(key);
if (!cancellablePromise) {
let operation: InstallOperation = InstallOperation.Install;
let cancellationToken: CancellationToken, successCallback: ValueCallback<void>, errorCallback: ErrorCallback;
cancellablePromise = createCancelablePromise(token => { cancellationToken = token; return new Promise((c, e) => { successCallback = c; errorCallback = e; }); });
this.installingExtensions.set(extension.identifier.id, cancellablePromise);
this.installingExtensions.set(key, cancellablePromise);
try {
const startTime = new Date().getTime();
const identifier = { id: getLocalExtensionIdFromGallery(extension, extension.version), uuid: extension.identifier.uuid };
const telemetryData = getGalleryExtensionTelemetryData(extension);
let operation: InstallOperation = InstallOperation.Install;
this.logService.info('Installing extension:', extension.name);
this._onInstallExtension.fire({ identifier, gallery: extension });
this.checkMalicious(extension)
.then(() => this.getInstalled(LocalExtensionType.User))
.then(installed => {
const installed = await this.getInstalled(LocalExtensionType.User);
const existingExtension = installed.filter(i => areSameExtensions(i.galleryIdentifier, extension.identifier))[0];
operation = existingExtension ? InstallOperation.Update : InstallOperation.Install;
return this.downloadInstallableExtension(extension, operation)
.then(installableExtension => this.installExtension(installableExtension, LocalExtensionType.User, cancellationToken).then(local => always(pfs.rimraf(installableExtension.zipPath), () => null).then(() => local)))
if (existingExtension) {
operation = InstallOperation.Update;
if (semver.gt(existingExtension.manifest.version, extension.version)) {
await this.uninstall(existingExtension, true);
}
}
this.downloadInstallableExtension(extension, operation)
.then(installableExtension => this.installExtension(installableExtension, LocalExtensionType.User, cancellationToken)
.then(local => always(pfs.rimraf(installableExtension.zipPath), () => null).then(() => local)))
.then(local => this.installDependenciesAndPackExtensions(local, existingExtension)
.then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error))));
})
.then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error))))
.then(
local => {
this.installingExtensions.delete(extension.identifier.id);
this.logService.info(`Extensions installed successfully:`, extension.identifier.id);
this._onDidInstallExtension.fire({ identifier, gallery: extension, local, operation });
this.reportTelemetry(this.getTelemetryEvent(operation), telemetryData, new Date().getTime() - startTime, void 0);
this.installingExtensions.delete(key);
onDidInstallExtensionSuccess(extension, operation, local);
successCallback(null);
},
error => {
this.installingExtensions.delete(extension.identifier.id);
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode);
this._onDidInstallExtension.fire({ identifier, gallery: extension, operation, error: errorCode });
this.reportTelemetry(this.getTelemetryEvent(operation), telemetryData, new Date().getTime() - startTime, error);
if (error instanceof Error) {
error.name = errorCode;
}
this.installingExtensions.delete(key);
onDidInstallExtensionFailure(extension, operation, error);
errorCallback(error);
});
} catch (error) {
this.installingExtensions.delete(extension.identifier.id);
this.installingExtensions.delete(key);
onDidInstallExtensionFailure(extension, operation, error);
errorCallback(error);
}
......@@ -342,6 +359,20 @@ export class ExtensionManagementService extends Disposable implements IExtension
return cancellablePromise;
}
private async checkAndGetCompatibleVersion(extension: IGalleryExtension): Promise<IGalleryExtension> {
if (await this.isMalicious(extension)) {
return Promise.reject(new ExtensionManagementError(nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."), INSTALL_ERROR_MALICIOUS));
}
extension = await this.galleryService.loadCompatibleVersion(extension);
if (!extension) {
return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
}
return extension;
}
reinstallFromGallery(extension: ILocalExtension): Promise<void> {
this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.identifier.id);
if (!this.galleryService.isEnabled()) {
......@@ -368,15 +399,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
return operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install';
}
private checkMalicious(extension: IGalleryExtension): Promise<void> {
private isMalicious(extension: IGalleryExtension): Promise<boolean> {
return this.getExtensionsReport()
.then(report => {
if (getMaliciousExtensionsSet(report).has(extension.identifier.id)) {
throw new ExtensionManagementError(INSTALL_ERROR_MALICIOUS, nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."));
} else {
return null;
}
});
.then(report => getMaliciousExtensionsSet(report).has(extension.identifier.id));
}
private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise<InstallableExtension> {
......@@ -386,12 +411,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
publisherDisplayName: extension.publisherDisplayName,
};
return this.galleryService.loadCompatibleVersion(extension)
.then(
compatible => {
if (compatible) {
this.logService.trace('Started downloading extension:', extension.name);
return this.galleryService.download(compatible, operation)
return this.galleryService.download(extension, operation)
.then(
zipPath => {
this.logService.info('Downloaded extension:', extension.name, zipPath);
......@@ -402,11 +423,6 @@ export class ExtensionManagementService extends Disposable implements IExtension
);
},
error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING)));
} else {
return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
}
},
error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
}
private installExtension(installableExtension: InstallableExtension, type: LocalExtensionType, token: CancellationToken): Promise<ILocalExtension> {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册