Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

* disable strict null check
This commit is contained in:
Anthony Dresser
2019-07-15 22:35:46 -07:00
committed by GitHub
parent f720ec642f
commit 0b7e7ddbf9
2406 changed files with 59140 additions and 35464 deletions

View File

@@ -3,21 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { tmpdir } from 'os';
import * as path from 'vs/base/common/path';
import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/common/errors';
import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion, IGalleryExtensionAssets, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { assign, getOrDefault } from 'vs/base/common/objects';
import { IRequestService } from 'vs/platform/request/node/request';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPager } from 'vs/base/common/paging';
import { IRequestOptions, IRequestContext, download, asJson, asText } from 'vs/base/node/request';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
import { IRequestService, IRequestOptions, IRequestContext, asJson, asText } from 'vs/platform/request/common/request';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { writeFileSync, readFile } from 'vs/base/node/pfs';
import { generateUuid, isUUID } from 'vs/base/common/uuid';
import { values } from 'vs/base/common/map';
// {{SQL CARBON EDIT}}
@@ -27,6 +21,11 @@ import { ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/workbench/contrib/exte
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { IProductService } from 'vs/platform/product/common/product';
interface IRawGalleryExtensionFile {
assetType: string;
@@ -212,13 +211,16 @@ function getStatistic(statistics: IRawGalleryExtensionStatistics[], name: string
return result ? result.value : 0;
}
function getCoreTranslationAssets(version: IRawGalleryExtensionVersion): { [languageId: string]: IGalleryExtensionAsset } {
function getCoreTranslationAssets(version: IRawGalleryExtensionVersion): [string, IGalleryExtensionAsset][] {
const coreTranslationAssetPrefix = 'Microsoft.VisualStudio.Code.Translation.';
const result = version.files.filter(f => f.assetType.indexOf(coreTranslationAssetPrefix) === 0);
return result.reduce((result, file) => {
result[file.assetType.substring(coreTranslationAssetPrefix.length)] = getVersionAsset(version, file.assetType);
return result.reduce<[string, IGalleryExtensionAsset][]>((result, file) => {
const asset = getVersionAsset(version, file.assetType);
if (asset) {
result.push([file.assetType.substring(coreTranslationAssetPrefix.length), asset]);
}
return result;
}, {});
}, []);
}
function getRepositoryAsset(version: IRawGalleryExtensionVersion): IGalleryExtensionAsset | null {
@@ -372,8 +374,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
_serviceBrand: any;
private extensionsGalleryUrl: string;
private extensionsControlUrl: string;
private extensionsGalleryUrl: string | undefined;
private extensionsControlUrl: string | undefined;
private readonly commonHeadersPromise: Promise<{ [key: string]: string; }>;
@@ -383,12 +385,14 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
// {{SQL CARBON EDIT}}
@IConfigurationService private configurationService: IConfigurationService
@IConfigurationService private configurationService: IConfigurationService,
@IFileService private readonly fileService: IFileService,
@IProductService private readonly productService: IProductService,
) {
const config = product.extensionsGallery;
const config = productService.extensionsGallery;
this.extensionsGalleryUrl = config && config.serviceUrl;
this.extensionsControlUrl = config && config.controlUrl;
this.commonHeadersPromise = resolveMarketplaceHeaders(this.environmentService);
this.commonHeadersPromise = resolveMarketplaceHeaders(productService.version, this.environmentService, this.fileService);
}
private api(path = ''): string {
@@ -433,7 +437,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const versionAsset = rawExtension.versions.filter(v => v.version === version)[0];
if (versionAsset) {
const extension = toExtension(rawExtension, versionAsset, 0, query);
if (extension.properties.engine && isEngineValid(extension.properties.engine)) {
if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) {
return extension;
}
}
@@ -697,9 +701,9 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
});
}
download(extension: IGalleryExtension, operation: InstallOperation): Promise<string> {
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<URI> {
this.logService.trace('ExtensionGalleryService#download', extension.identifier.id);
const zipPath = path.join(tmpdir(), generateUuid());
const zip = joinPath(location, generateUuid());
const data = getGalleryExtensionTelemetryData(extension);
const startTime = new Date().getTime();
/* __GDPR__
@@ -721,9 +725,9 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
} : extension.assets.download;
return this.getAsset(downloadAsset)
.then(context => download(zipPath, context))
.then(context => this.fileService.writeFile(zip, context.stream))
.then(() => log(new Date().getTime() - startTime))
.then(() => zipPath);
.then(() => zip);
}
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string> {
@@ -745,9 +749,9 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null> {
const asset = extension.assets.coreTranslations[languageId.toUpperCase()];
const asset = extension.assets.coreTranslations.filter(t => t[0] === languageId.toUpperCase())[0];
if (asset) {
return this.getAsset(asset)
return this.getAsset(asset[1])
.then(asText)
.then(JSON.parse);
}
@@ -779,7 +783,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => {
if (galleryExtensions.length) {
if (compatible) {
return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine) ? v : null)))
return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.version) ? v : null)))
.then(versions => versions
.filter(v => !!v)
.map(v => ({ version: v!.version, date: v!.lastUpdated })));
@@ -816,21 +820,26 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
const message = getErrorMessage(err);
/* __GDPR__
"galleryService:requestError" : {
"url" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('galleryService:requestError', { url, cdn: true, message });
/* __GDPR__
"galleryService:cdnFallback" : {
"url" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"message": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('galleryService:cdnFallback', { url, message });
type GalleryServiceRequestErrorClassification = {
url: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
cdn: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
message: { classification: 'CallstackOrException', purpose: 'FeatureInsight' };
};
type GalleryServiceRequestErrorEvent = {
url: string;
cdn: boolean;
message: string;
};
this.telemetryService.publicLog2<GalleryServiceRequestErrorEvent, GalleryServiceRequestErrorClassification>('galleryService:requestError', { url, cdn: true, message });
type GalleryServiceCDNFallbackClassification = {
url: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
message: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
type GalleryServiceCDNFallbackEvent = {
url: string;
message: string;
};
this.telemetryService.publicLog2<GalleryServiceCDNFallbackEvent, GalleryServiceCDNFallbackClassification>('galleryService:cdnFallback', { url, message });
const fallbackOptions = assign({}, options, { url: fallbackUrl });
return this.requestService.request(fallbackOptions, token).then(undefined, err => {
@@ -839,14 +848,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
const message = getErrorMessage(err);
/* __GDPR__
"galleryService:requestError" : {
"url" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('galleryService:requestError', { url: fallbackUrl, cdn: false, message });
this.telemetryService.publicLog2<GalleryServiceRequestErrorEvent, GalleryServiceRequestErrorClassification>('galleryService:requestError', { url: fallbackUrl, cdn: false, message });
return Promise.reject(err);
});
});
@@ -867,7 +869,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (!engine) {
return null;
}
if (isEngineValid(engine)) {
if (isEngineValid(engine, this.productService.version)) {
return Promise.resolve(version);
}
}
@@ -899,7 +901,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const version = versions[0];
return this.getEngine(version)
.then(engine => {
if (!isEngineValid(engine)) {
if (!isEngineValid(engine, this.productService.version)) {
return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1));
}
@@ -940,24 +942,30 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
}
export function resolveMarketplaceHeaders(environmentService: IEnvironmentService): Promise<{ [key: string]: string; }> {
const marketplaceMachineIdFile = path.join(environmentService.userDataPath, 'machineid');
export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService): Promise<{ [key: string]: string; }> {
const marketplaceMachineIdFile = joinPath(URI.file(environmentService.userDataPath), 'machineid');
return readFile(marketplaceMachineIdFile, 'utf8')
.then<string | null>(contents => isUUID(contents) ? contents : null, () => null /* error reading ID file */)
.then(uuid => {
if (!uuid) {
uuid = generateUuid();
try {
writeFileSync(marketplaceMachineIdFile, uuid);
} catch (error) {
//noop
}
}
return {
'X-Market-Client-Id': `VSCode ${pkg.version}`,
'User-Agent': `VSCode ${pkg.version}`,
'X-Market-User-Id': uuid
};
});
}
let uuid: string | null = null;
try {
const contents = await fileService.readFile(marketplaceMachineIdFile);
const value = contents.value.toString();
uuid = isUUID(value) ? value : null;
} catch (e) {
uuid = null;
}
if (!uuid) {
uuid = generateUuid();
try {
await fileService.writeFile(marketplaceMachineIdFile, VSBuffer.fromString(uuid));
} catch (error) {
//noop
}
}
return {
'X-Market-Client-Id': `VSCode ${version}`,
'User-Agent': `VSCode ${version}`,
'X-Market-User-Id': uuid
};
}

View File

@@ -39,7 +39,7 @@ export interface IGalleryExtensionAssets {
// {{SQL CARBON EDIT}}
downloadPage?: IGalleryExtensionAsset;
icon: IGalleryExtensionAsset;
coreTranslations: { [languageId: string]: IGalleryExtensionAsset };
coreTranslations: [string, IGalleryExtensionAsset][];
}
export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier {
@@ -155,7 +155,7 @@ export interface IExtensionGalleryService {
isEnabled(): boolean;
query(token: CancellationToken): Promise<IPager<IGalleryExtension>>;
query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>;
download(extension: IGalleryExtension, operation: InstallOperation): Promise<string>;
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<URI>;
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>;
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest | null>;
@@ -200,8 +200,8 @@ export interface IExtensionManagementService {
zip(extension: ILocalExtension): Promise<URI>;
unzip(zipLocation: URI, type: ExtensionType): Promise<IExtensionIdentifier>;
install(vsix: URI): Promise<IExtensionIdentifier>;
installFromGallery(extension: IGalleryExtension): Promise<void>;
install(vsix: URI): Promise<ILocalExtension>;
installFromGallery(extension: IGalleryExtension): Promise<ILocalExtension>;
uninstall(extension: ILocalExtension, force?: boolean): Promise<void>;
reinstallFromGallery(extension: ILocalExtension): Promise<void>;
getInstalled(type?: ExtensionType): Promise<ILocalExtension[]>;

View File

@@ -5,6 +5,7 @@
import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { compareIgnoreCase } from 'vs/base/common/strings';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean {
if (a.uuid && b.uuid) {
@@ -16,6 +17,24 @@ export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifi
return compareIgnoreCase(a.id, b.id) === 0;
}
export class ExtensionIdentifierWithVersion {
constructor(
readonly identifier: IExtensionIdentifier,
readonly version: string
) { }
key(): string {
return `${this.identifier.id}-${this.version}`;
}
equals(o: any): boolean {
if (!(o instanceof ExtensionIdentifierWithVersion)) {
return false;
}
return areSameExtensions(this.identifier, o.identifier) && this.version === o.version;
}
}
export function adoptToGalleryExtensionId(id: string): string {
return id.toLocaleLowerCase();
}
@@ -89,7 +108,7 @@ export function getGalleryExtensionTelemetryData(extension: IGalleryExtension):
};
}
export const BetterMergeId = 'pprice.better-merge';
export const BetterMergeId = new ExtensionIdentifier('pprice.better-merge');
export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set<string> {
const result = new Set<string>();

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Event } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
@@ -78,7 +78,9 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
_serviceBrand: any;
constructor(private channel: IChannel) { }
constructor(
private readonly channel: IChannel,
) { }
get onInstallExtension(): Event<InstallExtensionEvent> { return this.channel.listen('onInstallExtension'); }
get onDidInstallExtension(): Event<DidInstallExtensionEvent> { return Event.map(this.channel.listen<DidInstallExtensionEvent>('onDidInstallExtension'), i => ({ ...i, local: i.local ? transformIncomingExtension(i.local, null) : i.local })); }
@@ -93,12 +95,12 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
return Promise.resolve(this.channel.call('unzip', [zipLocation, type]));
}
install(vsix: URI): Promise<IExtensionIdentifier> {
return Promise.resolve(this.channel.call('install', [vsix]));
install(vsix: URI): Promise<ILocalExtension> {
return Promise.resolve(this.channel.call<ILocalExtension>('install', [vsix])).then(local => transformIncomingExtension(local, null));
}
installFromGallery(extension: IGalleryExtension): Promise<void> {
return Promise.resolve(this.channel.call('installFromGallery', [extension]));
installFromGallery(extension: IGalleryExtension): Promise<ILocalExtension> {
return Promise.resolve(this.channel.call<ILocalExtension>('installFromGallery', [extension])).then(local => transformIncomingExtension(local, null));
}
uninstall(extension: ILocalExtension, force = false): Promise<void> {

View File

@@ -21,7 +21,7 @@ import {
INSTALL_ERROR_MALICIOUS,
INSTALL_ERROR_INCOMPATIBLE
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localizeManifest } from '../common/extensionNls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { Limiter, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async';
@@ -35,7 +35,7 @@ import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/ex
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';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { tmpdir } from 'os';
import { generateUuid } from 'vs/base/common/uuid';
import { IDownloadService } from 'vs/platform/download/common/download';
@@ -44,7 +44,7 @@ import { Schemas } from 'vs/base/common/network';
import { CancellationToken } from 'vs/base/common/cancellation';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions';
import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions';
// {{SQL CARBON EDIT}
import product from 'vs/platform/product/node/product';
@@ -112,21 +112,21 @@ export class ExtensionManagementService extends Disposable implements IExtension
private uninstalledFileLimiter: Queue<any>;
private reportedExtensions: Promise<IReportedExtension[]> | undefined;
private lastReportTimestamp = 0;
private readonly installingExtensions: Map<string, CancelablePromise<void>> = new Map<string, CancelablePromise<void>>();
private readonly installingExtensions: Map<string, CancelablePromise<ILocalExtension>> = new Map<string, CancelablePromise<ILocalExtension>>();
private readonly uninstallingExtensions: Map<string, CancelablePromise<void>> = new Map<string, CancelablePromise<void>>();
private readonly manifestCache: ExtensionsManifestCache;
private readonly extensionLifecycle: ExtensionsLifecycle;
private readonly _onInstallExtension = new Emitter<InstallExtensionEvent>();
private readonly _onInstallExtension = this._register(new Emitter<InstallExtensionEvent>());
readonly onInstallExtension: Event<InstallExtensionEvent> = this._onInstallExtension.event;
private readonly _onDidInstallExtension = new Emitter<DidInstallExtensionEvent>();
private readonly _onDidInstallExtension = this._register(new Emitter<DidInstallExtensionEvent>());
readonly onDidInstallExtension: Event<DidInstallExtensionEvent> = this._onDidInstallExtension.event;
private readonly _onUninstallExtension = new Emitter<IExtensionIdentifier>();
private readonly _onUninstallExtension = this._register(new Emitter<IExtensionIdentifier>());
readonly onUninstallExtension: Event<IExtensionIdentifier> = this._onUninstallExtension.event;
private _onDidUninstallExtension = new Emitter<DidUninstallExtensionEvent>();
private _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
onDidUninstallExtension: Event<DidUninstallExtensionEvent> = this._onDidUninstallExtension.event;
constructor(
@@ -161,7 +161,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
unzip(zipLocation: URI, type: ExtensionType): Promise<IExtensionIdentifier> {
this.logService.trace('ExtensionManagementService#unzip', zipLocation.toString());
return this.install(zipLocation, type);
return this.install(zipLocation, type).then(local => local.identifier);
}
private collectFiles(extension: ILocalExtension): Promise<IFile[]> {
@@ -190,7 +190,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
install(vsix: URI, type: ExtensionType = ExtensionType.User): Promise<IExtensionIdentifier> {
install(vsix: URI, type: ExtensionType = ExtensionType.User): Promise<ILocalExtension> {
// {{SQL CARBON EDIT}}
let startTime = new Date().getTime();
@@ -232,20 +232,20 @@ export class ExtensionManagementService extends Disposable implements IExtension
local => {
this.reportTelemetry(this.getTelemetryEvent(InstallOperation.Install), getLocalExtensionTelemetryData(local), new Date().getTime() - startTime, void 0);
this._onDidInstallExtension.fire({ identifier, zipPath, local, operation: InstallOperation.Install });
return identifier;
return local;
},
error => { this._onDidInstallExtension.fire({ identifier, zipPath, error, operation: InstallOperation.Install }); return Promise.reject(error); }
);
// return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name))
// .then(
// metadata => this.installFromZipPath(identifierWithVersion, zipPath, metadata, type, operation, token),
// () => this.installFromZipPath(identifierWithVersion, zipPath, null, type, operation, token))
// .then(
// () => { this.logService.info('Successfully installed the extension:', identifier.id); return identifier; },
// e => {
// this.logService.error('Failed to install the extension:', identifier.id, e.message);
// return Promise.reject(e);
// });
// () => this.installFromZipPath(identifierWithVersion, zipPath, null, type, operation, token))
// .then(
// local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; },
// e => {
// this.logService.error('Failed to install the extension:', identifier.id, e.message);
// return Promise.reject(e);
// });
// {{SQL CARBON EDIT}} - End
});
});
@@ -284,12 +284,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
));
}
async installFromGallery(extension: IGalleryExtension): Promise<void> {
async installFromGallery(extension: IGalleryExtension): Promise<ILocalExtension> {
if (!this.galleryService.isEnabled()) {
return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")));
}
const startTime = new Date().getTime();
this.logService.info('Installing extension:', extension.identifier.id);
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: extension.identifier, gallery: extension, local, operation });
@@ -317,8 +317,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
let cancellablePromise = this.installingExtensions.get(key);
if (!cancellablePromise) {
this.logService.info('Installing extension:', extension.identifier.id);
this._onInstallExtension.fire({ identifier: extension.identifier, gallery: extension });
let operation: InstallOperation = InstallOperation.Install;
let cancellationToken: CancellationToken, successCallback: (a?: any) => void, errorCallback: (e?: any) => any | null;
let cancellationToken: CancellationToken, successCallback: (local: ILocalExtension) => void, errorCallback: (e?: any) => any | null;
cancellablePromise = createCancelablePromise(token => { cancellationToken = token; return new Promise((c, e) => { successCallback = c; errorCallback = e; }); });
this.installingExtensions.set(key, cancellablePromise);
try {
@@ -340,7 +343,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
this.installingExtensions.delete(key);
onDidInstallExtensionSuccess(extension, operation, local);
successCallback(null);
successCallback(local);
},
error => {
this.installingExtensions.delete(key);
@@ -367,7 +370,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
const compatibleExtension = await this.galleryService.getCompatibleExtension(extension);
if (!compatibleExtension) {
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 Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
}
return compatibleExtension;
@@ -408,9 +411,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
};
this.logService.trace('Started downloading extension:', extension.identifier.id);
return this.galleryService.download(extension, operation)
return this.galleryService.download(extension, URI.file(tmpdir()), operation)
.then(
zipPath => {
zip => {
const zipPath = zip.fsPath;
this.logService.info('Downloaded extension:', extension.identifier.id, zipPath);
return getManifest(zipPath)
.then(
@@ -470,7 +474,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
this.logService.info('Installation completed.', identifier.id);
if (metadata) {
local.metadata = metadata;
this.setMetadata(local, metadata);
return this.saveMetadataForLocalExtension(local);
}
return local;
@@ -500,12 +504,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
}
private rename(identfier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise<void> {
private rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise<void> {
return pfs.rename(extractPath, renamePath)
.then(undefined, error => {
if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) {
this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identfier.id);
return this.rename(identfier, extractPath, renamePath, retryUntil);
this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id);
return this.rename(identifier, extractPath, renamePath, retryUntil);
}
return Promise.reject(new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING));
});
@@ -743,7 +747,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.logService.trace('Started scanning system extensions');
const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System)
.then(result => {
this.logService.info('Scanned system extensions:', result.length);
this.logService.trace('Scanned system extensions:', result.length);
return result;
});
if (this.environmentService.isBuilt) {
@@ -756,7 +760,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
if (devSystemExtensionsList.length) {
return this.scanExtensions(this.devSystemExtensionsPath, ExtensionType.System)
.then(result => {
this.logService.info('Scanned dev system extensions:', result.length);
this.logService.trace('Scanned dev system extensions:', result.length);
return result.filter(r => devSystemExtensionsList.some(id => areSameExtensions(r.identifier, { id })));
});
} else {
@@ -776,7 +780,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier);
extensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]);
}
this.logService.info('Scanned user extensions:', extensions.length);
this.logService.trace('Scanned user extensions:', extensions.length);
return extensions;
});
}
@@ -800,25 +804,44 @@ export class ExtensionManagementService extends Disposable implements IExtension
const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)) : null;
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : null;
const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: metadata ? metadata.id : null };
return <ILocalExtension>{ type, identifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl };
const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
const local = <ILocalExtension>{ type, identifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl };
if (metadata) {
this.setMetadata(local, metadata);
}
return local;
}))
.then(undefined, () => null);
}
removeDeprecatedExtensions(): Promise<any> {
return this.removeUninstalledExtensions()
.then(() => this.removeOutdatedExtensions());
private setMetadata(local: ILocalExtension, metadata: IGalleryMetadata): void {
local.metadata = metadata;
local.identifier.uuid = metadata.id;
}
private removeUninstalledExtensions(): Promise<void> {
return this.getUninstalledExtensions()
.then(uninstalled => this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
.then(extensions => {
const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]);
return Promise.all(toRemove.map(e => this.extensionLifecycle.postUninstall(e).then(() => this.removeUninstalledExtension(e))));
})
).then(() => undefined);
async removeDeprecatedExtensions(): Promise<void> {
await this.removeUninstalledExtensions();
await this.removeOutdatedExtensions();
}
private async removeUninstalledExtensions(): Promise<void> {
const uninstalled = await this.getUninstalledExtensions();
const extensions = await this.scanExtensions(this.extensionsPath, ExtensionType.User); // All user extensions
const installed: Set<string> = new Set<string>();
for (const e of extensions) {
if (!uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]) {
installed.add(e.identifier.id.toLowerCase());
}
}
const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier);
await Promise.all(byExtension.map(async e => {
const latest = e.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0];
if (!installed.has(latest.identifier.id.toLowerCase())) {
await this.extensionLifecycle.postUninstall(latest);
}
}));
const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]);
await Promise.all(toRemove.map(e => this.removeUninstalledExtension(e)));
}
private removeOutdatedExtensions(): Promise<void> {
@@ -845,8 +868,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath));
}
private isUninstalled(identfier: ExtensionIdentifierWithVersion): Promise<boolean> {
return this.filterUninstalled(identfier).then(uninstalled => uninstalled.length === 1);
private isUninstalled(identifier: ExtensionIdentifierWithVersion): Promise<boolean> {
return this.filterUninstalled(identifier).then(uninstalled => uninstalled.length === 1);
}
private filterUninstalled(...identifiers: ExtensionIdentifierWithVersion[]): Promise<string[]> {

View File

@@ -16,11 +16,11 @@ export class ExtensionsManifestCache extends Disposable {
constructor(
private readonly environmentService: IEnvironmentService,
extensionsManagementServuce: IExtensionManagementService
extensionsManagementService: IExtensionManagementService
) {
super();
this._register(extensionsManagementServuce.onDidInstallExtension(e => this.onDidInstallExtension(e)));
this._register(extensionsManagementServuce.onDidUninstallExtension(e => this.onDidUnInstallExtension(e)));
this._register(extensionsManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
this._register(extensionsManagementService.onDidUninstallExtension(e => this.onDidUnInstallExtension(e)));
}
private onDidInstallExtension(e: DidInstallExtensionEvent): void {

View File

@@ -11,15 +11,32 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { join } from 'vs/base/common/path';
import { mkdirp, RimRafMode, rimraf } from 'vs/base/node/pfs';
// {{SQL CARBON EDIT}}
import { resolveMarketplaceHeaders, ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { resolveMarketplaceHeaders, ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { isUUID } from 'vs/base/common/uuid';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { NullLogService } from 'vs/platform/log/common/log';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import pkg from 'vs/platform/product/node/package';
suite('Extension Gallery Service', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'extensiongalleryservice');
const marketplaceHome = join(parentDir, 'Marketplace');
let fileService: IFileService;
let disposables: DisposableStore;
setup(done => {
disposables = new DisposableStore();
fileService = new FileService(new NullLogService());
disposables.add(fileService);
const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
disposables.add(diskFileSystemProvider);
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
// Delete any existing backups completely and then re-create it.
rimraf(marketplaceHome, RimRafMode.MOVE).then(() => {
mkdirp(marketplaceHome).then(() => {
@@ -29,6 +46,7 @@ suite('Extension Gallery Service', () => {
});
teardown(done => {
disposables.clear();
rimraf(marketplaceHome, RimRafMode.MOVE).then(done, done);
});
@@ -36,10 +54,10 @@ suite('Extension Gallery Service', () => {
const args = ['--user-data-dir', marketplaceHome];
const environmentService = new EnvironmentService(parseArgs(args), process.execPath);
return resolveMarketplaceHeaders(environmentService).then(headers => {
return resolveMarketplaceHeaders(pkg.version, environmentService, fileService).then(headers => {
assert.ok(isUUID(headers['X-Market-User-Id']));
return resolveMarketplaceHeaders(environmentService).then(headers2 => {
return resolveMarketplaceHeaders(pkg.version, environmentService, fileService).then(headers2 => {
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
});
});