mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-27 17:23:21 -05:00
Merge VS Code 1.23.1 (#1520)
This commit is contained in:
@@ -18,7 +18,7 @@ import { IPager } from 'vs/base/common/paging';
|
||||
import { IRequestOptions, IRequestContext, download, asJson, asText } from 'vs/base/node/request';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { isVersionValid } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { readFile } from 'vs/base/node/pfs';
|
||||
import { writeFileAndFlushSync } from 'vs/base/node/extfs';
|
||||
@@ -324,7 +324,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
|
||||
},
|
||||
/* __GDPR__FRAGMENT__
|
||||
"GalleryExtensionTelemetryData2" : {
|
||||
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"searchText": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"querySource": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
@@ -586,7 +586,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
const startTime = new Date().getTime();
|
||||
/* __GDPR__
|
||||
"galleryService:downloadVSIX" : {
|
||||
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
@@ -622,7 +622,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
}
|
||||
|
||||
loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension> {
|
||||
if (extension.properties.engine && this.isEngineValid(extension.properties.engine)) {
|
||||
if (extension.properties.engine && isEngineValid(extension.properties.engine)) {
|
||||
return TPromise.wrap(extension);
|
||||
}
|
||||
|
||||
@@ -738,7 +738,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
/* __GDPR__
|
||||
"galleryService:requestError" : {
|
||||
"url" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -786,7 +786,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
if (!engine) {
|
||||
return null;
|
||||
}
|
||||
if (this.isEngineValid(engine)) {
|
||||
if (isEngineValid(engine)) {
|
||||
return TPromise.wrap(version);
|
||||
}
|
||||
}
|
||||
@@ -807,7 +807,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
.then(manifest => {
|
||||
const engine = manifest.engines.vscode;
|
||||
|
||||
if (!this.isEngineValid(engine)) {
|
||||
if (!isEngineValid(engine)) {
|
||||
return this.getLastValidExtensionVersionReccursively(extension, versions.slice(1));
|
||||
}
|
||||
|
||||
@@ -817,11 +817,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
});
|
||||
}
|
||||
|
||||
private isEngineValid(engine: string): boolean {
|
||||
// TODO@joao: discuss with alex '*' doesn't seem to be a valid engine version
|
||||
return engine === '*' || isVersionValid(pkg.version, engine);
|
||||
}
|
||||
|
||||
private static hasExtensionByName(extensions: IGalleryExtension[], name: string): boolean {
|
||||
for (const extension of extensions) {
|
||||
if (`${extension.publisher}.${extension.name}` === name) {
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'path';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { toDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { flatten, distinct, coalesce } from 'vs/base/common/arrays';
|
||||
import { extract, buffer } from 'vs/base/node/zip';
|
||||
import { flatten, distinct } from 'vs/base/common/arrays';
|
||||
import { extract, buffer, ExtractError } from 'vs/base/node/zip';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import {
|
||||
IExtensionManagementService, IExtensionGalleryService, ILocalExtension,
|
||||
@@ -22,21 +22,23 @@ import {
|
||||
IExtensionIdentifier,
|
||||
IReportedExtension
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { localizeManifest } from '../common/extensionNls';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { Limiter } from 'vs/base/common/async';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Limiter, always } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as semver from 'semver';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
|
||||
|
||||
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
|
||||
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
|
||||
@@ -48,10 +50,9 @@ 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';
|
||||
const INSTALL_ERROR_DELETING = 'deleting';
|
||||
const INSTALL_ERROR_READING_EXTENSION_FROM_DISK = 'readingExtension';
|
||||
const INSTALL_ERROR_SAVING_METADATA = 'savingMetadata';
|
||||
const INSTALL_ERROR_UNKNOWN = 'unknown';
|
||||
const ERROR_UNKNOWN = 'unknown';
|
||||
|
||||
export class ExtensionManagementError extends Error {
|
||||
constructor(message: string, readonly code: string) {
|
||||
@@ -101,6 +102,11 @@ interface InstallableExtension {
|
||||
metadata?: IGalleryMetadata;
|
||||
}
|
||||
|
||||
enum Operation {
|
||||
Install = 1,
|
||||
Update
|
||||
}
|
||||
|
||||
export class ExtensionManagementService extends Disposable implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: any;
|
||||
@@ -110,6 +116,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
private uninstalledFileLimiter: Limiter<void>;
|
||||
private reportedExtensions: TPromise<IReportedExtension[]> | undefined;
|
||||
private lastReportTimestamp = 0;
|
||||
private readonly installationStartTime: Map<string, number> = new Map<string, number>();
|
||||
private readonly installingExtensions: Map<string, TPromise<ILocalExtension>> = new Map<string, TPromise<ILocalExtension>>();
|
||||
private readonly manifestCache: ExtensionsManifestCache;
|
||||
private readonly extensionLifecycle: ExtensionsLifecycle;
|
||||
@@ -128,9 +135,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IDialogService private dialogService: IDialogService,
|
||||
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
|
||||
@ILogService private logService: ILogService
|
||||
@ILogService private logService: ILogService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
) {
|
||||
super();
|
||||
this.extensionsPath = environmentService.extensionsPath;
|
||||
@@ -141,13 +149,17 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService));
|
||||
}
|
||||
|
||||
install(zipPath: string): TPromise<void> {
|
||||
install(zipPath: string): TPromise<ILocalExtension> {
|
||||
zipPath = path.resolve(zipPath);
|
||||
|
||||
return validateLocalExtension(zipPath)
|
||||
.then(manifest => {
|
||||
const identifier = { id: getLocalExtensionIdFromManifest(manifest) };
|
||||
return this.unsetUninstalledAndRemove(identifier.id)
|
||||
// {{SQL CARBON EDIT - Remove VS Code version check}}
|
||||
// if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) {
|
||||
// return TPromise.wrapError<ILocalExtension>(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version)));
|
||||
// }
|
||||
return this.removeIfExists(identifier.id)
|
||||
.then(
|
||||
() => this.checkOutdated(manifest)
|
||||
.then(validated => {
|
||||
@@ -175,18 +187,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
});
|
||||
}
|
||||
|
||||
private unsetUninstalledAndRemove(id: string): TPromise<void> {
|
||||
return this.isUninstalled(id)
|
||||
.then(isUninstalled => {
|
||||
if (isUninstalled) {
|
||||
this.logService.trace('Removing the extension:', id);
|
||||
const extensionPath = path.join(this.extensionsPath, id);
|
||||
return pfs.rimraf(extensionPath)
|
||||
.then(() => this.unsetUninstalled(id))
|
||||
.then(() => this.logService.info('Removed the extension:', id));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
private removeIfExists(id: string): TPromise<void> {
|
||||
return this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed => installed.filter(i => i.identifier.id === id)[0])
|
||||
.then(existing => existing ? this.removeExtension(existing, 'existing') : null);
|
||||
}
|
||||
|
||||
private checkOutdated(manifest: IExtensionManifest): TPromise<boolean> {
|
||||
@@ -196,11 +200,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, { id: getGalleryExtensionIdFromLocal(local) }) && semver.gt(local.manifest.version, manifest.version))[0];
|
||||
if (newer) {
|
||||
const message = nls.localize('installingOutdatedExtension', "A newer version of this extension is already installed. Would you like to override this with the older version?");
|
||||
const options = [
|
||||
const buttons = [
|
||||
nls.localize('override', "Override"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
return this.choiceService.choose(Severity.Info, message, options, 1, true)
|
||||
return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 1 })
|
||||
.then<boolean>(value => {
|
||||
if (value === 0) {
|
||||
return this.uninstall(newer, true).then(() => true);
|
||||
@@ -212,58 +216,65 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
});
|
||||
}
|
||||
|
||||
private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<void> {
|
||||
private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<ILocalExtension> {
|
||||
return this.installExtension({ zipPath, id: identifier.id, metadata })
|
||||
.then(local => {
|
||||
if (this.galleryService.isEnabled() && local.manifest.extensionDependencies && local.manifest.extensionDependencies.length) {
|
||||
return this.getDependenciesToInstall(local.manifest.extensionDependencies)
|
||||
.then(dependenciesToInstall => this.downloadAndInstallExtensions(metadata ? dependenciesToInstall.filter(d => d.identifier.uuid !== metadata.id) : dependenciesToInstall))
|
||||
.then(() => local, error => {
|
||||
this.uninstallExtension(local);
|
||||
this.setUninstalled(local);
|
||||
return TPromise.wrapError(new Error(nls.localize('errorInstallingDependencies', "Error while installing dependencies. {0}", error instanceof Error ? error.message : error)));
|
||||
});
|
||||
}
|
||||
return local;
|
||||
})
|
||||
.then(
|
||||
local => this._onDidInstallExtension.fire({ identifier, zipPath, local }),
|
||||
error => { this._onDidInstallExtension.fire({ identifier, zipPath, error }); return TPromise.wrapError(error); }
|
||||
local => { this._onDidInstallExtension.fire({ identifier, zipPath, local }); return local; },
|
||||
error => { this._onDidInstallExtension.fire({ identifier, zipPath, error }); return TPromise.wrapError(error); }
|
||||
);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
|
||||
this.onInstallExtensions([extension]);
|
||||
return this.collectExtensionsToInstall(extension)
|
||||
.then(
|
||||
extensionsToInstall => {
|
||||
if (extensionsToInstall.length > 1) {
|
||||
this.onInstallExtensions(extensionsToInstall.slice(1));
|
||||
}
|
||||
return this.downloadAndInstallExtensions(extensionsToInstall)
|
||||
.then(
|
||||
locals => this.onDidInstallExtensions(extensionsToInstall, locals, []),
|
||||
errors => this.onDidInstallExtensions(extensionsToInstall, [], errors));
|
||||
},
|
||||
error => this.onDidInstallExtensions([extension], [], [error]));
|
||||
return this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed => this.collectExtensionsToInstall(extension)
|
||||
.then(
|
||||
extensionsToInstall => {
|
||||
if (extensionsToInstall.length > 1) {
|
||||
this.onInstallExtensions(extensionsToInstall.slice(1));
|
||||
}
|
||||
const operataions: Operation[] = extensionsToInstall.map(e => this.getOperation(e, installed));
|
||||
return this.downloadAndInstallExtensions(extensionsToInstall)
|
||||
.then(
|
||||
locals => this.onDidInstallExtensions(extensionsToInstall, locals, operataions, [])
|
||||
.then(() => locals.filter(l => areSameExtensions({ id: getGalleryExtensionIdFromLocal(l), uuid: l.identifier.uuid }, extension.identifier)[0])),
|
||||
errors => this.onDidInstallExtensions(extensionsToInstall, [], operataions, errors));
|
||||
},
|
||||
error => this.onDidInstallExtensions([extension], [], [this.getOperation(extension, installed)], [error])));
|
||||
}
|
||||
|
||||
reinstall(extension: ILocalExtension): TPromise<void> {
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension> {
|
||||
if (!this.galleryService.isEnabled()) {
|
||||
return TPromise.wrapError(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")));
|
||||
}
|
||||
return this.findGalleryExtension(extension)
|
||||
.then(galleryExtension => {
|
||||
if (galleryExtension) {
|
||||
return this.uninstallExtension(extension)
|
||||
return this.setUninstalled(extension)
|
||||
.then(() => this.removeUninstalledExtension(extension)
|
||||
.then(
|
||||
() => this.installFromGallery(galleryExtension),
|
||||
e => TPromise.wrapError(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))))));
|
||||
() => this.installFromGallery(galleryExtension),
|
||||
e => TPromise.wrapError(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))))));
|
||||
}
|
||||
return TPromise.wrapError(new Error(nls.localize('Not Market place extension', "Only Market place Extensions can be reinstalled")));
|
||||
return TPromise.wrapError(new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled")));
|
||||
});
|
||||
}
|
||||
|
||||
private getOperation(extensionToInstall: IGalleryExtension, installed: ILocalExtension[]): Operation {
|
||||
return installed.some(i => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i), uuid: i.identifier.uuid }, extensionToInstall.identifier)) ? Operation.Update : Operation.Install;
|
||||
}
|
||||
|
||||
private collectExtensionsToInstall(extension: IGalleryExtension): TPromise<IGalleryExtension[]> {
|
||||
return this.galleryService.loadCompatibleVersion(extension)
|
||||
.then(compatible => {
|
||||
@@ -273,10 +284,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
return this.getDependenciesToInstall(compatible.properties.dependencies)
|
||||
.then(
|
||||
dependenciesToInstall => ([compatible, ...dependenciesToInstall.filter(d => d.identifier.uuid !== compatible.identifier.uuid)]),
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
dependenciesToInstall => ([compatible, ...dependenciesToInstall.filter(d => d.identifier.uuid !== compatible.identifier.uuid)]),
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
},
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
}
|
||||
|
||||
private downloadAndInstallExtensions(extensions: IGalleryExtension[]): TPromise<ILocalExtension[]> {
|
||||
@@ -298,8 +309,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
.then(extension => this.downloadInstallableExtension(extension))
|
||||
.then(installableExtension => this.installExtension(installableExtension))
|
||||
.then(
|
||||
local => { this.installingExtensions.delete(extension.identifier.id); return local; },
|
||||
e => { this.installingExtensions.delete(extension.identifier.id); return TPromise.wrapError(e); }
|
||||
local => { this.installingExtensions.delete(extension.identifier.id); return local; },
|
||||
e => { this.installingExtensions.delete(extension.identifier.id); return TPromise.wrapError(e); }
|
||||
);
|
||||
|
||||
this.installingExtensions.set(extension.identifier.id, installingExtension);
|
||||
@@ -316,36 +327,37 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
|
||||
return this.galleryService.loadCompatibleVersion(extension)
|
||||
.then(
|
||||
compatible => {
|
||||
if (compatible) {
|
||||
this.logService.trace('Started downloading extension:', extension.name);
|
||||
return this.galleryService.download(extension)
|
||||
.then(
|
||||
zipPath => {
|
||||
this.logService.info('Downloaded extension:', extension.name);
|
||||
return validateLocalExtension(zipPath)
|
||||
.then(
|
||||
manifest => (<InstallableExtension>{ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }),
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING))
|
||||
);
|
||||
},
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING)));
|
||||
} else {
|
||||
return TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the depending extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
|
||||
}
|
||||
},
|
||||
error => TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
compatible => {
|
||||
if (compatible) {
|
||||
this.logService.trace('Started downloading extension:', extension.name);
|
||||
return this.galleryService.download(extension)
|
||||
.then(
|
||||
zipPath => {
|
||||
this.logService.info('Downloaded extension:', extension.name);
|
||||
return validateLocalExtension(zipPath)
|
||||
.then(
|
||||
manifest => (<InstallableExtension>{ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }),
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING))
|
||||
);
|
||||
},
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING)));
|
||||
} else {
|
||||
return TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the depending extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
|
||||
}
|
||||
},
|
||||
error => TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
}
|
||||
|
||||
private onInstallExtensions(extensions: IGalleryExtension[]): void {
|
||||
for (const extension of extensions) {
|
||||
this.logService.info('Installing extension:', extension.name);
|
||||
this.installationStartTime.set(extension.identifier.id, new Date().getTime());
|
||||
const id = getLocalExtensionIdFromGallery(extension, extension.version);
|
||||
this._onInstallExtension.fire({ identifier: { id, uuid: extension.identifier.uuid }, gallery: extension });
|
||||
}
|
||||
}
|
||||
|
||||
private onDidInstallExtensions(extensions: IGalleryExtension[], locals: ILocalExtension[], errors: Error[]): TPromise<any> {
|
||||
private onDidInstallExtensions(extensions: IGalleryExtension[], locals: ILocalExtension[], operations: Operation[], errors: Error[]): TPromise<any> {
|
||||
extensions.forEach((gallery, index) => {
|
||||
const identifier = { id: getLocalExtensionIdFromGallery(gallery, gallery.version), uuid: gallery.identifier.uuid };
|
||||
const local = locals[index];
|
||||
@@ -354,10 +366,13 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
this.logService.info(`Extensions installed successfully:`, gallery.identifier.id);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery, local });
|
||||
} else {
|
||||
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : INSTALL_ERROR_UNKNOWN;
|
||||
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
|
||||
this.logService.error(`Failed to install extension:`, gallery.identifier.id, error ? error.message : errorCode);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery, error: errorCode });
|
||||
}
|
||||
const startTime = this.installationStartTime.get(gallery.identifier.id);
|
||||
this.reportTelemetry(operations[index] === Operation.Install ? 'extensionGallery:install' : 'extensionGallery:update', getGalleryExtensionTelemetryData(gallery), startTime ? new Date().getTime() - startTime : void 0, error);
|
||||
this.installationStartTime.delete(gallery.identifier.id);
|
||||
});
|
||||
return errors.length ? TPromise.wrapError(this.joinErrors(errors)) : TPromise.as(null);
|
||||
}
|
||||
@@ -383,18 +398,18 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
private installExtension(installableExtension: InstallableExtension): TPromise<ILocalExtension> {
|
||||
return this.unsetUninstalledAndGetLocal(installableExtension.id)
|
||||
.then(
|
||||
local => {
|
||||
if (local) {
|
||||
return local;
|
||||
}
|
||||
return this.extractAndInstall(installableExtension);
|
||||
},
|
||||
e => {
|
||||
if (isMacintosh) {
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('quitCode', "Unable to install the extension. Please Quit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
}
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('exitCode', "Unable to install the extension. Please Exit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
});
|
||||
local => {
|
||||
if (local) {
|
||||
return local;
|
||||
}
|
||||
return this.extractAndInstall(installableExtension);
|
||||
},
|
||||
e => {
|
||||
if (isMacintosh) {
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('quitCode', "Unable to install the extension. Please Quit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
}
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('exitCode', "Unable to install the extension. Please Exit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
});
|
||||
}
|
||||
|
||||
private unsetUninstalledAndGetLocal(id: string): TPromise<ILocalExtension> {
|
||||
@@ -415,43 +430,53 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
private extractAndInstall({ zipPath, id, metadata }: InstallableExtension): TPromise<ILocalExtension> {
|
||||
const tempPath = path.join(this.extensionsPath, `.${id}`);
|
||||
const extensionPath = path.join(this.extensionsPath, id);
|
||||
return pfs.rimraf(extensionPath)
|
||||
return this.extractAndRename(id, zipPath, tempPath, extensionPath)
|
||||
.then(() => {
|
||||
this.logService.trace(`Started extracting the extension from ${zipPath} to ${extensionPath}`);
|
||||
return extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true })
|
||||
.then(
|
||||
() => {
|
||||
this.logService.info(`Extracted extension to ${extensionPath}:`, id);
|
||||
return this.completeInstall(id, extensionPath, metadata);
|
||||
},
|
||||
e => TPromise.wrapError(new ExtensionManagementError(e.message, INSTALL_ERROR_EXTRACTING)))
|
||||
.then(null, e => {
|
||||
this.logService.info('Deleting the extracted extension', id);
|
||||
return pfs.rimraf(extensionPath).then(() => TPromise.wrapError(e), () => TPromise.wrapError(e));
|
||||
});
|
||||
}, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
|
||||
this.logService.info('Installation completed.', id);
|
||||
return this.scanExtension(id, this.extensionsPath, LocalExtensionType.User);
|
||||
})
|
||||
.then(local => {
|
||||
if (metadata) {
|
||||
local.metadata = metadata;
|
||||
return this.saveMetadataForLocalExtension(local);
|
||||
}
|
||||
return local;
|
||||
});
|
||||
}
|
||||
|
||||
private completeInstall(id: string, extensionPath: string, metadata: IGalleryMetadata): TPromise<ILocalExtension> {
|
||||
return TPromise.join([readManifest(extensionPath), pfs.readdir(extensionPath)])
|
||||
.then(null, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_READING_EXTENSION_FROM_DISK)))
|
||||
.then(([{ manifest }, children]) => {
|
||||
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
|
||||
const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)).toString() : null;
|
||||
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
|
||||
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)).toString() : null;
|
||||
const type = LocalExtensionType.User;
|
||||
const identifier = { id, uuid: metadata ? metadata.id : null };
|
||||
private extractAndRename(id: string, zipPath: string, extractPath: string, renamePath: string): TPromise<void> {
|
||||
return this.extract(id, zipPath, extractPath)
|
||||
.then(() => this.rename(id, extractPath, renamePath, Date.now() + (30 * 1000) /* Retry for 30 seconds */)
|
||||
.then(
|
||||
() => this.logService.info('Renamed to', renamePath),
|
||||
e => {
|
||||
this.logService.info('Rename failed. Deleting from extracted location', extractPath);
|
||||
return always(pfs.rimraf(extractPath), () => null).then(() => TPromise.wrapError(e));
|
||||
}));
|
||||
}
|
||||
|
||||
const local: ILocalExtension = { type, identifier, manifest, metadata, path: extensionPath, readmeUrl, changelogUrl };
|
||||
private extract(id: string, zipPath: string, extractPath: string): TPromise<void> {
|
||||
this.logService.trace(`Started extracting the extension from ${zipPath} to ${extractPath}`);
|
||||
return pfs.rimraf(extractPath)
|
||||
.then(
|
||||
() => extract(zipPath, extractPath, { sourcePath: 'extension', overwrite: true }, this.logService)
|
||||
.then(
|
||||
() => this.logService.info(`Extracted extension to ${extractPath}:`, id),
|
||||
e => always(pfs.rimraf(extractPath), () => null)
|
||||
.then(() => TPromise.wrapError(new ExtensionManagementError(e.message, e instanceof ExtractError ? e.type : INSTALL_ERROR_EXTRACTING)))),
|
||||
e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
|
||||
}
|
||||
|
||||
this.logService.trace(`Updating metadata of the extension:`, id);
|
||||
return this.saveMetadataForLocalExtension(local)
|
||||
.then(() => {
|
||||
this.logService.info(`Updated metadata of the extension:`, id);
|
||||
return local;
|
||||
}, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_SAVING_METADATA)));
|
||||
private rename(id: string, extractPath: string, renamePath: string, retryUntil: number): TPromise<void> {
|
||||
return pfs.rename(extractPath, renamePath)
|
||||
.then(null, error => {
|
||||
if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) {
|
||||
this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`);
|
||||
return this.rename(id, extractPath, renamePath, retryUntil);
|
||||
}
|
||||
return TPromise.wrapError(new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -459,7 +484,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed =>
|
||||
TPromise.join(installed.filter(local => extensions.some(galleryExtension => local.identifier.id === getLocalExtensionIdFromGallery(galleryExtension, galleryExtension.version))) // Only check id (pub.name-version) because we want to rollback the exact version
|
||||
.map(local => this.uninstallExtension(local))))
|
||||
.map(local => this.setUninstalled(local))))
|
||||
.then(() => null, () => null);
|
||||
}
|
||||
|
||||
@@ -529,10 +554,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.preUninstallExtension(extension)
|
||||
.then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.promptAndUninstall(extension, installed, force))
|
||||
.then(() => this.postUninstallExtension(extension),
|
||||
error => {
|
||||
this.postUninstallExtension(extension, INSTALL_ERROR_LOCAL);
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
error => {
|
||||
this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private hasDependencies(extension: ILocalExtension, installed: ILocalExtension[]): boolean {
|
||||
@@ -549,13 +574,13 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
const message = nls.localize('uninstallDependeciesConfirmation', "Would you like to uninstall '{0}' only or its dependencies also?", extension.manifest.displayName || extension.manifest.name);
|
||||
const options = [
|
||||
nls.localize('uninstallOnly', "Only"),
|
||||
nls.localize('uninstallAll', "All"),
|
||||
const buttons = [
|
||||
nls.localize('uninstallOnly', "Extension Only"),
|
||||
nls.localize('uninstallAll', "Uninstall All"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
this.logService.info('Requesting for confirmation to uninstall extension with dependencies', extension.identifier.id);
|
||||
return this.choiceService.choose(Severity.Info, message, options, 2, true)
|
||||
return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 2 })
|
||||
.then<void>(value => {
|
||||
if (value === 0) {
|
||||
return this.uninstallWithDependencies(extension, [], installed);
|
||||
@@ -575,12 +600,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
const message = nls.localize('uninstallConfirmation', "Are you sure you want to uninstall '{0}'?", extension.manifest.displayName || extension.manifest.name);
|
||||
const options = [
|
||||
const buttons = [
|
||||
nls.localize('ok', "OK"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
this.logService.info('Requesting for confirmation to uninstall extension', extension.identifier.id);
|
||||
return this.choiceService.choose(Severity.Info, message, options, 1, true)
|
||||
return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 1 })
|
||||
.then<void>(value => {
|
||||
if (value === 0) {
|
||||
return this.uninstallWithDependencies(extension, [], installed);
|
||||
@@ -649,10 +674,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.preUninstallExtension(extension)
|
||||
.then(() => this.uninstallExtension(extension))
|
||||
.then(() => this.postUninstallExtension(extension),
|
||||
error => {
|
||||
this.postUninstallExtension(extension, INSTALL_ERROR_LOCAL);
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
error => {
|
||||
this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private preUninstallExtension(extension: ILocalExtension): TPromise<void> {
|
||||
@@ -665,12 +690,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
private uninstallExtension(local: ILocalExtension): TPromise<void> {
|
||||
return this.setUninstalled(local.identifier.id);
|
||||
// Set all versions of the extension as uninstalled
|
||||
return this.scanUserExtensions(false)
|
||||
.then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id: getGalleryExtensionIdFromLocal(local), uuid: local.identifier.uuid }))));
|
||||
}
|
||||
|
||||
private async postUninstallExtension(extension: ILocalExtension, error?: string): TPromise<void> {
|
||||
private async postUninstallExtension(extension: ILocalExtension, error?: Error): TPromise<void> {
|
||||
if (error) {
|
||||
this.logService.error('Failed to uninstall extension:', extension.identifier.id, error);
|
||||
this.logService.error('Failed to uninstall extension:', extension.identifier.id, error.message);
|
||||
} else {
|
||||
this.logService.info('Successfully uninstalled extension:', extension.identifier.id);
|
||||
// only report if extension has a mapped gallery extension. UUID identifies the gallery extension.
|
||||
@@ -678,7 +705,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall);
|
||||
}
|
||||
}
|
||||
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error });
|
||||
this.reportTelemetry('extensionGallery:uninstall', getLocalExtensionTelemetryData(extension), void 0, error);
|
||||
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0;
|
||||
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error: errorcode });
|
||||
}
|
||||
|
||||
getInstalled(type: LocalExtensionType = null): TPromise<ILocalExtension[]> {
|
||||
@@ -721,11 +750,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
private scanExtensions(root: string, type: LocalExtensionType): TPromise<ILocalExtension[]> {
|
||||
const limiter = new Limiter(10);
|
||||
return pfs.readdir(root)
|
||||
.then(extensionsFolders => TPromise.join(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type)))))
|
||||
.then(extensions => coalesce(extensions));
|
||||
.then(extensionsFolders => TPromise.join<ILocalExtension>(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type)))))
|
||||
.then(extensions => extensions.filter(e => e && e.identifier));
|
||||
}
|
||||
|
||||
private scanExtension(folderName: string, root: string, type: LocalExtensionType): TPromise<ILocalExtension> {
|
||||
if (type === LocalExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user exension folder starting with `.`
|
||||
return TPromise.as(null);
|
||||
}
|
||||
const extensionPath = path.join(root, folderName);
|
||||
return pfs.readdir(extensionPath)
|
||||
.then(children => readManifest(extensionPath)
|
||||
@@ -798,7 +830,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
});
|
||||
}
|
||||
|
||||
private setUninstalled(...ids: string[]): TPromise<void> {
|
||||
private setUninstalled(...extensions: ILocalExtension[]): TPromise<void> {
|
||||
const ids = extensions.map(e => e.identifier.id);
|
||||
return this.withUninstalledExtensions(uninstalled => assign(uninstalled, ids.reduce((result, id) => { result[id] = true; return result; }, {})));
|
||||
}
|
||||
|
||||
@@ -852,6 +885,31 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
private reportTelemetry(eventName: string, extensionData: any, duration: number, error?: Error): void {
|
||||
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0;
|
||||
/* __GDPR__
|
||||
"extensionGallery:install" : {
|
||||
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
/* __GDPR__
|
||||
"extensionGallery:uninstall" : {
|
||||
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog(eventName, assign(extensionData, { success: !error, duration, errorcode }));
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, version: string): string {
|
||||
@@ -860,4 +918,4 @@ export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, ver
|
||||
|
||||
export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string {
|
||||
return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user