From a2bc061bc6f66e4823ba6fe6db8d9b25782b2d8b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 27 May 2020 20:31:00 +0200 Subject: [PATCH] allow setting default tag while installing extensions --- src/vs/code/node/cliProcessMain.ts | 14 ++++---- src/vs/platform/environment/node/argv.ts | 2 ++ .../common/extensionManagement.ts | 5 +-- .../node/extensionManagementService.ts | 33 ++++++++++--------- .../node/extensionsScanner.ts | 17 ++++++---- 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index ea0f62156a276..fbb9673b535ff 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -86,7 +86,7 @@ export class Main { } else if (argv['list-extensions']) { await this.listExtensions(!!argv['show-versions'], argv['category']); } else if (argv['install-extension']) { - await this.installExtensions(argv['install-extension'], !!argv['force']); + await this.installExtensions(argv['install-extension'], !!argv['force'], !!argv['default']); } else if (argv['uninstall-extension']) { await this.uninstallExtension(argv['uninstall-extension']); } else if (argv['locate-extension']) { @@ -126,7 +126,7 @@ export class Main { extensions.forEach(e => console.log(getId(e.manifest, showVersions))); } - private async installExtensions(extensions: string[], force: boolean): Promise { + private async installExtensions(extensions: string[], force: boolean, isDefault: boolean): Promise { const failed: string[] = []; const installedExtensionsManifests: IExtensionManifest[] = []; if (extensions.length) { @@ -135,7 +135,7 @@ export class Main { for (const extension of extensions) { try { - const manifest = await this.installExtension(extension, force); + const manifest = await this.installExtension(extension, force, isDefault); if (manifest) { installedExtensionsManifests.push(manifest); } @@ -150,7 +150,7 @@ export class Main { return failed.length ? Promise.reject(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))) : Promise.resolve(); } - private async installExtension(extension: string, force: boolean): Promise { + private async installExtension(extension: string, force: boolean, isDefault: boolean): Promise { if (/\.vsix$/i.test(extension)) { extension = path.isAbsolute(extension) ? extension : path.join(process.cwd(), extension); @@ -205,7 +205,7 @@ export class Main { } console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, extension.version)); } - await this.installFromGallery(id, extension); + await this.installFromGallery(id, extension, isDefault); return manifest; })); } @@ -227,11 +227,11 @@ export class Main { return true; } - private async installFromGallery(id: string, extension: IGalleryExtension): Promise { + private async installFromGallery(id: string, extension: IGalleryExtension, isDefault: boolean): Promise { console.log(localize('installing', "Installing extension '{0}' v{1}...", id, extension.version)); try { - await this.extensionManagementService.installFromGallery(extension); + await this.extensionManagementService.installFromGallery(extension, isDefault); console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, extension.version)); } catch (error) { if (isPromiseCanceledError(error)) { diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 61c2b16ad7a7d..5bd1488b47283 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -72,6 +72,7 @@ export interface ParsedArgs { remote?: string; 'disable-user-env-probe'?: boolean; 'force'?: boolean; + 'default'?: boolean; 'force-user-env'?: boolean; 'sync'?: 'on' | 'off'; @@ -187,6 +188,7 @@ export const OPTIONS: OptionDescriptions> = { 'file-chmod': { type: 'boolean' }, 'driver-verbose': { type: 'boolean' }, 'force': { type: 'boolean' }, + 'default': { type: 'boolean' }, 'trace': { type: 'boolean' }, 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 606a7932d81f4..b52906d91a736 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -92,6 +92,7 @@ export interface IGalleryMetadata { export interface ILocalExtension extends IExtension { readonly manifest: IExtensionManifest; + isDefault: boolean; publisherId: string | null; publisherDisplayName: string | null; readmeUrl: URI | null; @@ -205,8 +206,8 @@ export interface IExtensionManagementService { zip(extension: ILocalExtension): Promise; unzip(zipLocation: URI): Promise; getManifest(vsix: URI): Promise; - install(vsix: URI): Promise; - installFromGallery(extension: IGalleryExtension): Promise; + install(vsix: URI, isDefault?: boolean): Promise; + installFromGallery(extension: IGalleryExtension, isDefault?: boolean): Promise; uninstall(extension: ILocalExtension, force?: boolean): Promise; reinstallFromGallery(extension: ILocalExtension): Promise; getInstalled(type?: ExtensionType): Promise; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 371c35cdc4b14..b6969dea69c3a 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -45,7 +45,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 } from 'vs/platform/extensionManagement/node/extensionsScanner'; +import { ExtensionsScanner, IMetadata } from 'vs/platform/extensionManagement/node/extensionsScanner'; import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; @@ -57,7 +57,7 @@ const ERROR_UNKNOWN = 'unknown'; interface InstallableExtension { zipPath: string; identifierWithVersion: ExtensionIdentifierWithVersion; - metadata: IGalleryMetadata | null; + metadata?: IMetadata; } export class ExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -152,7 +152,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } - install(vsix: URI): Promise { + install(vsix: URI, isDefault: boolean = false): Promise { this.logService.trace('ExtensionManagementService#install', vsix.toString()); return createCancelablePromise(token => { return this.downloadVsix(vsix).then(downloadLocation => { @@ -192,10 +192,10 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(() => { this.logService.info('Installing the extension:', identifier.id); this._onInstallExtension.fire({ identifier, zipPath }); - return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) + return this.getGalleryMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) .then( - metadata => this.installFromZipPath(identifierWithVersion, zipPath, metadata, operation, token), - () => this.installFromZipPath(identifierWithVersion, zipPath, null, operation, token)) + metadata => this.installFromZipPath(identifierWithVersion, zipPath, { ...metadata, isDefault }, operation, token), + () => this.installFromZipPath(identifierWithVersion, zipPath, { isDefault }, operation, token)) .then( local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; }, e => { @@ -219,7 +219,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.downloadService.download(vsix, URI.file(downloadedLocation)).then(() => URI.file(downloadedLocation)); } - private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IGalleryMetadata | null, operation: InstallOperation, token: CancellationToken): Promise { + private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IMetadata, operation: InstallOperation, token: CancellationToken): Promise { return this.toNonCancellablePromise(this.installExtension({ zipPath, identifierWithVersion, metadata }, token) .then(local => this.installDependenciesAndPackExtensions(local, null) .then( @@ -239,7 +239,7 @@ export class ExtensionManagementService extends Disposable implements IExtension )); } - async installFromGallery(extension: IGalleryExtension): Promise { + async installFromGallery(extension: IGalleryExtension, isDefault?: boolean): Promise { if (!this.galleryService.isEnabled()) { return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); } @@ -287,8 +287,11 @@ export class ExtensionManagementService extends Disposable implements IExtension } this.downloadInstallableExtension(extension, operation) - .then(installableExtension => this.installExtension(installableExtension, cancellationToken) - .then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local))) + .then(installableExtension => { + installableExtension.metadata.isDefault = isDefault !== undefined ? isDefault : existingExtension.isDefault; + return this.installExtension(installableExtension, cancellationToken) + .then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local)); + }) .then(local => this.installDependenciesAndPackExtensions(local, existingExtension) .then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error)))) .then( @@ -358,7 +361,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(report => getMaliciousExtensionsSet(report).has(extension.identifier.id)); } - private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise { + private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise> { const metadata = { id: extension.identifier.uuid, publisherId: extension.publisherId, @@ -373,7 +376,7 @@ export class ExtensionManagementService extends Disposable implements IExtension this.logService.info('Downloaded extension:', extension.identifier.id, zipPath); return getManifest(zipPath) .then( - manifest => ({ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }), + manifest => (>{ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }), error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING)) ); }, @@ -480,14 +483,14 @@ export class ExtensionManagementService extends Disposable implements IExtension async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id); - local = await this.extensionsScanner.saveMetadataForLocalExtension(local, metadata); + local = await this.extensionsScanner.saveMetadataForLocalExtension(local, { ...metadata, isDefault: local.isDefault }); this.manifestCache.invalidate(); return local; } - private getMetadata(extensionName: string): Promise { + private getGalleryMetadata(extensionName: string): Promise { return this.findGalleryExtensionByName(extensionName) - .then(galleryExtension => galleryExtension ? { id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : null); + .then(galleryExtension => galleryExtension ? { id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : undefined); } private findGalleryExtension(local: ILocalExtension): Promise { diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts index 7781cc6696b70..19396b9a19fe6 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -32,6 +32,8 @@ const INSTALL_ERROR_EXTRACTING = 'extracting'; const INSTALL_ERROR_DELETING = 'deleting'; const INSTALL_ERROR_RENAMING = 'renaming'; +export type IMetadata = Partial & { isDefault: boolean; }; + export class ExtensionsScanner extends Disposable { private readonly systemExtensionsPath: string; @@ -127,7 +129,7 @@ export class ExtensionsScanner extends Disposable { throw new Error(localize('cannot read', "Cannot read the extension from {0}", this.extensionsPath)); } - async saveMetadataForLocalExtension(local: ILocalExtension, metadata: IGalleryMetadata): Promise { + async saveMetadataForLocalExtension(local: ILocalExtension, metadata: IMetadata): Promise { this.setMetadata(local, metadata); const manifestPath = path.join(local.location.fsPath, 'package.json'); const raw = await pfs.readFile(manifestPath, 'utf8'); @@ -227,7 +229,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)) : null; const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; - const local = { type, identifier, manifest, location: URI.file(extensionPath), readmeUrl, changelogUrl, publisherDisplayName: null, publisherId: null }; + const local = { type, identifier, manifest, location: URI.file(extensionPath), readmeUrl, changelogUrl, publisherDisplayName: null, publisherId: null, isDefault: false }; if (metadata) { this.setMetadata(local, metadata); } @@ -255,10 +257,11 @@ export class ExtensionsScanner extends Disposable { } } - private setMetadata(local: ILocalExtension, metadata: IGalleryMetadata): void { - local.publisherDisplayName = metadata.publisherDisplayName; - local.publisherId = metadata.publisherId; + private setMetadata(local: ILocalExtension, metadata: IMetadata): void { + local.publisherDisplayName = metadata.publisherDisplayName || null; + local.publisherId = metadata.publisherId || null; local.identifier.uuid = metadata.id; + local.isDefault = metadata.isDefault; } private async removeUninstalledExtensions(): Promise { @@ -314,7 +317,7 @@ export class ExtensionsScanner extends Disposable { return this._devSystemExtensionsPath; } - private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata | null; }> { + private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IMetadata | null; }> { const promises = [ pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8') .then(raw => this.parseManifest(raw)), @@ -330,7 +333,7 @@ export class ExtensionsScanner extends Disposable { }; } - private parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata | null; }> { + private parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IMetadata | null; }> { return new Promise((c, e) => { try { const manifest = JSON.parse(raw);