- ignore empty content
- be resilient while loading extensions
This commit is contained in:
Sandeep Somavarapu 2023-02-10 17:06:38 +01:00 committed by GitHub
parent eb6f14df61
commit cffee30671
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 16 deletions

View file

@ -224,7 +224,7 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable
let storedProfileExtensions: IStoredProfileExtension[] | undefined;
try {
const content = await this.fileService.readFile(file);
storedProfileExtensions = JSON.parse(content.value.toString());
storedProfileExtensions = JSON.parse(content.value.toString().trim() || '[]');
} catch (error) {
if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {
throw error;

View file

@ -403,6 +403,26 @@ suite('ExtensionsProfileScannerService', () => {
} catch (error) { /*expected*/ }
});
test('read extension when manifest is empty', async () => {
const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');
await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(''));
const testObject = instantiationService.createInstance(TestObject, extensionsLocation);
const actual = await testObject.scanProfileExtensions(extensionsManifest);
assert.deepStrictEqual(actual, []);
});
test('read extension when manifest has empty lines and spaces', async () => {
const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');
await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(`
`));
const testObject = instantiationService.createInstance(TestObject, extensionsLocation);
const actual = await testObject.scanProfileExtensions(extensionsManifest);
assert.deepStrictEqual(actual, []);
});
function aExtension(id: string, location: URI, e?: Partial<IExtension>): IExtension {
return {
identifier: { id },

View file

@ -8,7 +8,7 @@ import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { IExtensionDescription, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { dedupExtensions } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IExtensionsScannerService, toExtensionDescription } from 'vs/platform/extensionManagement/common/extensionsScannerService';
import { IExtensionsScannerService, IScannedExtension, toExtensionDescription } from 'vs/platform/extensionManagement/common/extensionsScannerService';
import { ILogService } from 'vs/platform/log/common/log';
import Severity from 'vs/base/common/severity';
import { localize } from 'vs/nls';
@ -16,6 +16,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { timeout } from 'vs/base/common/async';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { getErrorMessage } from 'vs/base/common/errors';
export class CachedExtensionScanner {
@ -53,26 +54,55 @@ export class CachedExtensionScanner {
private async _scanInstalledExtensions(): Promise<IExtensionDescription[]> {
try {
const language = platform.language;
const [scannedSystemExtensions, scannedUserExtensions] = await Promise.all([
const result = await Promise.allSettled([
this._extensionsScannerService.scanSystemExtensions({ language, useCache: true, checkControlFile: true }),
this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfileService.currentProfile.extensionsResource, useCache: true })]);
const scannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment({ language }, [...scannedSystemExtensions, ...scannedUserExtensions]);
let scannedSystemExtensions: IScannedExtension[] = [],
scannedUserExtensions: IScannedExtension[] = [],
scannedDevelopedExtensions: IScannedExtension[] = [],
hasErrors = false;
if (result[0].status === 'fulfilled') {
scannedSystemExtensions = result[0].value;
} else {
hasErrors = true;
this._logService.error(`Error scanning system extensions:`, getErrorMessage(result[0].reason));
}
if (result[1].status === 'fulfilled') {
scannedUserExtensions = result[1].value;
} else {
hasErrors = true;
this._logService.error(`Error scanning user extensions:`, getErrorMessage(result[1].reason));
}
try {
scannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment({ language }, [...scannedSystemExtensions, ...scannedUserExtensions]);
} catch (error) {
this._logService.error(error);
}
const system = scannedSystemExtensions.map(e => toExtensionDescription(e, false));
const user = scannedUserExtensions.map(e => toExtensionDescription(e, false));
const development = scannedDevelopedExtensions.map(e => toExtensionDescription(e, true));
const r = dedupExtensions(system, user, development, this._logService);
const disposable = this._extensionsScannerService.onDidChangeCache(() => {
disposable.dispose();
this._notificationService.prompt(
Severity.Error,
localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."),
[{
label: localize('reloadWindow', "Reload Window"),
run: () => this._hostService.reload()
}]
);
});
timeout(5000).then(() => disposable.dispose());
if (!hasErrors) {
const disposable = this._extensionsScannerService.onDidChangeCache(() => {
disposable.dispose();
this._notificationService.prompt(
Severity.Error,
localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."),
[{
label: localize('reloadWindow', "Reload Window"),
run: () => this._hostService.reload()
}]
);
});
timeout(5000).then(() => disposable.dispose());
}
return r;
} catch (err) {
this._logService.error(`Error scanning installed extensions:`);