Skip to content

Commit

Permalink
Support installing default extensions on first startup (#233957)
Browse files Browse the repository at this point in the history
Support installing default extensions on first startup #232990
  • Loading branch information
sandy081 authored Nov 15, 2024
1 parent 53ab97b commit 076f092
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { dirname, join } from 'path';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { isWindows } from '../../../../base/common/platform.js';
import { URI } from '../../../../base/common/uri.js';
import { INativeEnvironmentService } from '../../../../platform/environment/common/environment.js';
import { INativeServerExtensionManagementService } from '../../../../platform/extensionManagement/node/extensionManagementService.js';
import { ILogService } from '../../../../platform/log/common/log.js';
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { FileOperationResult, IFileService, IFileStat, toFileOperationResult } from '../../../../platform/files/common/files.js';
import { getErrorMessage } from '../../../../base/common/errors.js';

const defaultExtensionsInitStatusKey = 'initializing-default-extensions';

export class DefaultExtensionsInitializer extends Disposable {
constructor(
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,
@INativeServerExtensionManagementService private readonly extensionManagementService: INativeServerExtensionManagementService,
@IStorageService storageService: IStorageService,
@IFileService private readonly fileService: IFileService,
@ILogService private readonly logService: ILogService,
) {
super();

if (isWindows && storageService.getBoolean(defaultExtensionsInitStatusKey, StorageScope.APPLICATION, true)) {
storageService.store(defaultExtensionsInitStatusKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
this.initializeDefaultExtensions().then(() => storageService.store(defaultExtensionsInitStatusKey, false, StorageScope.APPLICATION, StorageTarget.MACHINE));
}
}

private async initializeDefaultExtensions(): Promise<void> {
const extensionsLocation = this.getDefaultExtensionVSIXsLocation();
let stat: IFileStat;
try {
stat = await this.fileService.resolve(extensionsLocation);
if (!stat.children) {
this.logService.debug('There are no default extensions to initialize', extensionsLocation.toString());
return;
}
} catch (error) {
if (toFileOperationResult(error) === FileOperationResult.FILE_NOT_FOUND) {
this.logService.debug('There are no default extensions to initialize', extensionsLocation.toString());
return;
}
this.logService.error('Error initializing extensions', error);
return;
}

const vsixs = stat.children.filter(child => child.name.endsWith('.vsix'));
if (vsixs.length === 0) {
this.logService.debug('There are no default extensions to initialize', extensionsLocation.toString());
return;
}

this.logService.info('Initializing default extensions', extensionsLocation.toString());
await Promise.all(vsixs.map(async vsix => {
this.logService.info('Installing default extension', vsix.resource.toString());
try {
await this.extensionManagementService.install(vsix.resource, { donotIncludePackAndDependencies: true, keepExisting: false });
this.logService.info('Default extension installed', vsix.resource.toString());
} catch (error) {
this.logService.error('Error installing default extension', vsix.resource.toString(), getErrorMessage(error));
}
}));
this.logService.info('Default extensions initialized', extensionsLocation.toString());
}

private getDefaultExtensionVSIXsLocation(): URI {
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app
// extensionsPath = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\extras\extensions
return URI.file(join(dirname(dirname(this.environmentService.appRoot)), 'extras', 'extensions'));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ import { getOSReleaseInfo } from '../../../base/node/osReleaseInfo.js';
import { getDesktopEnvironment } from '../../../base/common/desktopEnvironmentInfo.js';
import { getCodeDisplayProtocol, getDisplayProtocol } from '../../../base/node/osDisplayProtocolInfo.js';
import { RequestService } from '../../../platform/request/electron-utility/requestService.js';
import { DefaultExtensionsInitializer } from './contrib/defaultExtensionsInitializer.js';

class SharedProcessMain extends Disposable implements IClientConnectionFilter {

Expand Down Expand Up @@ -187,7 +188,8 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter {
instantiationService.createInstance(LogsDataCleaner),
instantiationService.createInstance(LocalizationsUpdater),
instantiationService.createInstance(ExtensionsContributions),
instantiationService.createInstance(UserDataProfilesCleaner)
instantiationService.createInstance(UserDataProfilesCleaner),
instantiationService.createInstance(DefaultExtensionsInitializer)
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
pinned: options.installGivenVersion ? true : !!options.pinned,
source: 'vsix',
},
options.keepExisting ?? true,
isBoolean(options.keepExisting) ? !options.keepExisting : true,
token);
return { local };
}
Expand Down

0 comments on commit 076f092

Please sign in to comment.