From 2ada481f67a4fc3048a521727b4e87480ba5a8fd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2025 11:25:39 +0100 Subject: [PATCH] fix #239434 --- .../common/extensionsProfileScannerService.ts | 3 +- .../common/extensionsScannerService.ts | 35 +++++++++++-------- .../node/extensionManagementService.ts | 16 ++++----- .../extensionsProfileScannerService.test.ts | 22 ++++++++++++ .../node/extensionsScannerService.test.ts | 29 ++++++++++++--- 5 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts index c0eefb920636b..16ba453fd05bb 100644 --- a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -270,8 +270,9 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable migrate = true; e.metadata.hasPreReleaseVersion = true; } + const uuid = e.metadata?.id ?? e.identifier.uuid; extensions.push({ - identifier: e.identifier, + identifier: uuid ? { id: e.identifier.id, uuid } : { id: e.identifier.id }, location, version: e.version, metadata: e.metadata, diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index c613e0669b4e7..ddcacaf37b2da 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -35,7 +35,12 @@ import { IUserDataProfile, IUserDataProfilesService } from '../../userDataProfil import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js'; import { localizeManifest } from './extensionNls.js'; -export type IScannedExtensionManifest = IRelaxedExtensionManifest & { __metadata?: Metadata }; +export type ManifestMetadata = Partial<{ + installedTimestamp: number; + size: number; +}>; + +export type IScannedExtensionManifest = IRelaxedExtensionManifest & { __metadata?: ManifestMetadata }; interface IRelaxedScannedExtension { type: ExtensionType; @@ -136,7 +141,7 @@ export interface IExtensionsScannerService { scanMultipleExtensions(extensionLocations: URI[], extensionType: ExtensionType, scanOptions: ScanOptions): Promise; scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise; - updateMetadata(extensionLocation: URI, metadata: Partial): Promise; + updateManifestMetadata(extensionLocation: URI, metadata: ManifestMetadata): Promise; initializeDefaultProfileExtensions(): Promise; } @@ -270,18 +275,10 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem return this.applyScanOptions(extensions, extensionType, { includeInvalid: scanOptions.includeInvalid, pickLatest: true }); } - async updateMetadata(extensionLocation: URI, metaData: Partial): Promise { + async updateManifestMetadata(extensionLocation: URI, metaData: ManifestMetadata): Promise { const manifestLocation = joinPath(extensionLocation, 'package.json'); const content = (await this.fileService.readFile(manifestLocation)).value.toString(); const manifest: IScannedExtensionManifest = JSON.parse(content); - - // unset if false - if (metaData.isMachineScoped === false) { - delete metaData.isMachineScoped; - } - if (metaData.isBuiltin === false) { - delete metaData.isBuiltin; - } manifest.__metadata = { ...manifest.__metadata, ...metaData }; await this.fileService.writeFile(joinPath(extensionLocation, 'package.json'), VSBuffer.fromString(JSON.stringify(manifest, null, '\t'))); @@ -665,10 +662,20 @@ class ExtensionsScanner extends Disposable { if (!manifest.publisher) { manifest.publisher = UNDEFINED_PUBLISHER; } - const metadata = scannedProfileExtension?.metadata ?? manifest.__metadata; - if (metadata && !metadata?.size && manifest.__metadata?.size) { - metadata.size = manifest.__metadata?.size; + + let metadata: Metadata | undefined; + if (scannedProfileExtension) { + metadata = { + ...scannedProfileExtension.metadata, + size: manifest.__metadata?.size, + }; + } else if (manifest.__metadata) { + metadata = { + installedTimestamp: manifest.__metadata.installedTimestamp, + size: manifest.__metadata.size, + }; } + delete manifest.__metadata; const id = getGalleryExtensionId(manifest.publisher, manifest.name); const identifier = metadata?.id ? { id, uuid: metadata.id } : { id }; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 35838459323b7..04ed182660968 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -37,7 +37,7 @@ import { } from '../common/extensionManagement.js'; import { areSameExtensions, computeTargetPlatform, ExtensionKey, getGalleryExtensionId, groupByExtension } from '../common/extensionManagementUtil.js'; import { IExtensionsProfileScannerService, IScannedProfileExtension } from '../common/extensionsProfileScannerService.js'; -import { IExtensionsScannerService, IScannedExtension, UserExtensionsScanOptions } from '../common/extensionsScannerService.js'; +import { IExtensionsScannerService, IScannedExtension, ManifestMetadata, UserExtensionsScanOptions } from '../common/extensionsScannerService.js'; import { ExtensionsDownloader } from './extensionDownloader.js'; import { ExtensionsLifecycle } from './extensionLifecycle.js'; import { fromExtractError, getManifest } from './extensionManagementUtil.js'; @@ -624,7 +624,7 @@ export class ExtensionsScanner extends Disposable { throw fromExtractError(e); } - const metadata: Metadata = { installedTimestamp: Date.now() }; + const metadata: ManifestMetadata = { installedTimestamp: Date.now() }; try { metadata.size = await computeSize(tempLocation, this.fileService); } catch (error) { @@ -633,7 +633,7 @@ export class ExtensionsScanner extends Disposable { } try { - await this.extensionsScannerService.updateMetadata(tempLocation, metadata); + await this.extensionsScannerService.updateManifestMetadata(tempLocation, metadata); } catch (error) { this.telemetryService.publicLog2('extension:extract', { extensionId: extensionKey.id, code: `${toFileOperationResult(error)}` }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata); @@ -678,13 +678,9 @@ export class ExtensionsScanner extends Disposable { return extensions.find(e => areSameExtensions(e.identifier, local.identifier)); } - async updateMetadata(local: ILocalExtension, metadata: Partial, profileLocation?: URI): Promise { + async updateMetadata(local: ILocalExtension, metadata: Partial, profileLocation: URI): Promise { try { - if (profileLocation) { - await this.extensionsProfileScannerService.updateMetadata([[local, metadata]], profileLocation); - } else { - await this.extensionsScannerService.updateMetadata(local.location, metadata); - } + await this.extensionsProfileScannerService.updateMetadata([[local, metadata]], profileLocation); } catch (error) { this.telemetryService.publicLog2('extension:extract', { extensionId: local.identifier.id, code: `${toFileOperationResult(error)}`, isProfile: !!profileLocation }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata); @@ -873,7 +869,7 @@ export class ExtensionsScanner extends Disposable { // set size if not set before if (isDefined(extension.metadata?.installedTimestamp) && isUndefined(extension.metadata?.size)) { const size = await computeSize(extension.location, this.fileService); - await this.extensionsScannerService.updateMetadata(extension.location, { size }); + await this.extensionsScannerService.updateManifestMetadata(extension.location, { size }); } })); } diff --git a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts index a21b64a95a5ee..7cecdfad82ba3 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts @@ -574,6 +574,28 @@ suite('ExtensionsProfileScannerService', () => { assert.ok(target4.notCalled); }); + test('read extension when uuid is different in identifier and manifest', async () => { + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{ + identifier: { + id: 'pub.a', + uuid: 'uuid1`' + }, + version: '1.0.0', + location: joinPath(extensionsLocation, 'pub.a-1.0.0').toString(), + relativeLocation: 'pub.a-1.0.0', + metadata: { + id: 'uuid', + } + }]))); + + const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation)); + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.length, 1); + assert.deepStrictEqual(actual[0].identifier.id, 'pub.a'); + assert.deepStrictEqual(actual[0].identifier.uuid, 'uuid'); + }); + function aExtension(id: string, location: URI, e?: Partial, manifest?: Partial): IExtension { return { identifier: { id }, diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts index 1d349f5822abd..ea0a4661df039 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts @@ -105,20 +105,20 @@ suite('NativeExtensionsScanerService Test', () => { }); test('scan user extensions', async () => { - const manifest: Partial = anExtensionManifest({ 'name': 'name', 'publisher': 'pub', __metadata: { id: 'uuid' } }); + const manifest: Partial = anExtensionManifest({ 'name': 'name', 'publisher': 'pub' }); const extensionLocation = await aUserExtension(manifest); const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); const actual = await testObject.scanAllUserExtensions(); assert.deepStrictEqual(actual.length, 1); - assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name', uuid: 'uuid' }); + assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); assert.deepStrictEqual(actual[0].location.toString(), extensionLocation.toString()); assert.deepStrictEqual(actual[0].isBuiltin, false); assert.deepStrictEqual(actual[0].type, ExtensionType.User); assert.deepStrictEqual(actual[0].isValid, true); assert.deepStrictEqual(actual[0].validations, []); - assert.deepStrictEqual(actual[0].metadata, { id: 'uuid' }); + assert.deepStrictEqual(actual[0].metadata, undefined); assert.deepStrictEqual(actual[0].targetPlatform, TargetPlatform.UNDEFINED); delete manifest.__metadata; assert.deepStrictEqual(actual[0].manifest, manifest); @@ -298,6 +298,27 @@ suite('NativeExtensionsScanerService Test', () => { assert.deepStrictEqual(actual!.manifest.displayName, 'Hello World'); }); + test('scan single extension with manifest metadata retains manifest metadata', async () => { + const manifest: Partial = anExtensionManifest({ 'name': 'name', 'publisher': 'pub' }); + const extensionLocation = await aUserExtension({ + ...manifest, + __metadata: { size: 12345, installedTimestamp: 1234567890 } + }); + const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); + + const actual = await testObject.scanExistingExtension(extensionLocation, ExtensionType.User, {}); + + assert.notStrictEqual(actual, null); + assert.deepStrictEqual(actual!.identifier, { id: 'pub.name' }); + assert.deepStrictEqual(actual!.location.toString(), extensionLocation.toString()); + assert.deepStrictEqual(actual!.isBuiltin, false); + assert.deepStrictEqual(actual!.type, ExtensionType.User); + assert.deepStrictEqual(actual!.isValid, true); + assert.deepStrictEqual(actual!.validations, []); + assert.deepStrictEqual(actual!.metadata, { size: 12345, installedTimestamp: 1234567890 }); + assert.deepStrictEqual(actual!.manifest, manifest); + }); + async function aUserExtension(manifest: Partial): Promise { const environmentService = instantiationService.get(INativeEnvironmentService); return anExtension(manifest, URI.file(environmentService.extensionsPath)); @@ -310,7 +331,7 @@ suite('NativeExtensionsScanerService Test', () => { async function anExtension(manifest: Partial, root: URI): Promise { const fileService = instantiationService.get(IFileService); - const extensionLocation = joinPath(root, `${manifest.publisher}.${manifest.name}-${manifest.version}-${manifest.__metadata?.targetPlatform ?? TargetPlatform.UNDEFINED}`); + const extensionLocation = joinPath(root, `${manifest.publisher}.${manifest.name}-${manifest.version}`); await fileService.writeFile(joinPath(extensionLocation, 'package.json'), VSBuffer.fromString(JSON.stringify(manifest))); return extensionLocation; }