VS Code merge to df8fe74bd55313de0dd2303bc47a4aab0ca56b0e (#17979)

* Merge from vscode 504f934659740e9d41501cad9f162b54d7745ad9

* delete unused folders

* distro

* Bump build node version

* update chokidar

* FIx hygiene errors

* distro

* Fix extension lint issues

* Remove strict-vscode

* Add copyright header exemptions

* Bump vscode-extension-telemetry to fix webpacking issue with zone.js

* distro

* Fix failing tests (revert marked.js back to current one until we decide to update)

* Skip searchmodel test

* Fix mac build

* temp debug script loading

* Try disabling coverage

* log error too

* Revert "log error too"

This reverts commit af0183e5d4ab458fdf44b88fbfab9908d090526f.

* Revert "temp debug script loading"

This reverts commit 3d687d541c76db2c5b55626c78ae448d3c25089c.

* Add comments explaining coverage disabling

* Fix ansi_up loading issue

* Merge latest from ads

* Use newer option

* Fix compile

* add debug logging warn

* Always log stack

* log more

* undo debug

* Update to use correct base path (+cleanup)

* distro

* fix compile errors

* Remove strict-vscode

* Fix sql editors not showing

* Show db dropdown input & fix styling

* Fix more info in gallery

* Fix gallery asset requests

* Delete unused workflow

* Fix tapable resolutions for smoke test compile error

* Fix smoke compile

* Disable crash reporting

* Disable interactive

Co-authored-by: ADS Merger <karlb@microsoft.com>
This commit is contained in:
Charles Gagnon
2022-01-06 09:06:56 -08:00
committed by GitHub
parent fd2736b6a6
commit 2bc6a0cd01
2099 changed files with 79520 additions and 43813 deletions

View File

@@ -0,0 +1,654 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Barrier, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { canceled, getErrorMessage } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { isWeb } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import {
DidUninstallExtensionEvent, ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOperation, InstallOptions,
InstallVSIXOptions, INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, IReportedExtension, StatisticType, UninstallOptions
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionIdentifierWithVersion, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import product from 'vs/platform/product/common/product';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export const INSTALL_ERROR_VALIDATING = 'validating';
export const ERROR_UNKNOWN = 'unknown';
export const INSTALL_ERROR_LOCAL = 'local';
export interface IInstallExtensionTask {
readonly identifier: IExtensionIdentifier;
readonly source: IGalleryExtension | URI;
readonly operation: InstallOperation;
run(): Promise<ILocalExtension>;
waitUntilTaskIsFinished(): Promise<ILocalExtension>;
cancel(): void;
}
export type UninstallExtensionTaskOptions = { readonly remove?: boolean; readonly versionOnly?: boolean };
export interface IUninstallExtensionTask {
readonly extension: ILocalExtension;
run(): Promise<void>;
waitUntilTaskIsFinished(): Promise<void>;
cancel(): void;
}
export abstract class AbstractExtensionManagementService extends Disposable implements IExtensionManagementService {
declare readonly _serviceBrand: undefined;
private reportedExtensions: Promise<IReportedExtension[]> | undefined;
private lastReportTimestamp = 0;
private readonly installingExtensions = new Map<string, IInstallExtensionTask>();
private readonly uninstallingExtensions = new Map<string, IUninstallExtensionTask>();
private readonly _onInstallExtension = this._register(new Emitter<InstallExtensionEvent>());
readonly onInstallExtension: Event<InstallExtensionEvent> = this._onInstallExtension.event;
protected readonly _onDidInstallExtensions = this._register(new Emitter<InstallExtensionResult[]>());
readonly onDidInstallExtensions = this._onDidInstallExtensions.event;
protected readonly _onUninstallExtension = this._register(new Emitter<IExtensionIdentifier>());
readonly onUninstallExtension: Event<IExtensionIdentifier> = this._onUninstallExtension.event;
protected _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
onDidUninstallExtension: Event<DidUninstallExtensionEvent> = this._onDidUninstallExtension.event;
private readonly participants: IExtensionManagementParticipant[] = [];
constructor(
@IExtensionGalleryService protected readonly galleryService: IExtensionGalleryService,
@ITelemetryService protected readonly telemetryService: ITelemetryService,
@ILogService protected readonly logService: ILogService,
) {
super();
this._register(toDisposable(() => {
this.installingExtensions.forEach(task => task.cancel());
this.uninstallingExtensions.forEach(promise => promise.cancel());
this.installingExtensions.clear();
this.uninstallingExtensions.clear();
}));
}
async installFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise<ILocalExtension> {
if (!this.galleryService.isEnabled()) {
throw new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"));
}
try {
extension = await this.checkAndGetCompatibleVersion(extension);
} catch (error) {
this.logService.error(getErrorMessage(error));
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
throw error;
}
if (!await this.canInstall(extension)) {
const error = new ExtensionManagementError(`Not supported`, INSTALL_ERROR_VALIDATING);
this.logService.error(`Canno install extension as it is not supported.`, extension.identifier.id, error.message);
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
throw error;
}
const manifest = await this.galleryService.getManifest(extension, CancellationToken.None);
if (manifest === null) {
const error = new ExtensionManagementError(`Missing manifest for extension ${extension.identifier.id}`, INSTALL_ERROR_VALIDATING);
this.logService.error(`Failed to install extension:`, extension.identifier.id, error.message);
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
throw error;
}
return this.installExtension(manifest, extension, options);
}
async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise<void> {
this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id);
return this.unininstallExtension(extension, options);
}
async reinstallFromGallery(extension: ILocalExtension): Promise<void> {
this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.identifier.id);
if (!this.galleryService.isEnabled()) {
throw new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"));
}
const galleryExtension = await this.findGalleryExtension(extension);
if (!galleryExtension) {
throw new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled"));
}
await this.createUninstallExtensionTask(extension, { remove: true, versionOnly: true }).run();
await this.installFromGallery(galleryExtension);
}
getExtensionsReport(): Promise<IReportedExtension[]> {
const now = new Date().getTime();
if (!this.reportedExtensions || now - this.lastReportTimestamp > 1000 * 60 * 5) { // 5 minute cache freshness
this.reportedExtensions = this.updateReportCache();
this.lastReportTimestamp = now;
}
return this.reportedExtensions;
}
registerParticipant(participant: IExtensionManagementParticipant): void {
this.participants.push(participant);
}
protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): Promise<ILocalExtension> {
// only cache gallery extensions tasks
if (!URI.isUri(extension)) {
let installExtensionTask = this.installingExtensions.get(new ExtensionIdentifierWithVersion(extension.identifier, extension.version).key());
if (installExtensionTask) {
this.logService.info('Extensions is already requested to install', extension.identifier.id);
return installExtensionTask.waitUntilTaskIsFinished();
}
options = { ...options, installOnlyNewlyAddedFromExtensionPack: true /* always true for gallery extensions */ };
}
const allInstallExtensionTasks: { task: IInstallExtensionTask, manifest: IExtensionManifest }[] = [];
const installResults: (InstallExtensionResult & { local: ILocalExtension })[] = [];
const installExtensionTask = this.createInstallExtensionTask(manifest, extension, options);
if (!URI.isUri(extension)) {
this.installingExtensions.set(new ExtensionIdentifierWithVersion(installExtensionTask.identifier, manifest.version).key(), installExtensionTask);
}
this._onInstallExtension.fire({ identifier: installExtensionTask.identifier, source: extension });
this.logService.info('Installing extension:', installExtensionTask.identifier.id);
allInstallExtensionTasks.push({ task: installExtensionTask, manifest });
let installExtensionHasDependents: boolean = false;
try {
if (options.donotIncludePackAndDependencies) {
this.logService.info('Installing the extension without checking dependencies and pack', installExtensionTask.identifier.id);
} else {
try {
const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensionsToInstall(installExtensionTask.identifier, manifest, !!options.installOnlyNewlyAddedFromExtensionPack);
for (const { gallery, manifest } of allDepsAndPackExtensionsToInstall) {
installExtensionHasDependents = installExtensionHasDependents || !!manifest.extensionDependencies?.some(id => areSameExtensions({ id }, installExtensionTask.identifier));
if (this.installingExtensions.has(new ExtensionIdentifierWithVersion(gallery.identifier, gallery.version).key())) {
this.logService.info('Extension is already requested to install', gallery.identifier.id);
} else {
const task = this.createInstallExtensionTask(manifest, gallery, { ...options, donotIncludePackAndDependencies: true });
this.installingExtensions.set(new ExtensionIdentifierWithVersion(task.identifier, manifest.version).key(), task);
this._onInstallExtension.fire({ identifier: task.identifier, source: gallery });
this.logService.info('Installing extension:', task.identifier.id);
allInstallExtensionTasks.push({ task, manifest });
}
}
} catch (error) {
this.logService.error('Error while preparing to install dependencies and extension packs of the extension:', installExtensionTask.identifier.id);
this.logService.error(error);
throw error;
}
}
const extensionsToInstallMap = allInstallExtensionTasks.reduce((result, { task, manifest }) => {
result.set(task.identifier.id.toLowerCase(), { task, manifest });
return result;
}, new Map<string, { task: IInstallExtensionTask, manifest: IExtensionManifest }>());
while (extensionsToInstallMap.size) {
let extensionsToInstall;
const extensionsWithoutDepsToInstall = [...extensionsToInstallMap.values()].filter(({ manifest }) => !manifest.extensionDependencies?.some(id => extensionsToInstallMap.has(id.toLowerCase())));
if (extensionsWithoutDepsToInstall.length) {
extensionsToInstall = extensionsToInstallMap.size === 1 ? extensionsWithoutDepsToInstall
/* If the main extension has no dependents remove it and install it at the end */
: extensionsWithoutDepsToInstall.filter(({ task }) => !(task === installExtensionTask && !installExtensionHasDependents));
} else {
this.logService.info('Found extensions with circular dependencies', extensionsWithoutDepsToInstall.map(({ task }) => task.identifier.id));
extensionsToInstall = [...extensionsToInstallMap.values()];
}
// Install extensions in parallel and wait until all extensions are installed / failed
await this.joinAllSettled(extensionsToInstall.map(async ({ task }) => {
const startTime = new Date().getTime();
try {
const local = await task.run();
await this.joinAllSettled(this.participants.map(participant => participant.postInstall(local, task.source, options, CancellationToken.None)));
if (!URI.isUri(task.source)) {
reportTelemetry(this.telemetryService, task.operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', getGalleryExtensionTelemetryData(task.source), new Date().getTime() - startTime, undefined);
// In web, report extension install statistics explicitly. In Desktop, statistics are automatically updated while downloading the VSIX.
if (isWeb && task.operation === InstallOperation.Install) {
try {
await this.galleryService.reportStatistic(local.manifest.publisher, local.manifest.name, local.manifest.version, StatisticType.Install);
} catch (error) { /* ignore */ }
}
}
installResults.push({ local, identifier: task.identifier, operation: task.operation, source: task.source });
} catch (error) {
if (!URI.isUri(task.source)) {
reportTelemetry(this.telemetryService, task.operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', getGalleryExtensionTelemetryData(task.source), new Date().getTime() - startTime, error);
}
this.logService.error('Error while installing the extension:', task.identifier.id);
this.logService.error(error);
throw error;
} finally { extensionsToInstallMap.delete(task.identifier.id.toLowerCase()); }
}));
}
installResults.forEach(({ identifier }) => this.logService.info(`Extension installed successfully:`, identifier.id));
this._onDidInstallExtensions.fire(installResults);
return installResults.filter(({ identifier }) => areSameExtensions(identifier, installExtensionTask.identifier))[0].local;
} catch (error) {
// cancel all tasks
allInstallExtensionTasks.forEach(({ task }) => task.cancel());
// rollback installed extensions
if (installResults.length) {
try {
const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true }).run()));
for (let index = 0; index < result.length; index++) {
const r = result[index];
const { identifier } = installResults[index];
if (r.status === 'fulfilled') {
this.logService.info('Rollback: Uninstalled extension', identifier.id);
} else {
this.logService.warn('Rollback: Error while uninstalling extension', identifier.id, getErrorMessage(r.reason));
}
}
} catch (error) {
// ignore error
this.logService.warn('Error while rolling back extensions', getErrorMessage(error), installResults.map(({ identifier }) => identifier.id));
}
}
this.logService.error(`Failed to install extension:`, installExtensionTask.identifier.id, getErrorMessage(error));
this._onDidInstallExtensions.fire(allInstallExtensionTasks.map(({ task }) => ({ identifier: task.identifier, operation: InstallOperation.Install, source: task.source })));
if (error instanceof Error) {
error.name = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
}
throw error;
} finally {
/* Remove the gallery tasks from the cache */
for (const { task, manifest } of allInstallExtensionTasks) {
if (!URI.isUri(task.source)) {
const key = new ExtensionIdentifierWithVersion(task.identifier, manifest.version).key();
if (!this.installingExtensions.delete(key)) {
this.logService.warn('Installation task is not found in the cache', key);
}
}
}
}
}
private async joinAllSettled<T>(promises: Promise<T>[]): Promise<T[]> {
const results: T[] = [];
const errors: any[] = [];
const promiseResults = await Promise.allSettled(promises);
for (const r of promiseResults) {
if (r.status === 'fulfilled') {
results.push(r.value);
} else {
errors.push(r.reason);
}
}
// If there are errors, throw the error.
if (errors.length) { throw joinErrors(errors); }
return results;
}
private async getAllDepsAndPackExtensionsToInstall(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean): Promise<{ gallery: IGalleryExtension, manifest: IExtensionManifest }[]> {
if (!this.galleryService.isEnabled()) {
return [];
}
let installed = await this.getInstalled();
const knownIdentifiers = [extensionIdentifier, ...(installed).map(i => i.identifier)];
const allDependenciesAndPacks: { gallery: IGalleryExtension, manifest: IExtensionManifest }[] = [];
const collectDependenciesAndPackExtensionsToInstall = async (extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest): Promise<void> => {
const dependenciesAndPackExtensions: string[] = manifest.extensionDependencies || [];
if (manifest.extensionPack) {
const existing = getOnlyNewlyAddedFromExtensionPack ? installed.find(e => areSameExtensions(e.identifier, extensionIdentifier)) : undefined;
for (const extension of manifest.extensionPack) {
// add only those extensions which are new in currently installed extension
if (!(existing && existing.manifest.extensionPack && existing.manifest.extensionPack.some(old => areSameExtensions({ id: old }, { id: extension })))) {
if (dependenciesAndPackExtensions.every(e => !areSameExtensions({ id: e }, { id: extension }))) {
dependenciesAndPackExtensions.push(extension);
}
}
}
}
if (dependenciesAndPackExtensions.length) {
// filter out installed and known extensions
const identifiers = [...knownIdentifiers, ...allDependenciesAndPacks.map(r => r.gallery.identifier)];
const names = dependenciesAndPackExtensions.filter(id => identifiers.every(galleryIdentifier => !areSameExtensions(galleryIdentifier, { id })));
if (names.length) {
const galleryResult = await this.galleryService.query({ names, pageSize: dependenciesAndPackExtensions.length }, CancellationToken.None);
for (const galleryExtension of galleryResult.firstPage) {
if (identifiers.find(identifier => areSameExtensions(identifier, galleryExtension.identifier))) {
continue;
}
const compatibleExtension = await this.checkAndGetCompatibleVersion(galleryExtension);
if (!await this.canInstall(compatibleExtension)) {
this.logService.info('Skipping the extension as it cannot be installed', compatibleExtension.identifier.id);
continue;
}
const manifest = await this.galleryService.getManifest(compatibleExtension, CancellationToken.None);
if (manifest === null) {
throw new ExtensionManagementError(`Missing manifest for extension ${compatibleExtension.identifier.id}`, INSTALL_ERROR_VALIDATING);
}
allDependenciesAndPacks.push({ gallery: compatibleExtension, manifest });
await collectDependenciesAndPackExtensionsToInstall(compatibleExtension.identifier, manifest);
}
}
}
};
await collectDependenciesAndPackExtensionsToInstall(extensionIdentifier, manifest);
installed = await this.getInstalled();
return allDependenciesAndPacks.filter(e => !installed.some(i => areSameExtensions(i.identifier, e.gallery.identifier)));
}
private async checkAndGetCompatibleVersion(extension: IGalleryExtension): Promise<IGalleryExtension> {
if (await this.isMalicious(extension)) {
throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), INSTALL_ERROR_MALICIOUS);
}
const compatibleExtension = await this.galleryService.getCompatibleExtension(extension);
if (!compatibleExtension) {
throw new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Can't install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, product.version), INSTALL_ERROR_INCOMPATIBLE);
}
return compatibleExtension;
}
private async isMalicious(extension: IGalleryExtension): Promise<boolean> {
const report = await this.getExtensionsReport();
return getMaliciousExtensionsSet(report).has(extension.identifier.id);
}
private async unininstallExtension(extension: ILocalExtension, options: UninstallOptions): Promise<void> {
const uninstallExtensionTask = this.uninstallingExtensions.get(extension.identifier.id.toLowerCase());
if (uninstallExtensionTask) {
this.logService.info('Extensions is already requested to uninstall', extension.identifier.id);
return uninstallExtensionTask.waitUntilTaskIsFinished();
}
const createUninstallExtensionTask = (extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask => {
const uninstallExtensionTask = this.createUninstallExtensionTask(extension, options);
this.uninstallingExtensions.set(uninstallExtensionTask.extension.identifier.id.toLowerCase(), uninstallExtensionTask);
this.logService.info('Uninstalling extension:', extension.identifier.id);
this._onUninstallExtension.fire(extension.identifier);
return uninstallExtensionTask;
};
const postUninstallExtension = (extension: ILocalExtension, error?: ExtensionManagementError): void => {
if (error) {
this.logService.error('Failed to uninstall extension:', extension.identifier.id, error.message);
} else {
this.logService.info('Successfully uninstalled extension:', extension.identifier.id);
}
reportTelemetry(this.telemetryService, 'extensionGallery:uninstall', getLocalExtensionTelemetryData(extension), undefined, error);
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error: error?.code });
};
const allTasks: IUninstallExtensionTask[] = [];
const processedTasks: IUninstallExtensionTask[] = [];
try {
allTasks.push(createUninstallExtensionTask(extension, {}));
const installed = await this.getInstalled(ExtensionType.User);
if (options.donotIncludePack) {
this.logService.info('Uninstalling the extension without including packed extension', extension.identifier.id);
} else {
const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed);
for (const packedExtension of packedExtensions) {
if (this.uninstallingExtensions.has(packedExtension.identifier.id.toLowerCase())) {
this.logService.info('Extensions is already requested to uninstall', packedExtension.identifier.id);
} else {
allTasks.push(createUninstallExtensionTask(packedExtension, {}));
}
}
}
if (options.donotCheckDependents) {
this.logService.info('Uninstalling the extension without checking dependents', extension.identifier.id);
} else {
this.checkForDependents(allTasks.map(task => task.extension), installed, extension);
}
// Uninstall extensions in parallel and wait until all extensions are uninstalled / failed
await this.joinAllSettled(allTasks.map(async task => {
try {
await task.run();
await this.joinAllSettled(this.participants.map(participant => participant.postUninstall(task.extension, options, CancellationToken.None)));
// only report if extension has a mapped gallery extension. UUID identifies the gallery extension.
if (task.extension.identifier.uuid) {
try {
await this.galleryService.reportStatistic(task.extension.manifest.publisher, task.extension.manifest.name, task.extension.manifest.version, StatisticType.Uninstall);
} catch (error) { /* ignore */ }
}
postUninstallExtension(task.extension);
} catch (e) {
const error = e instanceof ExtensionManagementError ? e : new ExtensionManagementError(getErrorMessage(e), ERROR_UNKNOWN);
postUninstallExtension(task.extension, error);
throw error;
} finally {
processedTasks.push(task);
}
}));
} catch (e) {
const error = e instanceof ExtensionManagementError ? e : new ExtensionManagementError(getErrorMessage(e), ERROR_UNKNOWN);
for (const task of allTasks) {
// cancel the tasks
try { task.cancel(); } catch (error) { /* ignore */ }
if (!processedTasks.includes(task)) {
postUninstallExtension(task.extension, error);
}
}
throw error;
} finally {
// Remove tasks from cache
for (const task of allTasks) {
if (!this.uninstallingExtensions.delete(task.extension.identifier.id.toLowerCase())) {
this.logService.warn('Uninstallation task is not found in the cache', task.extension.identifier.id);
}
}
}
}
private checkForDependents(extensionsToUninstall: ILocalExtension[], installed: ILocalExtension[], extensionToUninstall: ILocalExtension): void {
for (const extension of extensionsToUninstall) {
const dependents = this.getDependents(extension, installed);
if (dependents.length) {
const remainingDependents = dependents.filter(dependent => !extensionsToUninstall.some(e => areSameExtensions(e.identifier, dependent.identifier)));
if (remainingDependents.length) {
throw new Error(this.getDependentsErrorMessage(extension, remainingDependents, extensionToUninstall));
}
}
}
}
private getDependentsErrorMessage(dependingExtension: ILocalExtension, dependents: ILocalExtension[], extensionToUninstall: ILocalExtension): string {
if (extensionToUninstall === dependingExtension) {
if (dependents.length === 1) {
return nls.localize('singleDependentError', "Cannot uninstall '{0}' extension. '{1}' extension depends on this.",
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name);
}
if (dependents.length === 2) {
return nls.localize('twoDependentsError', "Cannot uninstall '{0}' extension. '{1}' and '{2}' extensions depend on this.",
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name);
}
return nls.localize('multipleDependentsError', "Cannot uninstall '{0}' extension. '{1}', '{2}' and other extension depend on this.",
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name);
}
if (dependents.length === 1) {
return nls.localize('singleIndirectDependentError', "Cannot uninstall '{0}' extension . It includes uninstalling '{1}' extension and '{2}' extension depends on this.",
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependingExtension.manifest.displayName
|| dependingExtension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name);
}
if (dependents.length === 2) {
return nls.localize('twoIndirectDependentsError', "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}' and '{3}' extensions depend on this.",
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependingExtension.manifest.displayName
|| dependingExtension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name);
}
return nls.localize('multipleIndirectDependentsError', "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}', '{3}' and other extensions depend on this.",
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependingExtension.manifest.displayName
|| dependingExtension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name);
}
private getAllPackExtensionsToUninstall(extension: ILocalExtension, installed: ILocalExtension[], checked: ILocalExtension[] = []): ILocalExtension[] {
if (checked.indexOf(extension) !== -1) {
return [];
}
checked.push(extension);
const extensionsPack = extension.manifest.extensionPack ? extension.manifest.extensionPack : [];
if (extensionsPack.length) {
const packedExtensions = installed.filter(i => !i.isBuiltin && extensionsPack.some(id => areSameExtensions({ id }, i.identifier)));
const packOfPackedExtensions: ILocalExtension[] = [];
for (const packedExtension of packedExtensions) {
packOfPackedExtensions.push(...this.getAllPackExtensionsToUninstall(packedExtension, installed, checked));
}
return [...packedExtensions, ...packOfPackedExtensions];
}
return [];
}
private getDependents(extension: ILocalExtension, installed: ILocalExtension[]): ILocalExtension[] {
return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier)));
}
private async findGalleryExtension(local: ILocalExtension): Promise<IGalleryExtension> {
if (local.identifier.uuid) {
const galleryExtension = await this.findGalleryExtensionById(local.identifier.uuid);
return galleryExtension ? galleryExtension : this.findGalleryExtensionByName(local.identifier.id);
}
return this.findGalleryExtensionByName(local.identifier.id);
}
private async findGalleryExtensionById(uuid: string): Promise<IGalleryExtension> {
const galleryResult = await this.galleryService.query({ ids: [uuid], pageSize: 1 }, CancellationToken.None);
return galleryResult.firstPage[0];
}
private async findGalleryExtensionByName(name: string): Promise<IGalleryExtension> {
const galleryResult = await this.galleryService.query({ names: [name], pageSize: 1 }, CancellationToken.None);
return galleryResult.firstPage[0];
}
private async updateReportCache(): Promise<IReportedExtension[]> {
try {
this.logService.trace('ExtensionManagementService.refreshReportedCache');
const result = await this.galleryService.getExtensionsReport();
this.logService.trace(`ExtensionManagementService.refreshReportedCache - got ${result.length} reported extensions from service`);
return result;
} catch (err) {
this.logService.trace('ExtensionManagementService.refreshReportedCache - failed to get extension report');
return [];
}
}
abstract zip(extension: ILocalExtension): Promise<URI>;
abstract unzip(zipLocation: URI): Promise<IExtensionIdentifier>;
abstract getManifest(vsix: URI): Promise<IExtensionManifest>;
abstract install(vsix: URI, options?: InstallVSIXOptions): Promise<ILocalExtension>;
abstract canInstall(extension: IGalleryExtension): Promise<boolean>;
abstract getInstalled(type?: ExtensionType): Promise<ILocalExtension[]>;
abstract updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension>;
abstract updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension>;
protected abstract createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): IInstallExtensionTask;
protected abstract createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask;
}
export function joinErrors(errorOrErrors: (Error | string) | (Array<Error | string>)): Error {
const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors];
if (errors.length === 1) {
return errors[0] instanceof Error ? <Error>errors[0] : new Error(<string>errors[0]);
}
return errors.reduce<Error>((previousValue: Error, currentValue: Error | string) => {
return new Error(`${previousValue.message}${previousValue.message ? ',' : ''}${currentValue instanceof Error ? currentValue.message : currentValue}`);
}, new Error(''));
}
export function reportTelemetry(telemetryService: ITelemetryService, eventName: string, extensionData: any, duration?: number, error?: Error): void {
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : undefined;
/* __GDPR__
"extensionGallery:install" : {
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"recommendationReason": { "retiredFromVersion": "1.23.0", "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"${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}"
]
}
*/
/* __GDPR__
"extensionGallery:update" : {
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"${include}": [
"${GalleryExtensionTelemetryData}"
]
}
*/
telemetryService.publicLogError(eventName, { ...extensionData, success: !error, duration, errorcode });
}
export abstract class AbstractExtensionTask<T> {
private readonly barrier = new Barrier();
private cancellablePromise: CancelablePromise<T> | undefined;
async waitUntilTaskIsFinished(): Promise<T> {
await this.barrier.wait();
return this.cancellablePromise!;
}
async run(): Promise<T> {
if (!this.cancellablePromise) {
this.cancellablePromise = createCancelablePromise(token => this.doRun(token));
}
this.barrier.open();
return this.cancellablePromise;
}
cancel(): void {
if (!this.cancellablePromise) {
this.cancellablePromise = createCancelablePromise(token => {
return new Promise((c, e) => {
const disposable = token.onCancellationRequested(() => {
disposable.dispose();
e(canceled());
});
});
});
this.barrier.open();
}
this.cancellablePromise.cancel();
}
protected abstract doRun(token: CancellationToken): Promise<T>;
}

View File

@@ -3,12 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtensionIdentifier, IGlobalExtensionEnablementService, DISABLED_EXTENSIONS_STORAGE_PATH } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { DISABLED_EXTENSIONS_STORAGE_PATH, IExtensionIdentifier, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
export class GlobalExtensionEnablementService extends Disposable implements IGlobalExtensionEnablementService {

View File

@@ -3,27 +3,27 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/common/errors';
import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion, IGalleryExtensionAssets, isIExtensionIdentifier, DefaultIconPath } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getOrDefault } from 'vs/base/common/objects';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPager } from 'vs/base/common/paging';
import { IRequestService, asJson, asText, isSuccess } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; // {{SQL CARBON EDIT}}
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionManifest, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/platform/extensions/common/extensions'; // {{SQL CARBON EDIT}} add imports
import { IFileService } from 'vs/platform/files/common/files';
import { canceled, getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
import { getOrDefault } from 'vs/base/common/objects';
import { IPager } from 'vs/base/common/paging';
import { isWeb } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { IProductService } from 'vs/platform/product/common/productService';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
import { IHeaders, IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; // {{SQL CARBON EDIT}} Add import
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { DefaultIconPath, IExtensionGalleryService, IExtensionIdentifier, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IReportedExtension, isIExtensionIdentifier, ITranslation, SortBy, SortOrder, StatisticType, WEB_EXTENSION_TAG } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionsPolicy, ExtensionsPolicyKey, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; // {{SQL CARBON EDIT}} Add ExtensionsPolicy and ExtensionsPolicyKey
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IFileService } from 'vs/platform/files/common/files';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { joinPath } from 'vs/base/common/resources';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; // {{SQL CARBON EDIT}} Remove unused
import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
interface IRawGalleryExtensionFile {
readonly assetType: string;
@@ -57,6 +57,11 @@ interface IRawGalleryExtension {
readonly publisher: { displayName: string, publisherId: string, publisherName: string; };
readonly versions: IRawGalleryExtensionVersion[];
readonly statistics: IRawGalleryExtensionStatistics[];
readonly tags: string[] | undefined;
readonly releaseDate: string;
readonly publishedDate: string;
readonly lastUpdated: string;
readonly categories: string[] | undefined;
readonly flags: string;
}
@@ -152,6 +157,7 @@ const DefaultQueryState: IQueryState = {
assetTypes: []
};
/* {{SQL CARBON EDIT}} Remove unused
type GalleryServiceQueryClassification = {
readonly filterTypes: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
readonly sortBy: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
@@ -164,6 +170,7 @@ type GalleryServiceQueryClassification = {
readonly errorCode?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
readonly count?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
*/
type QueryTelemetryData = {
readonly filterTypes: string[];
@@ -171,6 +178,7 @@ type QueryTelemetryData = {
readonly sortOrder: string;
};
/* {{SQL CARBON EDIT}} Remove unused
type GalleryServiceQueryEvent = QueryTelemetryData & {
readonly duration: number;
readonly success: boolean;
@@ -180,6 +188,7 @@ type GalleryServiceQueryEvent = QueryTelemetryData & {
readonly errorCode?: string;
readonly count?: string;
};
*/
class Query {
@@ -346,17 +355,6 @@ function getIsPreview(flags: string): boolean {
return flags.indexOf('preview') !== -1;
}
function getIsWebExtension(version: IRawGalleryExtensionVersion): boolean {
const webExtensionProperty = version.properties ? version.properties.find(p => p.key === PropertyType.WebExtension) : undefined;
return !!webExtensionProperty && webExtensionProperty.value === 'true';
}
function getWebResource(version: IRawGalleryExtensionVersion): URI | undefined {
return version.files.some(f => f.assetType.startsWith('Microsoft.VisualStudio.Code.WebResources'))
? joinPath(URI.parse(version.assetUri), 'Microsoft.VisualStudio.Code.WebResources', 'extension')
: undefined;
}
function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, index: number, query: Query, querySource?: string): IGalleryExtension {
const assets = <IGalleryExtensionAssets>{
manifest: getVersionAsset(version, AssetType.Manifest),
@@ -378,7 +376,6 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
},
name: galleryExtension.extensionName,
version: version.version,
date: version.lastUpdated,
displayName: galleryExtension.displayName,
publisherId: galleryExtension.publisher.publisherId,
publisher: galleryExtension.publisher.publisherName,
@@ -387,9 +384,11 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
installCount: getStatistic(galleryExtension.statistics, 'install'),
rating: getStatistic(galleryExtension.statistics, 'averagerating'),
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
assetUri: URI.parse(version.assetUri),
webResource: getWebResource(version),
assetTypes: version.files.map(({ assetType }) => assetType),
categories: galleryExtension.categories || [],
tags: galleryExtension.tags || [],
releaseDate: Date.parse(galleryExtension.releaseDate),
lastUpdated: Date.parse(version.lastUpdated), // {{SQL CARBON EDIT}} We don't have the lastUpdated at the top level currently
webExtension: !!galleryExtension.tags?.includes(WEB_EXTENSION_TAG),
assets,
properties: {
dependencies: getExtensions(version, PropertyType.Dependency),
@@ -398,7 +397,6 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
// {{SQL CARBON EDIT}}
azDataEngine: getAzureDataStudioEngine(version),
localizedLanguages: getLocalizedLanguages(version),
webExtension: getIsWebExtension(version)
},
/* __GDPR__FRAGMENT__
"GalleryExtensionTelemetryData2" : {
@@ -469,13 +467,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
async getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
const extension = await this.getCompatibleExtensionByEngine(arg1, version);
if (extension?.properties.webExtension) {
return extension.webResource ? extension : null;
} else {
return extension;
}
return this.getCompatibleExtensionByEngine(arg1, version);
}
private async getCompatibleExtensionByEngine(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
@@ -490,7 +482,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
const { id, uuid } = <IExtensionIdentifier>arg1; // {{SQL CARBON EDIT}} @anthonydresser remove extension ? extension.identifier
let query = new Query()
.withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, 1)
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code');
@@ -534,11 +526,22 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
throw new Error('No extension gallery service configured.');
}
const type = options.names ? 'ids' : (options.text ? 'text' : 'all');
let text = options.text || '';
const pageSize = getOrDefault(options, o => o.pageSize, 50);
type GalleryServiceQueryClassification = {
type: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
text: { classification: 'CustomerContent', purpose: 'FeatureInsight' };
};
type GalleryServiceQueryEvent = {
type: string;
text: string;
};
this.telemetryService.publicLog2<GalleryServiceQueryEvent, GalleryServiceQueryClassification>('galleryService:query', { type, text });
let query = new Query()
.withFlags(Flags.IncludeLatestVersionOnly, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withFlags(Flags.IncludeLatestVersionOnly, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, pageSize)
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code');
@@ -696,7 +699,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
extension.extensionId && extension.extensionId.toLocaleLowerCase().indexOf(text) > -1);
}
// {{SQL CARBON EDIT}}
public static compareByField(a: any, b: any, fieldName: string): number {
if (a && !b) {
return 1;
@@ -723,7 +725,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (!this.isEnabled()) {
throw new Error('No extension gallery service configured.');
}
// Always exclude non validated and unpublished extensions
query = query
.withFlags(query.flags, Flags.ExcludeNonValidated)
@@ -739,56 +740,34 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
'Content-Length': String(data.length)
};
const startTime = new Date().getTime();
let context: IRequestContext | undefined, error: any, total: number = 0;
const context = await this.requestService.request({
// {{SQL CARBON EDIT}}
type: 'GET',
url: this.api('/extensionquery'),
data,
headers
}, token);
try {
context = await this.requestService.request({
// {{SQL CARBON EDIT}}
type: 'GET',
url: this.api('/extensionquery'),
data,
headers
}, token);
// {{SQL CARBON EDIT}}
let extensionPolicy: string = this.configurationService.getValue<string>(ExtensionsPolicyKey);
if (context.res.statusCode && context.res.statusCode >= 400 && context.res.statusCode < 500 || extensionPolicy === ExtensionsPolicy.allowNone) {
return { galleryExtensions: [], total: 0 };
}
const result = await asJson<IRawGalleryQueryResult>(context);
if (result) {
const r = result.results[0];
const galleryExtensions = r.extensions;
// const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0]; {{SQL CARBON EDIT}} comment out for no unused
// const total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0; {{SQL CARBON EDIT}} comment out for no unused
// {{SQL CARBON EDIT}}
let extensionPolicy: string = this.configurationService.getValue<string>(ExtensionsPolicyKey);
if (context.res.statusCode && context.res.statusCode >= 400 && context.res.statusCode < 500 || extensionPolicy === ExtensionsPolicy.allowNone) {
return { galleryExtensions: [], total: 0 };
}
let filteredExtensionsResult = this.createQueryResult(query, galleryExtensions);
const result = await asJson<IRawGalleryQueryResult>(context);
if (result) {
const r = result.results[0];
const galleryExtensions = r.extensions;
// const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0]; {{SQL CARBON EDIT}} comment out for no unused
// const total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0; {{SQL CARBON EDIT}} comment out for no unused
// {{SQL CARBON EDIT}}
let filteredExtensionsResult = this.createQueryResult(query, galleryExtensions);
return { galleryExtensions: filteredExtensionsResult.galleryExtensions, total: filteredExtensionsResult.total };
// {{SQL CARBON EDIT}} - End
}
return { galleryExtensions: [], total };
} catch (e) {
error = e;
throw e;
} finally {
this.telemetryService.publicLog2<GalleryServiceQueryEvent, GalleryServiceQueryClassification>('galleryService:query', {
...query.telemetryData,
requestBodySize: String(data.length),
duration: new Date().getTime() - startTime,
success: !!context && isSuccess(context),
responseBodySize: context?.res.headers['Content-Length'],
statusCode: context ? String(context.res.statusCode) : undefined,
errorCode: error
? isPromiseCanceledError(error) ? 'canceled' : getErrorMessage(error).startsWith('XHR timeout') ? 'timeout' : 'failed'
: undefined,
count: String(total)
});
return { galleryExtensions: filteredExtensionsResult.galleryExtensions, total: filteredExtensionsResult.total };
// {{SQL CARBON EDIT}} - End
}
return { galleryExtensions: [], total: 0 };
}
async reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void> {
@@ -796,12 +775,15 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return undefined;
}
const url = isWeb ? this.api(`/itemName/${publisher}.${name}/version/${version}/statType/${type === StatisticType.Install ? '1' : '3'}/vscodewebextension`) : this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`);
const Accept = isWeb ? 'api-version=6.1-preview.1' : '*/*;api-version=4.0-preview.1';
const commonHeaders = await this.commonHeadersPromise;
const headers = { ...commonHeaders, Accept: '*/*;api-version=4.0-preview.1' };
const headers = { ...commonHeaders, Accept };
try {
await this.requestService.request({
type: 'POST',
url: this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`),
url,
headers
}, CancellationToken.None);
} catch (error) { /* Ignore */ }
@@ -873,7 +855,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
async getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]> {
let query = new Query()
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withFlags(Flags.IncludeVersions, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, 1)
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code');
@@ -904,7 +886,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
private async getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): Promise<IRequestContext> {
const commonHeaders = await this.commonHeadersPromise;
const commonHeaders = {}; // await this.commonHeadersPromise; {{SQL CARBON EDIT}} Because we query other sources such as github don't insert the custom VS headers - otherwise Electron will make a CORS preflight request which not all endpoints support.
const baseOptions = { type: 'GET' };
const headers = { ...commonHeaders, ...(options.headers || {}) };
options = { ...options, ...baseOptions, headers };

View File

@@ -3,17 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { Event } from 'vs/base/common/event';
import { IPager } from 'vs/base/common/paging';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { Event } from 'vs/base/common/event';
import { FileAccess } from 'vs/base/common/network';
import { IPager } from 'vs/base/common/paging';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ExtensionType, IExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$';
export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
export const WEB_EXTENSION_TAG = '__web_extension';
export interface IGalleryExtensionProperties {
dependencies?: string[];
@@ -22,7 +23,6 @@ export interface IGalleryExtensionProperties {
// {{SQL CARBON EDIT}}
azDataEngine?: string;
localizedLanguages?: string[];
webExtension?: boolean;
}
export interface IGalleryExtensionAsset {
@@ -80,7 +80,6 @@ export interface IGalleryExtension {
name: string;
identifier: IGalleryExtensionIdentifier;
version: string;
date: string;
displayName: string;
publisherId: string;
publisher: string;
@@ -89,13 +88,15 @@ export interface IGalleryExtension {
installCount: number;
rating: number;
ratingCount: number;
assetUri: URI;
assetTypes: string[];
categories: readonly string[];
tags: readonly string[];
releaseDate: number;
lastUpdated: number;
assets: IGalleryExtensionAssets;
properties: IGalleryExtensionProperties;
telemetryData: any;
preview: boolean;
webResource?: URI;
webExtension: boolean;
}
export interface IGalleryMetadata {
@@ -144,6 +145,7 @@ export interface IQueryOptions {
}
export const enum StatisticType {
Install = 'install',
Uninstall = 'uninstall'
}
@@ -183,17 +185,14 @@ export interface IExtensionGalleryService {
export interface InstallExtensionEvent {
identifier: IExtensionIdentifier;
zipPath?: string;
gallery?: IGalleryExtension;
source: URI | IGalleryExtension;
}
export interface DidInstallExtensionEvent {
identifier: IExtensionIdentifier;
operation: InstallOperation;
zipPath?: string;
gallery?: IGalleryExtension;
local?: ILocalExtension;
error?: string;
export interface InstallExtensionResult {
readonly identifier: IExtensionIdentifier;
readonly operation: InstallOperation;
readonly source?: URI | IGalleryExtension;
readonly local?: ILocalExtension;
}
export interface DidUninstallExtensionEvent {
@@ -208,6 +207,7 @@ export const INSTALL_ERROR_INCOMPATIBLE = 'incompatible';
export class ExtensionManagementError extends Error {
constructor(message: string, readonly code: string) {
super(message);
this.name = code;
}
}
@@ -215,12 +215,17 @@ export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, d
export type InstallVSIXOptions = InstallOptions & { installOnlyNewlyAddedFromExtensionPack?: boolean };
export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean };
export interface IExtensionManagementParticipant {
postInstall(local: ILocalExtension, source: URI | IGalleryExtension, options: InstallOptions | InstallVSIXOptions, token: CancellationToken): Promise<void>;
postUninstall(local: ILocalExtension, options: UninstallOptions, token: CancellationToken): Promise<void>;
}
export const IExtensionManagementService = createDecorator<IExtensionManagementService>('extensionManagementService');
export interface IExtensionManagementService {
readonly _serviceBrand: undefined;
onInstallExtension: Event<InstallExtensionEvent>;
onDidInstallExtension: Event<DidInstallExtensionEvent>;
onDidInstallExtensions: Event<readonly InstallExtensionResult[]>;
onUninstallExtension: Event<IExtensionIdentifier>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
@@ -237,6 +242,8 @@ export interface IExtensionManagementService {
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension>;
updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension>;
registerParticipant(pariticipant: IExtensionManagementParticipant): void;
}
export const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled';

View File

@@ -3,17 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { CancellationToken } from 'vs/base/common/cancellation';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { getBaseLabel } from 'vs/base/common/labels';
import { Schemas } from 'vs/base/common/network';
import { gt } from 'vs/base/common/semver/semver';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { CLIOutput, IExtensionGalleryService, IExtensionManagementCLIService, IExtensionManagementService, IGalleryExtension, ILocalExtension, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, EXTENSION_CATEGORIES, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { getBaseLabel } from 'vs/base/common/labels';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Schemas } from 'vs/base/common/network';
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, e.g.: {0}", 'ms-dotnettools.csharp');
@@ -236,9 +236,9 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
try {
if (installOptions.isBuiltin) {
output.log(localize('installing builtin ', "Installing builtin extension '{0}' v{1}...", id, galleryExtension.version));
output.log(version ? localize('installing builtin with version', "Installing builtin extension '{0}' v{1}...", id, version) : localize('installing builtin ', "Installing builtin extension '{0}'...", id));
} else {
output.log(localize('installing', "Installing extension '{0}' v{1}...", id, galleryExtension.version));
output.log(version ? localize('installing with version', "Installing extension '{0}' v{1}...", id, version) : localize('installing', "Installing extension '{0}'...", id));
}
await this.extensionManagementService.installFromGallery(galleryExtension, installOptions);

View File

@@ -3,14 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension, IExtensionTipsService, InstallOptions, UninstallOptions, InstallVSIXOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Emitter, Event } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
import { cloneAndChange } from 'vs/base/common/objects';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { Disposable } from 'vs/base/common/lifecycle';
import { cloneAndChange } from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IReportedExtension, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI {
return URI.revive(transformer ? transformer.transformIncoming(uri) : uri);
@@ -34,13 +34,13 @@ function transformOutgoingExtension(extension: ILocalExtension, transformer: IUR
export class ExtensionManagementChannel implements IServerChannel {
onInstallExtension: Event<InstallExtensionEvent>;
onDidInstallExtension: Event<DidInstallExtensionEvent>;
onDidInstallExtensions: Event<readonly InstallExtensionResult[]>;
onUninstallExtension: Event<IExtensionIdentifier>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer | null) {
this.onInstallExtension = Event.buffer(service.onInstallExtension, true);
this.onDidInstallExtension = Event.buffer(service.onDidInstallExtension, true);
this.onDidInstallExtensions = Event.buffer(service.onDidInstallExtensions, true);
this.onUninstallExtension = Event.buffer(service.onUninstallExtension, true);
this.onDidUninstallExtension = Event.buffer(service.onDidUninstallExtension, true);
}
@@ -49,7 +49,7 @@ export class ExtensionManagementChannel implements IServerChannel {
const uriTransformer = this.getUriTransformer(context);
switch (event) {
case 'onInstallExtension': return this.onInstallExtension;
case 'onDidInstallExtension': return Event.map(this.onDidInstallExtension, i => ({ ...i, local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local }));
case 'onDidInstallExtensions': return Event.map(this.onDidInstallExtensions, results => results.map(i => ({ ...i, local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local })));
case 'onUninstallExtension': return this.onUninstallExtension;
case 'onDidUninstallExtension': return this.onDidUninstallExtension;
}
@@ -85,8 +85,8 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
private readonly _onInstallExtension = this._register(new Emitter<InstallExtensionEvent>());
readonly onInstallExtension = this._onInstallExtension.event;
private readonly _onDidInstallExtension = this._register(new Emitter<DidInstallExtensionEvent>());
readonly onDidInstallExtension = this._onDidInstallExtension.event;
private readonly _onDidInstallExtensions = this._register(new Emitter<readonly InstallExtensionResult[]>());
readonly onDidInstallExtensions = this._onDidInstallExtensions.event;
private readonly _onUninstallExtension = this._register(new Emitter<IExtensionIdentifier>());
readonly onUninstallExtension = this._onUninstallExtension.event;
@@ -98,12 +98,20 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
private readonly channel: IChannel,
) {
super();
this._register(this.channel.listen<InstallExtensionEvent>('onInstallExtension')(e => this._onInstallExtension.fire(e)));
this._register(this.channel.listen<DidInstallExtensionEvent>('onDidInstallExtension')(e => this._onDidInstallExtension.fire({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local })));
this._register(this.channel.listen<InstallExtensionEvent>('onInstallExtension')(e => this._onInstallExtension.fire({ identifier: e.identifier, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source })));
this._register(this.channel.listen<readonly InstallExtensionResult[]>('onDidInstallExtensions')(results => this._onDidInstallExtensions.fire(results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source })))));
this._register(this.channel.listen<IExtensionIdentifier>('onUninstallExtension')(e => this._onUninstallExtension.fire(e)));
this._register(this.channel.listen<DidUninstallExtensionEvent>('onDidUninstallExtension')(e => this._onDidUninstallExtension.fire(e)));
}
private isUriComponents(thing: unknown): thing is UriComponents {
if (!thing) {
return false;
}
return typeof (<any>thing).path === 'string' &&
typeof (<any>thing).scheme === 'string';
}
zip(extension: ILocalExtension): Promise<URI> {
return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(<UriComponents>result)));
}
@@ -154,6 +162,8 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
getExtensionsReport(): Promise<IReportedExtension[]> {
return Promise.resolve(this.channel.call('getExtensionsReport'));
}
registerParticipant() { throw new Error('Not Supported'); }
}
export class ExtensionTipsChannel implements IServerChannel {

View File

@@ -3,9 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IReportedExtension, IExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { compareIgnoreCase } from 'vs/base/common/strings';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IExtensionIdentifier, IExtensionIdentifierWithVersion, IGalleryExtension, ILocalExtension, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier, IExtension } from 'vs/platform/extensions/common/extensions';
export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean {
if (a.uuid && b.uuid) {
@@ -131,3 +131,22 @@ export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set<str
return result;
}
export function getExtensionDependencies(installedExtensions: ReadonlyArray<IExtension>, extension: IExtension): IExtension[] {
const dependencies: IExtension[] = [];
const extensions = extension.manifest.extensionDependencies?.slice(0) ?? [];
while (extensions.length) {
const id = extensions.shift();
if (id && dependencies.every(e => !areSameExtensions(e.identifier, { id }))) {
const ext = installedExtensions.filter(e => areSameExtensions(e.identifier, { id }));
if (ext.length === 1) {
dependencies.push(ext[0]);
extensions.push(...ext[0].manifest.extensionDependencies?.slice(0) ?? []);
}
}
}
return dependencies;
}

View File

@@ -9,11 +9,11 @@ import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
const nlsRegex = /^%([\w\d.-]+)%$/i;
export interface ITranslations {
[key: string]: string;
[key: string]: string | { message: string; comment: string[] };
}
export function localizeManifest(manifest: IExtensionManifest, translations: ITranslations): IExtensionManifest {
const patcher = (value: string) => {
const patcher = (value: string): string | undefined => {
if (typeof value !== 'string') {
return undefined;
}
@@ -24,7 +24,8 @@ export function localizeManifest(manifest: IExtensionManifest, translations: ITr
return undefined;
}
return translations[match[1]] || value;
const translation = translations[match[1]] ?? value;
return typeof translation === 'string' ? translation : (typeof translation.message === 'string' ? translation.message : value);
};
return cloneAndChange(manifest, patcher);

View File

@@ -3,19 +3,19 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { IProductService } from 'vs/platform/product/common/productService';
import { IConfigBasedExtensionTip as IRawConfigBasedExtensionTip } from 'vs/base/common/product';
import { IFileService } from 'vs/platform/files/common/files';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IExtensionTipsService, IExecutableBasedExtensionTip, IWorkspaceTips, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement';
import { forEach } from 'vs/base/common/collections';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILogService } from 'vs/platform/log/common/log';
import { joinPath } from 'vs/base/common/resources';
import { getDomainsOfRemotes } from 'vs/platform/extensionManagement/common/configRemotes';
import { forEach } from 'vs/base/common/collections';
import { Disposable } from 'vs/base/common/lifecycle';
import { IConfigBasedExtensionTip as IRawConfigBasedExtensionTip } from 'vs/base/common/product';
import { joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { getDomainsOfRemotes } from 'vs/platform/extensionManagement/common/configRemotes';
import { IConfigBasedExtensionTip, IExecutableBasedExtensionTip, IExtensionTipsService, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { asJson, IRequestService } from 'vs/platform/request/common/request';
export class ExtensionTipsService extends Disposable implements IExtensionTipsService {

View File

@@ -3,26 +3,26 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { basename, join, } from 'vs/base/common/path';
import { IProductService } from 'vs/platform/product/common/productService';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { env } from 'vs/base/common/process';
import { IFileService } from 'vs/platform/files/common/files';
import { isWindows } from 'vs/base/common/platform';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { forEach, IStringDictionary } from 'vs/base/common/collections';
import { IRequestService } from 'vs/platform/request/common/request';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService';
import { disposableTimeout, timeout } from 'vs/base/common/async';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
import { localize } from 'vs/nls';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { forEach, IStringDictionary } from 'vs/base/common/collections';
import { Event } from 'vs/base/common/event';
import { join } from 'vs/base/common/path';
import { isWindows } from 'vs/base/common/platform';
import { env } from 'vs/base/common/process';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService';
import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IProductService } from 'vs/platform/product/common/productService';
import { IRequestService } from 'vs/platform/request/common/request';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
type ExeExtensionRecommendationsClassification = {
extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
@@ -130,13 +130,13 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
for (const extensionId of installed) {
const tip = importantExeBasedRecommendations.get(extensionId);
if (tip) {
this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: basename(tip.windowsPath!) });
this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: tip.exeName });
}
}
for (const extensionId of recommendations) {
const tip = importantExeBasedRecommendations.get(extensionId);
if (tip) {
this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:notInstalled', { extensionId, exeName: basename(tip.windowsPath!) });
this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:notInstalled', { extensionId, exeName: tip.exeName });
}
}

View File

@@ -3,20 +3,20 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Promises as FSPromises } from 'vs/base/node/pfs';
import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { ExtensionIdentifierWithVersion, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ILogService } from 'vs/platform/log/common/log';
import { generateUuid } from 'vs/base/common/uuid';
import * as semver from 'vs/base/common/semver/semver';
import { isWindows } from 'vs/base/common/platform';
import { Promises } from 'vs/base/common/async';
import { getErrorMessage } from 'vs/base/common/errors';
import { Disposable } from 'vs/base/common/lifecycle';
import { isWindows } from 'vs/base/common/platform';
import { joinPath } from 'vs/base/common/resources';
import * as semver from 'vs/base/common/semver/semver';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { Promises as FSPromises } from 'vs/base/node/pfs';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifierWithVersion, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
const ExtensionIdVersionRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)$/;

View File

@@ -3,17 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILogService } from 'vs/platform/log/common/log';
import { fork, ChildProcess } from 'child_process';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { join } from 'vs/base/common/path';
import { ChildProcess, fork } from 'child_process';
import { Limiter } from 'vs/base/common/async';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { join } from 'vs/base/common/path';
import { Promises } from 'vs/base/node/pfs';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILogService } from 'vs/platform/log/common/log';
export class ExtensionsLifecycle extends Disposable {

View File

@@ -5,10 +5,10 @@
import { Disposable } from 'vs/base/common/lifecycle';
import { join } from 'vs/base/common/path';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionManagementService, DidInstallExtensionEvent, DidUninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE } from 'vs/platform/extensions/common/extensions';
import * as pfs from 'vs/base/node/pfs';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { DidUninstallExtensionEvent, IExtensionManagementService, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement';
import { MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE } from 'vs/platform/extensions/common/extensions';
export class ExtensionsManifestCache extends Disposable {
@@ -19,12 +19,12 @@ export class ExtensionsManifestCache extends Disposable {
extensionsManagementService: IExtensionManagementService
) {
super();
this._register(extensionsManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
this._register(extensionsManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e)));
this._register(extensionsManagementService.onDidUninstallExtension(e => this.onDidUnInstallExtension(e)));
}
private onDidInstallExtension(e: DidInstallExtensionEvent): void {
if (!e.error) {
private onDidInstallExtensions(results: readonly InstallExtensionResult[]): void {
if (results.some(r => !!r.local)) {
this.invalidate();
}
}

View File

@@ -3,30 +3,30 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as semver from 'vs/base/common/semver/semver';
import { Disposable } from 'vs/base/common/lifecycle';
import * as pfs from 'vs/base/node/pfs';
import * as path from 'vs/base/common/path';
import { ILogService } from 'vs/platform/log/common/log';
import { ILocalExtension, IGalleryMetadata, ExtensionManagementError } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { areSameExtensions, ExtensionIdentifierWithVersion, groupByExtension, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { flatten } from 'vs/base/common/arrays';
import { Limiter, Promises, Queue } from 'vs/base/common/async';
import { IStringDictionary } from 'vs/base/common/collections';
import { getErrorMessage } from 'vs/base/common/errors';
import { Disposable } from 'vs/base/common/lifecycle';
import { FileAccess } from 'vs/base/common/network';
import * as path from 'vs/base/common/path';
import { isWindows } from 'vs/base/common/platform';
import { basename } from 'vs/base/common/resources';
import * as semver from 'vs/base/common/semver/semver';
import { URI } from 'vs/base/common/uri';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls';
import { generateUuid } from 'vs/base/common/uuid';
import * as pfs from 'vs/base/node/pfs';
import { extract, ExtractError } from 'vs/base/node/zip';
import { localize } from 'vs/nls';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionManagementError, IGalleryMetadata, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionIdentifierWithVersion, getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls';
import { ExtensionType, IExtensionIdentifier, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { CancellationToken } from 'vscode';
import { extract, ExtractError } from 'vs/base/node/zip';
import { isWindows } from 'vs/base/common/platform';
import { flatten } from 'vs/base/common/arrays';
import { IStringDictionary } from 'vs/base/common/collections';
import { FileAccess } from 'vs/base/common/network';
import { IFileService } from 'vs/platform/files/common/files';
import { basename } from 'vs/base/common/resources';
import { generateUuid } from 'vs/base/common/uuid';
import { getErrorMessage } from 'vs/base/common/errors';
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser';

View File

@@ -3,15 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionManagementService, ILocalExtension, InstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtUri } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { DidUninstallExtensionEvent, IExtensionManagementService, ILocalExtension, InstallExtensionEvent, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { FileChangeType, FileSystemProviderCapabilities, IFileChange, IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtUri } from 'vs/base/common/resources';
import { ILogService } from 'vs/platform/log/common/log';
export class ExtensionsWatcher extends Disposable {
@@ -35,13 +35,13 @@ export class ExtensionsWatcher extends Disposable {
this.startTimestamp = Date.now();
});
this._register(extensionsManagementService.onInstallExtension(e => this.onInstallExtension(e)));
this._register(extensionsManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
this._register(extensionsManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e)));
this._register(extensionsManagementService.onDidUninstallExtension(e => this.onDidUninstallExtension(e)));
const extensionsResource = URI.file(environmentService.extensionsPath);
const extUri = new ExtUri(resource => !fileService.hasCapability(resource, FileSystemProviderCapabilities.PathCaseSensitive));
this._register(fileService.watch(extensionsResource));
this._register(Event.filter(fileService.onDidFilesChange, e => e.changes.some(change => this.doesChangeAffects(change, extensionsResource, extUri)))(() => this.onDidChange()));
this._register(Event.filter(fileService.onDidChangeFilesRaw, e => e.changes.some(change => this.doesChangeAffects(change, extensionsResource, extUri)))(() => this.onDidChange()));
}
private doesChangeAffects(change: IFileChange, extensionsResource: URI, extUri: ExtUri): boolean {
@@ -72,10 +72,12 @@ export class ExtensionsWatcher extends Disposable {
this.addInstallingExtension(e.identifier);
}
private onDidInstallExtension(e: DidInstallExtensionEvent): void {
this.removeInstallingExtension(e.identifier);
if (!e.error) {
this.addInstalledExtension(e.identifier);
private onDidInstallExtensions(results: readonly InstallExtensionResult[]): void {
for (const e of results) {
this.removeInstallingExtension(e.identifier);
if (e.local) {
this.addInstalledExtension(e.identifier);
}
}
}

View File

@@ -4,19 +4,19 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { isUUID } from 'vs/base/common/uuid';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { isUUID } from 'vs/base/common/uuid';
import { mock } from 'vs/base/test/common/mock';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { NullLogService } from 'vs/platform/log/common/log';
import product from 'vs/platform/product/common/product';
import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { mock } from 'vs/base/test/common/mock';
class EnvironmentServiceMock extends mock<IEnvironmentService>() {
override readonly serviceMachineIdResource: URI;