diff --git a/doc/Settings.md b/doc/Settings.md index 5b63e25891..33109d2f1e 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -243,6 +243,17 @@ You can enable the feature as shown below. }, ``` +### uninstallPreviousArgument + +This feature enables the Windows Package Manager to override the upgrade behavior to UninstallPrevious by passing the `--uninstall-previous` argument with the upgrade or install command. +You can enable the feature as shown below. + +```json + "experimentalFeatures": { + "uninstallPreviousArgument": true + }, +``` + ### dependencies Experimental feature with the aim of managing dependencies, as of now it only shows package dependency information. You can enable the feature as shown below. diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index 6549c38324..db7258ce66 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -208,6 +208,11 @@ "description": "Enable argument to open default logs location", "type": "boolean", "default": false + }, + "uninstallPreviousArgument": { + "description": "Enable argument to override upgrade behavior to UninstallPrevious", + "type": "boolean", + "default": false } } } diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 73ae2fe195..438cba36d4 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -107,6 +107,8 @@ namespace AppInstaller::CLI return Argument{ "product-code"_liv, NoAlias, Args::Type::ProductCode, Resource::String::ProductCodeArgumentDescription, ArgumentType::Standard, false }; case Args::Type::OpenLogs: return Argument{ "open-logs"_liv, NoAlias, "logs"_liv, Args::Type::OpenLogs, Resource::String::OpenLogsArgumentDescription, ArgumentType::Flag, ExperimentalFeature::Feature::OpenLogsArgument}; + case Args::Type::UninstallPrevious: + return Argument{ "uninstall-previous"_liv, NoAlias, Args::Type::UninstallPrevious, Resource::String::UninstallPreviousArgumentDescription, ArgumentType::Flag, ExperimentalFeature::Feature::UninstallPreviousArgument }; case Args::Type::Force: return Argument{ "force"_liv, NoAlias, Args::Type::Force, Resource::String::ForceArgumentDescription, ArgumentType::Flag, false }; default: diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index f7808c611e..a9f98f0486 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -43,6 +43,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument::ForType(Args::Type::Rename), + Argument::ForType(Args::Type::UninstallPrevious), Argument::ForType(Args::Type::Force), }; } diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp index a1df44d719..53238e2455 100644 --- a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -110,6 +110,7 @@ namespace AppInstaller::CLI Argument::ForType(Execution::Args::Type::CustomHeader), Argument{ "all"_liv, 'r', "recurse"_liv, Args::Type::All, Resource::String::UpdateAllArgumentDescription, ArgumentType::Flag }, Argument{ "include-unknown"_liv, 'u', "unknown"_liv, Args::Type::IncludeUnknown, Resource::String::IncludeUnknownArgumentDescription, ArgumentType::Flag }, + Argument::ForType(Args::Type::UninstallPrevious), Argument::ForType(Args::Type::Force), }; } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index 22aa215354..911d1c56d3 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -84,6 +84,7 @@ namespace AppInstaller::CLI::Execution // Upgrade command All, // Used in Update command to update all installed packages to latest IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions + UninstallPrevious, // Used in Upgrade command to override the default manifest behavior to UninstallPrevious // Show command ListVersions, // Used in Show command to list all available versions of an app diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 66204f4735..96b78aa579 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -412,6 +412,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UninstallFailedWithCode); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFlowStartingPackageUninstall); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFlowUninstallSuccess); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallPreviousArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(UnrecognizedCommand); WINGET_DEFINE_RESOURCE_STRINGID(UnsupportedArgument); WINGET_DEFINE_RESOURCE_STRINGID(UpdateAllArgumentDescription); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index c1c4f73882..d3ff0b8eec 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -246,6 +246,7 @@ namespace AppInstaller::CLI::Workflow { bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); UpdateBehaviorEnum updateBehavior = context.Get().value().UpdateBehavior; + bool doUninstallPrevious = isUpdate && (updateBehavior == UpdateBehaviorEnum::UninstallPrevious || context.Args.Contains(Execution::Args::Type::UninstallPrevious)); switch (m_installerType) { @@ -255,7 +256,7 @@ namespace AppInstaller::CLI::Workflow case InstallerTypeEnum::Msi: case InstallerTypeEnum::Nullsoft: case InstallerTypeEnum::Wix: - if (isUpdate && updateBehavior == UpdateBehaviorEnum::UninstallPrevious) + if (doUninstallPrevious) { context << GetUninstallInfo << @@ -280,7 +281,7 @@ namespace AppInstaller::CLI::Workflow (isUpdate ? MSStoreUpdate : MSStoreInstall); break; case InstallerTypeEnum::Portable: - if (isUpdate && updateBehavior == UpdateBehaviorEnum::UninstallPrevious) + if (doUninstallPrevious) { context << GetUninstallInfo << diff --git a/src/AppInstallerCLIE2ETests/FeaturesCommand.cs b/src/AppInstallerCLIE2ETests/FeaturesCommand.cs index dff6f3dc4e..4282bc9f54 100644 --- a/src/AppInstallerCLIE2ETests/FeaturesCommand.cs +++ b/src/AppInstallerCLIE2ETests/FeaturesCommand.cs @@ -53,6 +53,7 @@ public void EnableExperimentalFeatures() WinGetSettingsHelper.ConfigureFeature("experimentalCmd", true); WinGetSettingsHelper.ConfigureFeature("directMSI", true); WinGetSettingsHelper.ConfigureFeature("openLogsArgument", true); + WinGetSettingsHelper.ConfigureFeature("uninstallPreviousArgument", true); var result = TestCommon.RunAICLICommand("features", string.Empty); Assert.True(result.StdOut.Contains("Enabled")); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 23bc85bf23..6cd2cc9b74 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -688,6 +688,9 @@ They can be configured through the settings file 'winget settings'. An unexpected error occurred while executing the command: + + Uninstall the previous version of the package during upgrade + Unrecognized command: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index 26534d8606..e1993cf9c3 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -46,6 +46,8 @@ namespace AppInstaller::Settings return userSettings.Get(); case ExperimentalFeature::Feature::Pinning: return userSettings.Get(); + case ExperimentalFeature::Feature::UninstallPreviousArgument: + return userSettings.Get(); default: THROW_HR(E_UNEXPECTED); } @@ -81,6 +83,8 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "Open Logs Argument", "openLogsArgument", "https://aka.ms/winget-settings", Feature::OpenLogsArgument }; case Feature::Pinning: return ExperimentalFeature{ "Package Pinning", "pinning", "https://aka.ms/winget-settings", Feature::Pinning}; + case Feature::UninstallPreviousArgument: + return ExperimentalFeature{ "Uninstall Previous Argument", "uninstallPreviousArgument", "https://aka.ms/winget-settings", Feature::UninstallPreviousArgument }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index f8c02303f0..bff388cf0e 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -26,6 +26,7 @@ namespace AppInstaller::Settings DirectMSI = 0x2, OpenLogsArgument = 0x4, Pinning = 0x8, + UninstallPreviousArgument = 0x10, Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index c4f9f6235c..10fe6572ee 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -79,6 +79,7 @@ namespace AppInstaller::Settings EFDirectMSI, EFOpenLogsArgument, EFPinning, + EFUninstallPreviousArgument, // Telemetry TelemetryDisable, // Install behavior @@ -148,6 +149,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFOpenLogsArgument, bool, bool, false, ".experimentalFeatures.openLogsArgument"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFPinning, bool, bool, false, ".experimentalFeatures.pinning"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFUninstallPreviousArgument, bool, bool, false, ".experimentalFeatures.uninstallPreviousArgument"sv); // Telemetry SETTINGMAPPING_SPECIALIZATION(Setting::TelemetryDisable, bool, bool, false, ".telemetry.disable"sv); // Install behavior diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 2b2c41160b..c0222a117e 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -262,6 +262,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFDirectMSI) WINGET_VALIDATE_PASS_THROUGH(EFOpenLogsArgument) WINGET_VALIDATE_PASS_THROUGH(EFPinning) + WINGET_VALIDATE_PASS_THROUGH(EFUninstallPreviousArgument) WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) WINGET_VALIDATE_PASS_THROUGH(InteractivityDisable) WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump)