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:
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;