Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -136,7 +141,7 @@ export interface IExtensionsScannerService {
scanMultipleExtensions(extensionLocations: URI[], extensionType: ExtensionType, scanOptions: ScanOptions): Promise<IScannedExtension[]>;
scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise<IScannedExtension[]>;

updateMetadata(extensionLocation: URI, metadata: Partial<Metadata>): Promise<void>;
updateManifestMetadata(extensionLocation: URI, metadata: ManifestMetadata): Promise<void>;
initializeDefaultProfileExtensions(): Promise<void>;
}

Expand Down Expand Up @@ -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<Metadata>): Promise<void> {
async updateManifestMetadata(extensionLocation: URI, metaData: ManifestMetadata): Promise<void> {
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')));
Expand Down Expand Up @@ -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 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand All @@ -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<UpdateMetadataErrorEvent, UpdateMetadataErrorClassification>('extension:extract', { extensionId: extensionKey.id, code: `${toFileOperationResult(error)}` });
throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata);
Expand Down Expand Up @@ -678,13 +678,9 @@ export class ExtensionsScanner extends Disposable {
return extensions.find(e => areSameExtensions(e.identifier, local.identifier));
}

async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension> {
async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI): Promise<ILocalExtension> {
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<UpdateMetadataErrorEvent, UpdateMetadataErrorClassification>('extension:extract', { extensionId: local.identifier.id, code: `${toFileOperationResult(error)}`, isProfile: !!profileLocation });
throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata);
Expand Down Expand Up @@ -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 });
}
}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<IExtension>, manifest?: Partial<IExtensionManifest>): IExtension {
return {
identifier: { id },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,20 @@ suite('NativeExtensionsScanerService Test', () => {
});

test('scan user extensions', async () => {
const manifest: Partial<IScannedExtensionManifest> = anExtensionManifest({ 'name': 'name', 'publisher': 'pub', __metadata: { id: 'uuid' } });
const manifest: Partial<IScannedExtensionManifest> = 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);
Expand Down Expand Up @@ -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<IExtensionManifest> = 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<IScannedExtensionManifest>): Promise<URI> {
const environmentService = instantiationService.get(INativeEnvironmentService);
return anExtension(manifest, URI.file(environmentService.extensionsPath));
Expand All @@ -310,7 +331,7 @@ suite('NativeExtensionsScanerService Test', () => {

async function anExtension(manifest: Partial<IScannedExtensionManifest>, root: URI): Promise<URI> {
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;
}
Expand Down
Loading