mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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[]>;
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user