From 74c419b5bd8c18e45ec8a962a2b01a17bd17e7c7 Mon Sep 17 00:00:00 2001 From: samhanic <55490861+samhanic@users.noreply.github.com> Date: Sun, 3 Dec 2023 14:25:40 +0100 Subject: [PATCH 1/6] add cli update-extensions prototype --- cli/src/bin/code/legacy_args.rs | 9 +++++++++ cli/src/commands/args.rs | 5 +++++ cli/src/tunnels/code_server.rs | 4 ++++ resources/completions/bash/code | 2 +- resources/completions/zsh/_code | 1 + src/server-main.js | 4 ++-- src/vs/code/node/cli.ts | 1 + src/vs/code/node/cliProcessMain.ts | 4 ++++ src/vs/platform/environment/common/argv.ts | 1 + src/vs/platform/environment/node/argv.ts | 1 + .../extensionManagement/common/extensionManagementCLI.ts | 4 ++++ src/vs/server/node/remoteExtensionHostAgentCli.ts | 5 +++++ src/vs/server/node/server.cli.ts | 8 ++++++-- src/vs/server/node/serverEnvironmentService.ts | 2 ++ 14 files changed, 46 insertions(+), 5 deletions(-) diff --git a/cli/src/bin/code/legacy_args.rs b/cli/src/bin/code/legacy_args.rs index 6a533426608..3f134443641 100644 --- a/cli/src/bin/code/legacy_args.rs +++ b/cli/src/bin/code/legacy_args.rs @@ -60,6 +60,7 @@ pub fn try_parse_legacy( // Now translate them to subcommands. // --list-extensions -> ext list + // --update-extensions -> update // --install-extension=id -> ext install // --uninstall-extension=id -> ext uninstall // --status -> status @@ -87,6 +88,14 @@ pub fn try_parse_legacy( })), ..Default::default() }) + } else if let Some(_exts) = args.remove("update-extensions") { + Some(CliCore { + subcommand: Some(Commands::Extension(ExtensionArgs { + subcommand: ExtensionSubcommand::Update, + desktop_code_options, + })), + ..Default::default() + }) } else if let Some(exts) = args.remove("uninstall-extension") { Some(CliCore { subcommand: Some(Commands::Extension(ExtensionArgs { diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index 7863635ae99..6b0017a3488 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -254,6 +254,8 @@ pub enum ExtensionSubcommand { Install(InstallExtensionArgs), /// Uninstall an extension. Uninstall(UninstallExtensionArgs), + /// Update the marketplace-installed extensions. + Update, } impl ExtensionSubcommand { @@ -284,6 +286,9 @@ impl ExtensionSubcommand { target.push(format!("--uninstall-extension={}", id)); } } + ExtensionSubcommand::Update => { + target.push("--update-extensions".to_string()); + } } } } diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index 06e42681799..27d8bed1845 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -58,6 +58,7 @@ pub struct CodeServerArgs { // extension management pub install_extensions: Vec, pub uninstall_extensions: Vec, + pub update_extensions: bool, pub list_extensions: bool, pub show_versions: bool, pub category: Option, @@ -129,6 +130,9 @@ impl CodeServerArgs { for extension in &self.uninstall_extensions { args.push(format!("--uninstall-extension={}", extension)); } + if self.update_extensions { + args.push(String::from("--update-extensions")); + } if self.list_extensions { args.push(String::from("--list-extensions")); if self.show_versions { diff --git a/resources/completions/bash/code b/resources/completions/bash/code index 9f1b3d682d3..d141c297b96 100644 --- a/resources/completions/bash/code +++ b/resources/completions/bash/code @@ -49,7 +49,7 @@ _@@APPNAME@@() --list-extensions --show-versions --install-extension --uninstall-extension --enable-proposed-api --verbose --log -s --status -p --performance --prof-startup --disable-extensions - --disable-extension --inspect-extensions + --disable-extension --inspect-extensions --update-extensions --inspect-brk-extensions --disable-gpu' -- "$cur") ) [[ $COMPREPLY == *= ]] && compopt -o nospace return diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code index 97d163c13c3..46a0e019680 100644 --- a/resources/completions/zsh/_code +++ b/resources/completions/zsh/_code @@ -21,6 +21,7 @@ arguments=( '--show-versions[show versions of installed extensions, when using --list-extensions]' '--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"' '--uninstall-extension[uninstall an extension]:id or path:_files -g "*.vsix(-.)"' + '--update-extensions[update the marketplace-installed extensions]' '--enable-proposed-api[enables proposed API features for extensions]::extension id: ' '--verbose[print verbose output (implies --wait)]' '--log[log level to use]:level [info]:(critical error warn info debug trace off)' diff --git a/src/server-main.js b/src/server-main.js index 5167527baed..81e88e118f7 100644 --- a/src/server-main.js +++ b/src/server-main.js @@ -20,7 +20,7 @@ async function start() { // Do a quick parse to determine if a server or the cli needs to be started const parsedArgs = minimist(process.argv.slice(2), { - boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version', 'accept-server-license-terms'], + boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version', 'accept-server-license-terms', 'update-extensions'], string: ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension', 'socket-path', 'host', 'port', 'compatibility'], alias: { help: 'h', version: 'v' } }); @@ -34,7 +34,7 @@ async function start() { }); const extensionLookupArgs = ['list-extensions', 'locate-extension']; - const extensionInstallArgs = ['install-extension', 'install-builtin-extension', 'uninstall-extension']; + const extensionInstallArgs = ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'update-extensions']; const shouldSpawnCli = parsedArgs.help || parsedArgs.version || extensionLookupArgs.some(a => !!parsedArgs[a]) || (extensionInstallArgs.some(a => !!parsedArgs[a]) && !parsedArgs['start-server']); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 4770e9ef0bd..89265911347 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -33,6 +33,7 @@ function shouldSpawnCliProcess(argv: NativeParsedArgs): boolean { || !!argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension'] + || !!argv['update-extensions'] || !!argv['locate-extension'] || !!argv['telemetry']; } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index b2861976c11..b91367f1fc2 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -280,6 +280,10 @@ class CliMain extends Disposable { return instantiationService.createInstance(ExtensionManagementCLI, new ConsoleLogger(LogLevel.Info, false)).uninstallExtensions(this.asExtensionIdOrVSIX(this.argv['uninstall-extension']), !!this.argv['force'], profileLocation); } + else if (this.argv['update-extensions']) { + return instantiationService.createInstance(ExtensionManagementCLI, new ConsoleLogger(LogLevel.Info, false)).updateExtensions(profileLocation); + } + // Locate Extension else if (this.argv['locate-extension']) { return instantiationService.createInstance(ExtensionManagementCLI, new ConsoleLogger(LogLevel.Info, false)).locateExtension(this.argv['locate-extension']); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index f205cc30439..8b9f9100b05 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -82,6 +82,7 @@ export interface NativeParsedArgs { 'pre-release'?: boolean; 'install-builtin-extension'?: string[]; // undefined or array of 1 or more 'uninstall-extension'?: string[]; // undefined or array of 1 or more + 'update-extensions'?: boolean; 'locate-extension'?: string[]; // undefined or array of 1 or more 'enable-proposed-api'?: string[]; // undefined or array of 1 or more 'open-url'?: boolean; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 79046c7fa20..8ffc1c4a775 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -100,6 +100,7 @@ export const OPTIONS: OptionDescriptions> = { 'install-extension': { type: 'string[]', cat: 'e', args: 'ext-id | path', description: localize('installExtension', "Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '${publisher}.${name}'. Use '--force' argument to update to latest version. To install a specific version provide '@${version}'. For example: 'vscode.csharp@1.2.3'.") }, 'pre-release': { type: 'boolean', cat: 'e', description: localize('install prerelease', "Installs the pre-release version of the extension, when using --install-extension") }, 'uninstall-extension': { type: 'string[]', cat: 'e', args: 'ext-id', description: localize('uninstallExtension', "Uninstalls an extension.") }, + 'update-extensions': { type: 'boolean', cat: 'e', description: localize('updateExtensions', "Update the marketplace-installed extensions.") }, 'enable-proposed-api': { type: 'string[]', allowEmptyValue: true, cat: 'e', args: 'ext-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, 'version': { type: 'boolean', cat: 't', alias: 'v', description: localize('version', "Print version.") }, diff --git a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts index 1515c45cca2..046ba0df740 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts @@ -172,6 +172,10 @@ export class ExtensionManagementCLI { } } + public async updateExtensions(profileLocation?: URI): Promise { + this.logger.info("Update extensions: TODO"); + } + private async installVSIX(vsix: URI, installOptions: InstallOptions, force: boolean, installedExtensions: ILocalExtension[]): Promise { const manifest = await this.extensionManagementService.getManifest(vsix); diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index 5f6c64c3a1d..6960a2d4424 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -159,6 +159,11 @@ class CliMain extends Disposable { return extensionManagementCLI.uninstallExtensions(this.asExtensionIdOrVSIX(this.args['uninstall-extension']), !!this.args['force']); } + // Update the marketplace-installed extensions + else if (this.args['update-extensions']) { + return extensionManagementCLI.updateExtensions(); + } + // Locate Extension else if (this.args['locate-extension']) { return extensionManagementCLI.locateExtension(this.args['locate-extension']); diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index 10e1acbb917..22e77753062 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -67,6 +67,7 @@ const isSupportedForPipe = (optionId: keyof RemoteParsedArgs) => { case 'status': case 'install-extension': case 'uninstall-extension': + case 'update-extensions': case 'list-extensions': case 'force': case 'show-versions': @@ -217,7 +218,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise cmdLine.push('--install-extension', id)); parsedArgs['uninstall-extension']?.forEach(id => cmdLine.push('--uninstall-extension', id)); @@ -227,6 +228,9 @@ export async function main(desc: ProductDescription, args: string[]): Promise console.log(err)); @@ -293,7 +297,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise> = { 'builtin-extensions-dir': OPTIONS['builtin-extensions-dir'], 'install-extension': OPTIONS['install-extension'], 'install-builtin-extension': OPTIONS['install-builtin-extension'], + 'update-extensions': OPTIONS['update-extensions'], 'uninstall-extension': OPTIONS['uninstall-extension'], 'list-extensions': OPTIONS['list-extensions'], 'locate-extension': OPTIONS['locate-extension'], @@ -177,6 +178,7 @@ export interface ServerParsedArgs { 'builtin-extensions-dir'?: string; 'install-extension'?: string[]; 'install-builtin-extension'?: string[]; + 'update-extensions'?: boolean; 'uninstall-extension'?: string[]; 'list-extensions'?: boolean; 'locate-extension'?: string[]; From 0148753af20e6b0fdb1888b21679cceb9f7d9c15 Mon Sep 17 00:00:00 2001 From: samhanic <55490861+samhanic@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:58:37 +0100 Subject: [PATCH 2/6] add updateExtensions option to cli --- .../common/extensionManagementCLI.ts | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts index 046ba0df740..e41b3a2e3d9 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts @@ -173,7 +173,54 @@ export class ExtensionManagementCLI { } public async updateExtensions(profileLocation?: URI): Promise { - this.logger.info("Update extensions: TODO"); + const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User, profileLocation); + + const installedExtensionsQuery: IExtensionInfo[] = []; + for (const extension of installedExtensions) { + if (!!extension.identifier.uuid) { // No need to check new version for an unpublished extension + installedExtensionsQuery.push({ + id: `${extension.manifest.publisher}.${extension.manifest.name}`, + preRelease: extension.isPreReleaseVersion + }); + } + } + + this.logger.info(localize('updateExtensionsQuery', "Update extensions: querying version for {0} extensions", installedExtensionsQuery.length)); + const availableVersions = await this.extensionGalleryService.getExtensions(installedExtensionsQuery, { compatible: true }, CancellationToken.None); + + const newVersionAvailable: [IGalleryExtension, boolean, string][] = []; + for (const newVersion of availableVersions) { + for (const oldVersion of installedExtensions) { + if (areSameExtensions(oldVersion.identifier, newVersion.identifier) && gt(newVersion.version, oldVersion.manifest.version)) { + newVersionAvailable.push([newVersion, oldVersion.isPreReleaseVersion, oldVersion.manifest.version]); + } + } + } + + if (newVersionAvailable.length) { + this.logger.info(localize('updateExtensionsNewVersionsAvailable', "Update extensions: {0} extensions have new version available", newVersionAvailable.length)); + const failed: string[] = []; + await Promise.all(newVersionAvailable.map(async extensionInfo => { + try { + if (extensionInfo[1]) { + this.logger.info(localize('updateExtensionsPrereleaseVersion', "Updating extension '{0}' from prerelease version {1} to version {2}", extensionInfo[0].identifier.id, extensionInfo[2], extensionInfo[0].version)); + } + else { + this.logger.info(localize('updateExtensionsStandardVersion', "Updating extension '{0}' from version {1} to version {2}", extensionInfo[0].identifier.id, extensionInfo[2], extensionInfo[0].version)); + } + await this.extensionManagementService.installFromGallery(extensionInfo[0]); + } catch (err) { + this.logger.error(err.message || err.stack || err); + failed.push(extensionInfo[0].identifier.id); + } + })); + if (failed.length) { + throw new Error(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))); + } + } + else { + this.logger.info(localize('updateExtensionsNoExtensions', "No extension to update")); + } } private async installVSIX(vsix: URI, installOptions: InstallOptions, force: boolean, installedExtensions: ILocalExtension[]): Promise { From 3ff7d4545b3804e4fe4130bfeb1c5c063c85dd6b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 4 Dec 2023 08:12:05 -0800 Subject: [PATCH 3/6] Update cli/src/commands/args.rs Co-authored-by: Sandeep Somavarapu --- cli/src/commands/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index 6b0017a3488..ce1bf513e90 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -254,7 +254,7 @@ pub enum ExtensionSubcommand { Install(InstallExtensionArgs), /// Uninstall an extension. Uninstall(UninstallExtensionArgs), - /// Update the marketplace-installed extensions. + /// Update the installed extensions. Update, } From c2104486017f839e19cc9b157dff754a6c8943bb Mon Sep 17 00:00:00 2001 From: samhanic <55490861+samhanic@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:54:02 +0100 Subject: [PATCH 4/6] CLI update extension review fixes --- resources/completions/zsh/_code | 2 +- src/vs/platform/environment/node/argv.ts | 2 +- .../common/extensionManagementCLI.ts | 48 +++++++------------ .../node/remoteExtensionHostAgentCli.ts | 2 +- 4 files changed, 19 insertions(+), 35 deletions(-) diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code index 46a0e019680..eafa37c81c7 100644 --- a/resources/completions/zsh/_code +++ b/resources/completions/zsh/_code @@ -21,7 +21,7 @@ arguments=( '--show-versions[show versions of installed extensions, when using --list-extensions]' '--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"' '--uninstall-extension[uninstall an extension]:id or path:_files -g "*.vsix(-.)"' - '--update-extensions[update the marketplace-installed extensions]' + '--update-extensions[update the installed extensions]' '--enable-proposed-api[enables proposed API features for extensions]::extension id: ' '--verbose[print verbose output (implies --wait)]' '--log[log level to use]:level [info]:(critical error warn info debug trace off)' diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 8ffc1c4a775..691dd284960 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -100,7 +100,7 @@ export const OPTIONS: OptionDescriptions> = { 'install-extension': { type: 'string[]', cat: 'e', args: 'ext-id | path', description: localize('installExtension', "Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '${publisher}.${name}'. Use '--force' argument to update to latest version. To install a specific version provide '@${version}'. For example: 'vscode.csharp@1.2.3'.") }, 'pre-release': { type: 'boolean', cat: 'e', description: localize('install prerelease', "Installs the pre-release version of the extension, when using --install-extension") }, 'uninstall-extension': { type: 'string[]', cat: 'e', args: 'ext-id', description: localize('uninstallExtension', "Uninstalls an extension.") }, - 'update-extensions': { type: 'boolean', cat: 'e', description: localize('updateExtensions', "Update the marketplace-installed extensions.") }, + 'update-extensions': { type: 'boolean', cat: 'e', description: localize('updateExtensions', "Update the installed extensions.") }, 'enable-proposed-api': { type: 'string[]', allowEmptyValue: true, cat: 'e', args: 'ext-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, 'version': { type: 'boolean', cat: 't', alias: 'v', description: localize('version', "Print version.") }, diff --git a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts index e41b3a2e3d9..6a29ab91f67 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts @@ -10,7 +10,7 @@ import { basename } from 'vs/base/common/resources'; import { gt } from 'vs/base/common/semver/semver'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { EXTENSION_IDENTIFIER_REGEX, IExtensionGalleryService, IExtensionInfo, IExtensionManagementService, IGalleryExtension, ILocalExtension, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EXTENSION_IDENTIFIER_REGEX, IExtensionGalleryService, IExtensionInfo, IExtensionManagementService, IGalleryExtension, ILocalExtension, InstallOptions, InstallExtensionInfo, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, getGalleryExtensionId, getIdAndVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, EXTENSION_CATEGORIES, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { ILogger } from 'vs/platform/log/common/log'; @@ -29,7 +29,7 @@ function getId(manifest: IExtensionManifest, withVersion?: boolean): string { } type InstallVSIXInfo = { vsix: URI; installOptions: InstallOptions }; -type InstallExtensionInfo = { id: string; version?: string; installOptions: InstallOptions }; +type CLIInstallExtensionInfo = { id: string; version?: string; installOptions: InstallOptions }; export class ExtensionManagementCLI { @@ -89,7 +89,7 @@ export class ExtensionManagementCLI { } const installVSIXInfos: InstallVSIXInfo[] = []; - let installExtensionInfos: InstallExtensionInfo[] = []; + let installExtensionInfos: CLIInstallExtensionInfo[] = []; const addInstallExtensionInfo = (id: string, version: string | undefined, isBuiltin: boolean) => { installExtensionInfos.push({ id, version: version !== 'prerelease' ? version : undefined, installOptions: { ...installOptions, isBuiltin, installPreReleaseVersion: version === 'prerelease' || installOptions.installPreReleaseVersion } }); }; @@ -178,45 +178,29 @@ export class ExtensionManagementCLI { const installedExtensionsQuery: IExtensionInfo[] = []; for (const extension of installedExtensions) { if (!!extension.identifier.uuid) { // No need to check new version for an unpublished extension - installedExtensionsQuery.push({ - id: `${extension.manifest.publisher}.${extension.manifest.name}`, - preRelease: extension.isPreReleaseVersion - }); + installedExtensionsQuery.push({ ...extension.identifier, preRelease: extension.preRelease }); } } - this.logger.info(localize('updateExtensionsQuery', "Update extensions: querying version for {0} extensions", installedExtensionsQuery.length)); + this.logger.trace(localize('updateExtensionsQuery', "Fetching latest versions for {0} extensions", installedExtensionsQuery.length)); const availableVersions = await this.extensionGalleryService.getExtensions(installedExtensionsQuery, { compatible: true }, CancellationToken.None); - const newVersionAvailable: [IGalleryExtension, boolean, string][] = []; + const extensionsToUpdate: InstallExtensionInfo[] = []; for (const newVersion of availableVersions) { for (const oldVersion of installedExtensions) { if (areSameExtensions(oldVersion.identifier, newVersion.identifier) && gt(newVersion.version, oldVersion.manifest.version)) { - newVersionAvailable.push([newVersion, oldVersion.isPreReleaseVersion, oldVersion.manifest.version]); + extensionsToUpdate.push({ + extension: newVersion, + options: { operation: InstallOperation.Update, installPreReleaseVersion: oldVersion.isPreReleaseVersion } + }); } } } - if (newVersionAvailable.length) { - this.logger.info(localize('updateExtensionsNewVersionsAvailable', "Update extensions: {0} extensions have new version available", newVersionAvailable.length)); - const failed: string[] = []; - await Promise.all(newVersionAvailable.map(async extensionInfo => { - try { - if (extensionInfo[1]) { - this.logger.info(localize('updateExtensionsPrereleaseVersion', "Updating extension '{0}' from prerelease version {1} to version {2}", extensionInfo[0].identifier.id, extensionInfo[2], extensionInfo[0].version)); - } - else { - this.logger.info(localize('updateExtensionsStandardVersion', "Updating extension '{0}' from version {1} to version {2}", extensionInfo[0].identifier.id, extensionInfo[2], extensionInfo[0].version)); - } - await this.extensionManagementService.installFromGallery(extensionInfo[0]); - } catch (err) { - this.logger.error(err.message || err.stack || err); - failed.push(extensionInfo[0].identifier.id); - } - })); - if (failed.length) { - throw new Error(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))); - } + if (extensionsToUpdate.length) { + this.logger.info(localize('updateExtensionsNewVersionsAvailable', "Updating extensions: {0}", extensionsToUpdate.map(ext => ext.extension.identifier.id).join(', '))); + await this.extensionManagementService.installGalleryExtensions(extensionsToUpdate); + this.logger.info(localize('updateExtensionsDone', "Updated {0} extensions", extensionsToUpdate.length)); } else { this.logger.info(localize('updateExtensionsNoExtensions', "No extension to update")); @@ -248,7 +232,7 @@ export class ExtensionManagementCLI { return null; } - private async getGalleryExtensions(extensions: InstallExtensionInfo[]): Promise> { + private async getGalleryExtensions(extensions: CLIInstallExtensionInfo[]): Promise> { const galleryExtensions = new Map(); const preRelease = extensions.some(e => e.installOptions.installPreReleaseVersion); const targetPlatform = await this.extensionManagementService.getTargetPlatform(); @@ -267,7 +251,7 @@ export class ExtensionManagementCLI { return galleryExtensions; } - private async installFromGallery({ id, version, installOptions }: InstallExtensionInfo, galleryExtension: IGalleryExtension, installed: ILocalExtension[]): Promise { + private async installFromGallery({ id, version, installOptions }: CLIInstallExtensionInfo, galleryExtension: IGalleryExtension, installed: ILocalExtension[]): Promise { const manifest = await this.extensionGalleryService.getManifest(galleryExtension, CancellationToken.None); if (manifest && !this.validateExtensionKind(manifest)) { return null; diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index 6960a2d4424..b489ca72bcb 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -159,7 +159,7 @@ class CliMain extends Disposable { return extensionManagementCLI.uninstallExtensions(this.asExtensionIdOrVSIX(this.args['uninstall-extension']), !!this.args['force']); } - // Update the marketplace-installed extensions + // Update the installed extensions else if (this.args['update-extensions']) { return extensionManagementCLI.updateExtensions(); } From d2005b6706931608b7426c61bdc037e205904a76 Mon Sep 17 00:00:00 2001 From: samhanic <55490861+samhanic@users.noreply.github.com> Date: Thu, 4 Jan 2024 21:46:28 +0100 Subject: [PATCH 5/6] CLI update extension: add some logs --- .../common/extensionManagementCLI.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts index 6a29ab91f67..08dd0fb2761 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts @@ -197,14 +197,20 @@ export class ExtensionManagementCLI { } } - if (extensionsToUpdate.length) { - this.logger.info(localize('updateExtensionsNewVersionsAvailable', "Updating extensions: {0}", extensionsToUpdate.map(ext => ext.extension.identifier.id).join(', '))); - await this.extensionManagementService.installGalleryExtensions(extensionsToUpdate); - this.logger.info(localize('updateExtensionsDone', "Updated {0} extensions", extensionsToUpdate.length)); - } - else { + if (!extensionsToUpdate.length) { this.logger.info(localize('updateExtensionsNoExtensions', "No extension to update")); + return; } + + this.logger.info(localize('updateExtensionsNewVersionsAvailable', "Updating extensions: {0}", extensionsToUpdate.map(ext => ext.extension.identifier.id).join(', '))); + const installationResult = await this.extensionManagementService.installGalleryExtensions(extensionsToUpdate); + + const failed: string[] = installationResult.filter(ext => ext.error).map(ext => ext.identifier.id); + if (failed.length) { + throw new Error(localize('updateExtensionsFailed', "Failed updating extensions: {0}", failed.join(', '))); + } + + this.logger.info(localize('updateExtensionsDone', "Successfully updated {0} extensions", extensionsToUpdate.length)); } private async installVSIX(vsix: URI, installOptions: InstallOptions, force: boolean, installedExtensions: ILocalExtension[]): Promise { From 78fee308ca3d0e8a7ebb21ddc52b8a3e2b31e21f Mon Sep 17 00:00:00 2001 From: samhanic <55490861+samhanic@users.noreply.github.com> Date: Fri, 5 Jan 2024 12:28:17 +0100 Subject: [PATCH 6/6] review fix --- .../common/extensionManagementCLI.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts index 08dd0fb2761..cd67533d2c8 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts @@ -205,12 +205,13 @@ export class ExtensionManagementCLI { this.logger.info(localize('updateExtensionsNewVersionsAvailable', "Updating extensions: {0}", extensionsToUpdate.map(ext => ext.extension.identifier.id).join(', '))); const installationResult = await this.extensionManagementService.installGalleryExtensions(extensionsToUpdate); - const failed: string[] = installationResult.filter(ext => ext.error).map(ext => ext.identifier.id); - if (failed.length) { - throw new Error(localize('updateExtensionsFailed', "Failed updating extensions: {0}", failed.join(', '))); + for (const extensionResult of installationResult) { + if (extensionResult.error) { + this.logger.error(localize('errorUpdatingExtension', "Error while updating extension {0}: {1}", extensionResult.identifier.id, getErrorMessage(extensionResult.error))); + } else { + this.logger.info(localize('successUpdate', "Extension '{0}' v{1} was successfully updated.", extensionResult.identifier.id, extensionResult.local?.manifest.version)); + } } - - this.logger.info(localize('updateExtensionsDone', "Successfully updated {0} extensions", extensionsToUpdate.length)); } private async installVSIX(vsix: URI, installOptions: InstallOptions, force: boolean, installedExtensions: ILocalExtension[]): Promise {