Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)

* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79

* Fix breaks

* Extension management fixes

* Fix breaks in windows bundling

* Fix/skip failing tests

* Update distro

* Add clear to nuget.config

* Add hygiene task

* Bump distro

* Fix hygiene issue

* Add build to hygiene exclusion

* Update distro

* Update hygiene

* Hygiene exclusions

* Update tsconfig

* Bump distro for server breaks

* Update build config

* Update darwin path

* Add done calls to notebook tests

* Skip failing tests

* Disable smoke tests
This commit is contained in:
Karl Burtram
2021-02-09 16:15:05 -08:00
committed by GitHub
parent 6f192f9af5
commit ce612a3d96
1929 changed files with 68012 additions and 34564 deletions

View File

@@ -4,15 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { rename } from 'vs/base/node/pfs';
import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
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 'semver-umd';
import * as semver from 'vs/base/common/semver/semver';
import { isWindows } from 'vs/base/common/platform';
const ExtensionIdVersionRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)$/;
@@ -23,7 +25,7 @@ export class ExtensionsDownloader extends Disposable {
private readonly cleanUpPromise: Promise<void>;
constructor(
@IEnvironmentService environmentService: INativeEnvironmentService,
@INativeEnvironmentService environmentService: INativeEnvironmentService,
@IFileService private readonly fileService: IFileService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@ILogService private readonly logService: ILogService,
@@ -36,8 +38,21 @@ export class ExtensionsDownloader extends Disposable {
async downloadExtension(extension: IGalleryExtension, operation: InstallOperation): Promise<URI> {
await this.cleanUpPromise;
const location = joinPath(this.extensionsDownloadDir, this.getName(extension));
await this.download(extension, location, operation);
const vsixName = this.getName(extension);
const location = joinPath(this.extensionsDownloadDir, vsixName);
// Download only if vsix does not exist
if (!await this.fileService.exists(location)) {
// Download to temporary location first only if vsix does not exist
const tempLocation = joinPath(this.extensionsDownloadDir, `.${vsixName}`);
if (!await this.fileService.exists(tempLocation)) {
await this.extensionGalleryService.download(extension, tempLocation, operation);
}
// Rename temp location to original
await this.rename(tempLocation, location, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */);
}
return location;
}
@@ -45,9 +60,15 @@ export class ExtensionsDownloader extends Disposable {
// noop as caching is enabled always
}
private async download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void> {
if (!await this.fileService.exists(location)) {
await this.extensionGalleryService.download(extension, location, operation);
private async rename(from: URI, to: URI, retryUntil: number): Promise<void> {
try {
await rename(from.fsPath, to.fsPath);
} catch (error) {
if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) {
this.logService.info(`Failed renaming ${from} to ${to} with 'EPERM' error. Trying again...`);
return this.rename(from, to, retryUntil);
}
throw error;
}
}
@@ -67,7 +88,7 @@ export class ExtensionsDownloader extends Disposable {
all.push([extension, stat]);
}
}
const byExtension = groupByExtension(all, ([extension]) => extension.identifier);
const byExtension = groupByExtension(all, ([extension]) => extension);
const distinct: IFileStatWithMetadata[] = [];
for (const p of byExtension) {
p.sort((a, b) => semver.rcompare(a[0].version, b[0].version));

View File

@@ -18,13 +18,15 @@ import {
InstallOperation,
INSTALL_ERROR_MALICIOUS,
INSTALL_ERROR_INCOMPATIBLE,
ExtensionManagementError
ExtensionManagementError,
InstallOptions,
UninstallOptions
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, getGalleryExtensionId, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import * as semver from 'semver-umd';
import * as semver from 'vs/base/common/semver/semver';
import { URI } from 'vs/base/common/uri';
import product from 'vs/platform/product/common/product';
import { isMacintosh } from 'vs/base/common/platform';
@@ -33,7 +35,7 @@ import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/ex
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { tmpdir } from 'os';
import { joinPath } from 'vs/base/common/resources';
import { generateUuid } from 'vs/base/common/uuid';
import { IDownloadService } from 'vs/platform/download/common/download';
import { optional, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -42,7 +44,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader';
import { ExtensionsScanner, IMetadata } from 'vs/platform/extensionManagement/node/extensionsScanner';
import { ExtensionsScanner, ILocalExtensionManifest, IMetadata } from 'vs/platform/extensionManagement/node/extensionsScanner';
import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle';
const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled';
@@ -82,7 +84,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
onDidUninstallExtension: Event<DidUninstallExtensionEvent> = this._onDidUninstallExtension.event;
constructor(
@IEnvironmentService environmentService: INativeEnvironmentService,
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@ILogService private readonly logService: ILogService,
@optional(IDownloadService) private downloadService: IDownloadService,
@@ -106,7 +108,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
async zip(extension: ILocalExtension): Promise<URI> {
this.logService.trace('ExtensionManagementService#zip', extension.identifier.id);
const files = await this.collectFiles(extension);
const location = await zip(path.join(tmpdir(), generateUuid()), files);
const location = await zip(joinPath(this.environmentService.tmpDir, generateUuid()).fsPath, files);
return URI.file(location);
}
@@ -147,10 +149,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
return files.map(f => (<IFile>{ path: `extension/${path.relative(extension.location.fsPath, f)}`, localPath: f }));
}
async install(vsix: URI, isMachineScoped?: boolean): Promise<ILocalExtension> {
async install(vsix: URI, options: InstallOptions = {}): Promise<ILocalExtension> {
// {{SQL CARBON EDIT}}
let startTime = new Date().getTime();
this.logService.trace('ExtensionManagementService#install', vsix.toString());
return createCancelablePromise(async token => {
@@ -172,7 +173,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
const installedExtensions = await this.getInstalled(ExtensionType.User);
const existing = installedExtensions.find(i => areSameExtensions(identifier, i.identifier));
if (existing) {
isMachineScoped = isMachineScoped || existing.isMachineScoped;
options.isMachineScoped = options.isMachineScoped || existing.isMachineScoped;
options.isBuiltin = options.isBuiltin || existing.isBuiltin;
// operation = InstallOperation.Update; {{ SQL CARBON EDIT }}
if (identifierWithVersion.equals(new ExtensionIdentifierWithVersion(existing.identifier, existing.manifest.version))) {
try {
@@ -201,6 +203,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
// {{SQL CARBON EDIT}}
// Until there's a gallery for SQL Ops Studio, skip retrieving the metadata from the gallery
let isMachineScoped = options.isMachineScoped;
return this.installExtension({ zipPath, identifierWithVersion, metadata: { isMachineScoped } }, token)
.then(
local => {
@@ -234,7 +237,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
throw new Error('Download service is not available');
}
const downloadedLocation = URI.file(path.join(tmpdir(), generateUuid()));
const downloadedLocation = joinPath(this.environmentService.tmpDir, generateUuid());
await this.downloadService.download(vsix, downloadedLocation);
return downloadedLocation;
}
@@ -244,7 +247,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
try {
const local = await this.installExtension({ zipPath, identifierWithVersion, metadata }, token);
try {
await this.installDependenciesAndPackExtensions(local, undefined);
await this.installDependenciesAndPackExtensions(local, undefined, options);
} catch (error) {
if (isNonEmptyArray(local.manifest.extensionDependencies)) {
this.logService.warn(`Cannot install dependencies of extension:`, local.identifier.id, error.message);
@@ -253,10 +256,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.logService.warn(`Cannot install packed extensions of extension:`, local.identifier.id, error.message);
}
}
this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, local, operation });
this._onDidInstallExtension.fire({ identifier: identifierWithVersion, zipPath, local, operation });
return local;
} catch (error) {
this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, operation, error });
this._onDidInstallExtension.fire({ identifier: identifierWithVersion, zipPath, operation, error });
throw error;
}
}*/
@@ -265,7 +268,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
return true;
}
async installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise<ILocalExtension> {
async installFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise<ILocalExtension> {
if (!this.galleryService.isEnabled()) {
throw new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"));
}
@@ -285,7 +288,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
const key = new ExtensionIdentifierWithVersion(extension.identifier, extension.version).key();
let cancellablePromise = this.installingExtensions.get(key);
if (!cancellablePromise) {
cancellablePromise = createCancelablePromise(token => this.doInstallFromGallery(extension, !!isMachineScoped, token));
cancellablePromise = createCancelablePromise(token => this.doInstallFromGallery(extension, options, token));
this.installingExtensions.set(key, cancellablePromise);
cancellablePromise.finally(() => this.installingExtensions.delete(key));
}
@@ -293,7 +296,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
return cancellablePromise;
}
private async doInstallFromGallery(extension: IGalleryExtension, isMachineScoped: boolean, token: CancellationToken): Promise<ILocalExtension> {
private async doInstallFromGallery(extension: IGalleryExtension, options: InstallOptions, token: CancellationToken): Promise<ILocalExtension> {
const startTime = new Date().getTime();
let operation: InstallOperation = InstallOperation.Install;
this.logService.info('Installing extension:', extension.identifier.id);
@@ -307,16 +310,19 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
const installableExtension = await this.downloadInstallableExtension(extension, operation);
installableExtension.metadata.isMachineScoped = isMachineScoped || existingExtension?.isMachineScoped;
installableExtension.metadata.isMachineScoped = options.isMachineScoped || existingExtension?.isMachineScoped;
installableExtension.metadata.isBuiltin = options.isBuiltin || existingExtension?.isBuiltin;
const local = await this.installExtension(installableExtension, token);
try { await this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)); } catch (error) { /* Ignore */ }
try {
await this.installDependenciesAndPackExtensions(local, existingExtension);
} catch (error) {
try { await this.uninstall(local); } catch (error) { /* Ignore */ }
throw error;
if (!options.donotIncludePackAndDependencies) {
try {
await this.installDependenciesAndPackExtensions(local, existingExtension, options);
} catch (error) {
try { await this.uninstall(local); } catch (error) { /* Ignore */ }
throw error;
}
}
if (existingExtension && semver.neq(existingExtension.manifest.version, extension.version)) {
@@ -412,7 +418,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
try {
const local = await this.unsetUninstalledAndGetLocal(installableExtension.identifierWithVersion);
if (local) {
return local;
return installableExtension.metadata ? this.extensionsScanner.saveMetadataForLocalExtension(local, installableExtension.metadata) : local;
}
} catch (e) {
if (isMacintosh) {
@@ -430,26 +436,25 @@ export class ExtensionManagementService extends Disposable implements IExtension
return null;
}
this.logService.trace('Removing the extension from uninstalled list:', identifierWithVersion.identifier.id);
this.logService.trace('Removing the extension from uninstalled list:', identifierWithVersion.id);
// If the same version of extension is marked as uninstalled, remove it from there and return the local.
await this.unsetUninstalled(identifierWithVersion);
this.logService.info('Removed the extension from uninstalled list:', identifierWithVersion.identifier.id);
this.logService.info('Removed the extension from uninstalled list:', identifierWithVersion.id);
const installed = await this.getInstalled(ExtensionType.User);
return installed.find(i => new ExtensionIdentifierWithVersion(i.identifier, i.manifest.version).equals(identifierWithVersion)) || null;
}
private async extractAndInstall({ zipPath, identifierWithVersion, metadata }: InstallableExtension, token: CancellationToken): Promise<ILocalExtension> {
const { identifier } = identifierWithVersion;
let local = await this.extensionsScanner.extractUserExtension(identifierWithVersion, zipPath, token);
this.logService.info('Installation completed.', identifier.id);
this.logService.info('Installation completed.', identifierWithVersion.id);
if (metadata) {
local = await this.extensionsScanner.saveMetadataForLocalExtension(local, metadata);
}
return local;
}
private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | undefined): Promise<void> {
private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | undefined, options: InstallOptions): Promise<void> {
if (!this.galleryService.isEnabled()) {
return;
}
@@ -472,7 +477,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
const galleryResult = await this.galleryService.query({ names, pageSize: dependenciesAndPackExtensions.length }, CancellationToken.None);
const extensionsToInstall = galleryResult.firstPage;
try {
await Promise.all(extensionsToInstall.map(e => this.installFromGallery(e)));
await Promise.all(extensionsToInstall.map(e => this.installFromGallery(e, options)));
} catch (error) {
try { await this.rollback(extensionsToInstall); } catch (e) { /* ignore */ }
throw error;
@@ -487,7 +492,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
await Promise.all(extensionsToUninstall.map(local => this.uninstall(local)));
}
async uninstall(extension: ILocalExtension): Promise<void> {
async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise<void> {
this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id);
const installed = await this.getInstalled(ExtensionType.User);
const extensionToUninstall = installed.find(e => areSameExtensions(e.identifier, extension.identifier));
@@ -496,7 +501,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
try {
await this.checkForDependenciesAndUninstall(extensionToUninstall, installed);
await this.checkForDependenciesAndUninstall(extensionToUninstall, installed, options);
} catch (error) {
throw this.joinErrors(error);
}
@@ -504,7 +509,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id);
local = await this.extensionsScanner.saveMetadataForLocalExtension(local, { ...metadata, isMachineScoped: local.isMachineScoped });
local = await this.extensionsScanner.saveMetadataForLocalExtension(local, { ...((<ILocalExtensionManifest>local.manifest).__metadata || {}), ...metadata });
this.manifestCache.invalidate();
return local;
}
async updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension> {
this.logService.trace('ExtensionManagementService#updateExtensionScope', local.identifier.id);
local = await this.extensionsScanner.saveMetadataForLocalExtension(local, { ...((<ILocalExtensionManifest>local.manifest).__metadata || {}), isMachineScoped });
this.manifestCache.invalidate();
return local;
}
@@ -543,15 +555,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
}, new Error(''));
}
private async checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): Promise<void> {
private async checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], options: UninstallOptions): Promise<void> {
try {
await this.preUninstallExtension(extension);
const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed);
if (packedExtensions.length) {
await this.uninstallExtensions(extension, packedExtensions, installed);
} else {
await this.uninstallExtensions(extension, [], installed);
}
const packedExtensions = options.donotIncludePack ? [] : this.getAllPackExtensionsToUninstall(extension, installed);
await this.uninstallExtensions(extension, packedExtensions, installed, options);
} catch (error) {
await this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
throw error;
@@ -559,10 +567,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
await this.postUninstallExtension(extension);
}
private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): Promise<void> {
private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[], options: UninstallOptions): Promise<void> {
const extensionsToUninstall = [extension, ...otherExtensionsToUninstall];
for (const e of extensionsToUninstall) {
this.checkForDependents(e, extensionsToUninstall, installed, extension);
if (!options.donotCheckDependents) {
for (const e of extensionsToUninstall) {
this.checkForDependents(e, extensionsToUninstall, installed, extension);
}
}
await Promise.all([this.uninstallExtension(extension), ...otherExtensionsToUninstall.map(d => this.doUninstall(d))]);
}
@@ -613,7 +623,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
checked.push(extension);
const extensionsPack = extension.manifest.extensionPack ? extension.manifest.extensionPack : [];
if (extensionsPack.length) {
const packedExtensions = installed.filter(i => extensionsPack.some(id => areSameExtensions({ id }, i.identifier)));
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));

View File

@@ -0,0 +1,97 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as crypto from 'crypto';
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
export class ExtensionUrlTrustService implements IExtensionUrlTrustService {
declare readonly _serviceBrand: undefined;
private trustedExtensionUrlPublicKeys = new Map<string, (crypto.KeyObject | string | null)[]>();
constructor(
@IProductService private readonly productService: IProductService,
@ILogService private readonly logService: ILogService
) { }
async isExtensionUrlTrusted(extensionId: string, url: string): Promise<boolean> {
if (!this.productService.trustedExtensionUrlPublicKeys) {
this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'There are no configured trusted keys');
return false;
}
const match = /^(.*)#([^#]+)$/.exec(url);
if (!match) {
this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Uri has no fragment', url);
return false;
}
const [, originalUrl, fragment] = match;
let keys = this.trustedExtensionUrlPublicKeys.get(extensionId);
if (!keys) {
keys = this.productService.trustedExtensionUrlPublicKeys[extensionId];
if (!keys || keys.length === 0) {
this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Extension doesn\'t have any trusted keys', extensionId);
return false;
}
this.trustedExtensionUrlPublicKeys.set(extensionId, [...keys]);
}
const fragmentBuffer = Buffer.from(decodeURIComponent(fragment), 'base64');
if (fragmentBuffer.length <= 6) {
this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Uri fragment is not a signature', url);
return false;
}
const timestampBuffer = fragmentBuffer.slice(0, 6);
const timestamp = fragmentBuffer.readUIntBE(0, 6);
const diff = Date.now() - timestamp;
if (diff < 0 || diff > 3_600_000) { // 1 hour
this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri has expired', url);
return false;
}
const signatureBuffer = fragmentBuffer.slice(6);
const verify = crypto.createVerify('SHA256');
verify.write(timestampBuffer);
verify.write(Buffer.from(originalUrl));
verify.end();
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (key === null) { // failed to be parsed before
continue;
} else if (typeof key === 'string') { // needs to be parsed
try {
key = crypto.createPublicKey({ key: Buffer.from(key, 'base64'), format: 'der', type: 'spki' });
keys[i] = key;
} catch (err) {
this.logService.warn('ExtensionUrlTrustService#isExtensionUrlTrusted', `Failed to parse trusted extension uri public key #${i + 1} for ${extensionId}:`, err);
keys[i] = null;
continue;
}
}
if (verify.verify(key, signatureBuffer)) {
this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri is valid', url);
return true;
}
}
this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri could not be verified', url);
return false;
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as semver from 'semver-umd';
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';
@@ -13,8 +13,7 @@ import { ExtensionType, IExtensionManifest, IExtensionIdentifier } from 'vs/plat
import { areSameExtensions, ExtensionIdentifierWithVersion, groupByExtension, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Limiter, Queue } from 'vs/base/common/async';
import { URI } from 'vs/base/common/uri';
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls';
import { localize } from 'vs/nls';
import { IProductService } from 'vs/platform/product/common/productService';
@@ -23,6 +22,7 @@ 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';
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser';
@@ -30,8 +30,9 @@ const INSTALL_ERROR_EXTRACTING = 'extracting';
const INSTALL_ERROR_DELETING = 'deleting';
const INSTALL_ERROR_RENAMING = 'renaming';
export type IMetadata = Partial<IGalleryMetadata & { isMachineScoped: boolean; }>;
type ILocalExtensionManifest = IExtensionManifest & { __metadata?: IMetadata };
export type IMetadata = Partial<IGalleryMetadata & { isMachineScoped: boolean; isBuiltin: boolean }>;
export type ILocalExtensionManifest = IExtensionManifest & { __metadata?: IMetadata };
type IRelaxedLocalExtension = Omit<ILocalExtension, 'isBuiltin'> & { isBuiltin: boolean };
export class ExtensionsScanner extends Disposable {
@@ -43,12 +44,12 @@ export class ExtensionsScanner extends Disposable {
constructor(
private readonly beforeRemovingExtension: (e: ILocalExtension) => Promise<void>,
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: INativeEnvironmentService,
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,
@IProductService private readonly productService: IProductService,
) {
super();
this.systemExtensionsPath = environmentService.builtinExtensionsPath;
this.extensionsPath = environmentService.extensionsPath!;
this.extensionsPath = environmentService.extensionsPath;
this.uninstalledPath = path.join(this.extensionsPath, '.obsolete');
this.uninstalledFileLimiter = new Queue();
}
@@ -94,7 +95,6 @@ export class ExtensionsScanner extends Disposable {
}
async extractUserExtension(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, token: CancellationToken): Promise<ILocalExtension> {
const { identifier } = identifierWithVersion;
const folderName = identifierWithVersion.key();
const tempPath = path.join(this.extensionsPath, `.${folderName}`);
const extensionPath = path.join(this.extensionsPath, folderName);
@@ -105,12 +105,12 @@ export class ExtensionsScanner extends Disposable {
try {
await pfs.rimraf(extensionPath);
} catch (e) { /* ignore */ }
throw new ExtensionManagementError(localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, identifier.id), INSTALL_ERROR_DELETING);
throw new ExtensionManagementError(localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, identifierWithVersion.id), INSTALL_ERROR_DELETING);
}
await this.extractAtLocation(identifier, zipPath, tempPath, token);
await this.extractAtLocation(identifierWithVersion, zipPath, tempPath, token);
try {
await this.rename(identifier, tempPath, extensionPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */);
await this.rename(identifierWithVersion, tempPath, extensionPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */);
this.logService.info('Renamed to', extensionPath);
} catch (error) {
this.logService.info('Rename failed. Deleting from extracted location', tempPath);
@@ -136,6 +136,7 @@ export class ExtensionsScanner extends Disposable {
// unset if false
metadata.isMachineScoped = metadata.isMachineScoped || undefined;
metadata.isBuiltin = metadata.isBuiltin || undefined;
const manifestPath = path.join(local.location.fsPath, 'package.json');
const raw = await pfs.readFile(manifestPath, 'utf8');
const { manifest } = await this.parseManifest(raw);
@@ -253,7 +254,7 @@ export class ExtensionsScanner extends Disposable {
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : undefined;
const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
const local = <ILocalExtension>{ type, identifier, manifest, location: URI.file(extensionPath), readmeUrl, changelogUrl, publisherDisplayName: null, publisherId: null, isMachineScoped: false };
const local = <ILocalExtension>{ type, identifier, manifest, location: URI.file(extensionPath), readmeUrl, changelogUrl, publisherDisplayName: null, publisherId: null, isMachineScoped: false, isBuiltin: type === ExtensionType.System };
if (metadata) {
this.setMetadata(local, metadata);
}
@@ -281,11 +282,12 @@ export class ExtensionsScanner extends Disposable {
}
}
private setMetadata(local: ILocalExtension, metadata: IMetadata): void {
private setMetadata(local: IRelaxedLocalExtension, metadata: IMetadata): void {
local.publisherDisplayName = metadata.publisherDisplayName || null;
local.publisherId = metadata.publisherId || null;
local.identifier.uuid = metadata.id;
local.isMachineScoped = !!metadata.isMachineScoped;
local.isBuiltin = local.type === ExtensionType.System || !!metadata.isBuiltin;
}
private async removeUninstalledExtensions(): Promise<void> {
@@ -336,7 +338,7 @@ export class ExtensionsScanner extends Disposable {
private _devSystemExtensionsPath: string | null = null;
private get devSystemExtensionsPath(): string {
if (!this._devSystemExtensionsPath) {
this._devSystemExtensionsPath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions'));
this._devSystemExtensionsPath = path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', '.build', 'builtInExtensions'));
}
return this._devSystemExtensionsPath;
}
@@ -362,7 +364,6 @@ export class ExtensionsScanner extends Disposable {
try {
const manifest = JSON.parse(raw);
const metadata = manifest.__metadata || null;
delete manifest.__metadata;
c({ manifest, metadata });
} catch (err) {
e(new Error(localize('invalidManifest', "Extension invalid: package.json is not a JSON file.")));