winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit bf9914a1126e2673f4c386a17400482ff095b038
parent 25857a8a985cdd39b0e6d7932c1c178e1f01a3ff
Author: Kaleb Luedtke <jluedtk@jci.com>
Date:   Wed, 11 Jan 2023 22:23:58 -0600

Add --custom argument for passing additional installer arguments (#2832)


Diffstat:
Msrc/AppInstallerCLICore/Argument.cpp | 2++
Msrc/AppInstallerCLICore/Commands/InstallCommand.cpp | 1+
Msrc/AppInstallerCLICore/Commands/UpgradeCommand.cpp | 1+
Msrc/AppInstallerCLICore/ExecutionArgs.h | 1+
Msrc/AppInstallerCLICore/Resources.h | 1+
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp | 11+++++++++++
Msrc/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 3+++
Msrc/AppInstallerCLITests/InstallFlow.cpp | 37+++++++++++++++++++++++++++++++++++++
Msrc/Microsoft.Management.Deployment/InstallOptions.cpp | 8++++++++
Msrc/Microsoft.Management.Deployment/InstallOptions.h | 3+++
Msrc/Microsoft.Management.Deployment/PackageManager.cpp | 5+++++
Msrc/Microsoft.Management.Deployment/PackageManager.idl | 9++++++++-
Msrc/PowerShell/Microsoft.WinGet.Client/Commands/Common/BaseInstallCommand.cs | 14+++++++++++++-
13 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp @@ -53,6 +53,8 @@ namespace AppInstaller::CLI return Argument{ "architecture"_liv, 'a', Args::Type::InstallArchitecture, Resource::String::InstallArchitectureArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Log: return Argument{ "log"_liv, 'o', Args::Type::Log, Resource::String::LogArgumentDescription, ArgumentType::Standard }; + case Args::Type::CustomSwitches: + return Argument{ "custom"_liv, NoAlias, Args::Type::CustomSwitches, Resource::String::CustomSwitchesArgumentDescription, ArgumentType::Standard}; case Args::Type::Override: return Argument{ "override"_liv, NoAlias, Args::Type::Override, Resource::String::OverrideArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::InstallLocation: diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -33,6 +33,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::Silent), Argument::ForType(Args::Type::Locale), Argument::ForType(Args::Type::Log), + Argument::ForType(Args::Type::CustomSwitches), Argument::ForType(Args::Type::Override), Argument::ForType(Args::Type::InstallLocation), Argument::ForType(Args::Type::HashOverride), diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -98,6 +98,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::Silent), // -h Argument::ForType(Args::Type::Purge), Argument::ForType(Args::Type::Log), // -o + Argument::ForType(Args::Type::CustomSwitches), Argument::ForType(Args::Type::Override), Argument::ForType(Args::Type::InstallLocation), // -l Argument{ s_ArgumentName_Scope, Argument::NoAlias, Execution::Args::Type::InstallScope, Resource::String::InstalledScopeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h @@ -36,6 +36,7 @@ namespace AppInstaller::CLI::Execution Silent, Locale, Log, + CustomSwitches, // CustomSwitches args are args passed to the installer in addition to any defined in the manifest Override, // Override args are (and the only args) directly passed to installer InstallLocation, InstallScope, diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h @@ -50,6 +50,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ConvertInstallFlowToUpgrade); WINGET_DEFINE_RESOURCE_STRINGID(CountArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CountOutOfBoundsError); + WINGET_DEFINE_RESOURCE_STRINGID(CustomSwitchesArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowInstall); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceNotFound); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceTooManyMatches); diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -118,6 +118,17 @@ namespace AppInstaller::CLI::Workflow installerArgs += ' ' + installerSwitches.at(InstallerSwitchType::Custom); } + // Construct custom arg passed in by cli arg + if (context.Args.Contains(Execution::Args::Type::CustomSwitches)) + { + std::string_view customSwitches = context.Args.GetArg(Execution::Args::Type::CustomSwitches); + // Since these arguments are appended to the installer at runtime, it doesn't make sense to append them if empty or whitespace + if (!Utility::IsEmptyOrWhitespace(customSwitches)) + { + installerArgs += ' ' + std::string{ customSwitches }; + } + } + // Construct update arg if applicable if (isUpdate && installerSwitches.find(InstallerSwitchType::Update) != installerSwitches.end()) { diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1322,6 +1322,9 @@ Please specify one of them using the --source option to proceed.</value> <data name="CountOutOfBoundsError" xml:space="preserve"> <value>The requested number of results must be between 1 and 1000.</value> </data> + <data name="CustomSwitchesArgumentDescription" xml:space="preserve"> + <value>Arguments to be passed on to the installer in addition to the defaults</value> + </data> <data name="UpgradeDifferentInstallTechnologyInNewerVersions" xml:space="preserve"> <value>A newer version was found, but the install technology is different from the current version installed. Please uninstall the package and install the newer version.</value> </data> diff --git a/src/AppInstallerCLITests/InstallFlow.cpp b/src/AppInstallerCLITests/InstallFlow.cpp @@ -825,6 +825,43 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); + // Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest and --custom argument used in cli + auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); + context.Args.AddArg(Execution::Args::Type::Silent); + context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv); + context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv); + context.Args.AddArg(Execution::Args::Type::CustomSwitches, "/MyAppendedSwitch"sv); + context.Add<Data::Manifest>(manifest); + context.Add<Data::Installer>(manifest.Installers.at(0)); + context << GetInstallerArgs; + std::string installerArgs = context.Get<Data::InstallerArgs>(); + REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest + REQUIRE(installerArgs.find("/mylog=\"MyLog.log\"") != std::string::npos); // Use declaration in manifest + REQUIRE(installerArgs.find("/mycustom") != std::string::npos); // Use declaration in manifest + REQUIRE(installerArgs.find("/myinstalldir=\"MyDir\"") != std::string::npos); // Use declaration in manifest + REQUIRE(installerArgs.find("/MyAppendedSwitch") != std::string::npos); // Use declaration from argument + } + + { + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + // Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest and whitespace-only --custom argument used in cli + auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); + context.Args.AddArg(Execution::Args::Type::Silent); + context.Args.AddArg(Execution::Args::Type::CustomSwitches, "\t"sv); + context.Add<Data::Manifest>(manifest); + context.Add<Data::Installer>(manifest.Installers.at(0)); + context << GetInstallerArgs; + std::string installerArgs = context.Get<Data::InstallerArgs>(); + REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest + REQUIRE(installerArgs.find("\t") == std::string::npos); // Whitespace only Custom switches should not be appended + } + + { + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); // Override switch specified. The whole arg passed to installer is overridden. auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); diff --git a/src/Microsoft.Management.Deployment/InstallOptions.cpp b/src/Microsoft.Management.Deployment/InstallOptions.cpp @@ -84,6 +84,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation { m_replacementInstallerArguments = value; } + hstring InstallOptions::AdditionalInstallerArguments() + { + return hstring(m_additionalInstallerArguments); + } + void InstallOptions::AdditionalInstallerArguments(hstring const& value) + { + m_additionalInstallerArguments = value; + } hstring InstallOptions::CorrelationData() { return hstring(m_correlationData); diff --git a/src/Microsoft.Management.Deployment/InstallOptions.h b/src/Microsoft.Management.Deployment/InstallOptions.h @@ -25,6 +25,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation void AllowHashMismatch(bool value); hstring ReplacementInstallerArguments(); void ReplacementInstallerArguments(hstring const& value); + hstring AdditionalInstallerArguments(); + void AdditionalInstallerArguments(hstring const& value); hstring CorrelationData(); void CorrelationData(hstring const& value); hstring AdditionalPackageCatalogArguments(); @@ -44,6 +46,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::wstring m_logOutputPath = L""; bool m_allowHashMismatch = false; std::wstring m_replacementInstallerArguments = L""; + std::wstring m_additionalInstallerArguments = L""; std::wstring m_correlationData = L""; std::wstring m_additionalPackageCatalogArguments = L""; Windows::Foundation::Collections::IVector<Windows::System::ProcessorArchitecture> m_allowedArchitectures{ diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -381,6 +381,11 @@ namespace winrt::Microsoft::Management::Deployment::implementation context->Args.AddArg(Execution::Args::Type::Override, ::AppInstaller::Utility::ConvertToUTF8(options.ReplacementInstallerArguments())); } + if (!options.AdditionalInstallerArguments().empty()) + { + context->Args.AddArg(Execution::Args::Type::CustomSwitches, ::AppInstaller::Utility::ConvertToUTF8(options.AdditionalInstallerArguments())); + } + if (options.AllowedArchitectures().Size() != 0) { std::vector<AppInstaller::Utility::Architecture> allowedArchitectures; diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -2,7 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Management.Deployment { - [contractversion(5)] + [contractversion(6)] apicontract WindowsPackageManagerContract{}; /// State of the install @@ -765,6 +765,13 @@ namespace Microsoft.Management.Deployment /// Force the operation to continue upon non security related failures. Boolean Force; } + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] + { + /// A string that will be passed to the installer + /// IMPLEMENTATION NOTE: maps to "--custom" in the winget cmd line + String AdditionalInstallerArguments; + } } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] diff --git a/src/PowerShell/Microsoft.WinGet.Client/Commands/Common/BaseInstallCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Commands/Common/BaseInstallCommand.cs @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Client.Commands.Common { using System.IO; using System.Management.Automation; - using Microsoft.Management.Deployment; + using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Helpers; using Windows.Foundation; @@ -33,6 +33,12 @@ namespace Microsoft.WinGet.Client.Commands.Common public string Override { get; set; } /// <summary> + /// Gets or sets the arguments to be passed on to the installer in addition to the defaults. + /// </summary> + [Parameter(ValueFromPipelineByPropertyName = true)] + public string Custom { get; set; } + + /// <summary> /// Gets or sets the installation location. /// </summary> [Parameter(ValueFromPipelineByPropertyName = true)] @@ -91,6 +97,12 @@ namespace Microsoft.WinGet.Client.Commands.Common options.ReplacementInstallerArguments = this.Override; } + // Since these arguments are appended to the installer at runtime, it doesn't make sense to append them if they are whitespace + if (!string.IsNullOrWhiteSpace(this.Custom)) + { + options.AdditionalInstallerArguments = this.Custom; + } + if (this.Location != null) { options.PreferredInstallLocation = this.Location;