From 45cd1a41423098589cb26b1bbec73f4365484ee2 Mon Sep 17 00:00:00 2001 From: TheCakeIsNaOH Date: Fri, 27 Jan 2023 21:54:37 -0600 Subject: [PATCH 1/3] (#508) Make package id comparisons case insensitive This helps ensure that package IDs are handled in a case insensitive manner. --- src/chocolatey/infrastructure.app/services/NugetService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 7e4251846e..0f177f52b1 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -571,7 +571,7 @@ public virtual ConcurrentDictionary Install(ChocolateyCon if (packageNames.Count == 1) { var packageName = packageNames.DefaultIfEmpty(string.Empty).FirstOrDefault(); - if (packageName.EndsWith(NuGetConstants.PackageExtension) || packageName.EndsWith(PackagingConstants.ManifestExtension)) + if (packageName.EndsWith(NuGetConstants.PackageExtension, StringComparison.OrdinalIgnoreCase) || packageName.EndsWith(PackagingConstants.ManifestExtension, StringComparison.OrdinalIgnoreCase)) { this.Log().Warn(ChocolateyLoggers.Important, "DEPRECATION WARNING"); this.Log().Warn(InstallWithFilePathDeprecationMessage); @@ -581,7 +581,7 @@ public virtual ConcurrentDictionary Install(ChocolateyCon config.Sources = _fileSystem.GetDirectoryName(_fileSystem.GetFullPath(packageName)); - if (packageName.EndsWith(PackagingConstants.ManifestExtension)) + if (packageName.EndsWith(PackagingConstants.ManifestExtension, StringComparison.OrdinalIgnoreCase)) { packageNames.Add(_fileSystem.GetFilenameWithoutExtension(packageName)); @@ -631,7 +631,7 @@ public virtual ConcurrentDictionary Install(ChocolateyCon var installedPackage = allLocalPackages.FirstOrDefault(p => p.Name.IsEqualTo(packageName)); - if (Platform.GetPlatform() != PlatformType.Windows && !packageName.EndsWith(".template")) + if (Platform.GetPlatform() != PlatformType.Windows && !packageName.EndsWith(".template", StringComparison.OrdinalIgnoreCase)) { var logMessage = "{0} is not a supported package on non-Windows systems.{1}Only template packages are currently supported.".FormatWith(packageName, Environment.NewLine); this.Log().Warn(ChocolateyLoggers.Important, logMessage); From dc243a811cedde2f0811aa9af575f9ba11efc085 Mon Sep 17 00:00:00 2001 From: TheCakeIsNaOH Date: Fri, 27 Jan 2023 21:54:50 -0600 Subject: [PATCH 2/3] (#2761) Allow overriding remembered arguments This allows overriding of remembered package parameters, install arguments, cache location and execution timeout during upgrade. So a user can pass in different package parameters or arguments without having to completely reinstall the package or turn off remembered arguments. Switch arguments cannot be checked, because the lack of a switch normally would mean that they are just relying on the remembered args to remember it, so there is no way to determine if an override of the remembered args is appropriate. --- .../builders/ConfigurationBuilder.cs | 7 ++++++- .../configuration/ChocolateyConfiguration.cs | 2 ++ .../services/NugetService.cs | 20 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs index 3d7be60ae5..15d87c37ba 100644 --- a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs +++ b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs @@ -420,11 +420,16 @@ private static void SetGlobalOptions(IList args, ChocolateyConfiguration if (timeout > 0 || timeoutString.IsEqualTo("0")) { config.CommandExecutionTimeoutSeconds = timeout; + config.CommandExecutionTimeoutSecondsArgumentWasPassed = true; } }) .Add("c=|cache=|cachelocation=|cache-location=", "CacheLocation - Location for download cache, defaults to %TEMP% or value in chocolatey.config file.", - option => config.CacheLocation = option.UnquoteSafe()) + option => + { + config.CacheLocation = option.UnquoteSafe(); + config.CacheLocationArgumentWasPassed = true; + }) .Add("allowunofficial|allow-unofficial|allowunofficialbuild|allow-unofficial-build", "AllowUnofficialBuild - When not using the official build you must set this flag for choco to continue.", option => config.AllowUnofficialBuild = option != null) diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index 9954353c49..0f3314e96e 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -252,8 +252,10 @@ private void AppendOutput(StringBuilder propertyValues, string append) // configuration set variables public string CacheLocation { get; set; } + public bool CacheLocationArgumentWasPassed { get; set; } public int CommandExecutionTimeoutSeconds { get; set; } + public bool CommandExecutionTimeoutSecondsArgumentWasPassed { get; set; } public int WebRequestTimeoutSeconds { get; set; } public string DefaultTemplateName { get; set; } diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 0f177f52b1..2025d25125 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -1968,6 +1968,16 @@ protected virtual ChocolateyConfiguration SetConfigFromRememberedArguments(Choco ConfigurationOptions.OptionSet.Parse(packageArguments); // there may be overrides from the user running upgrade + if (!string.IsNullOrWhiteSpace(originalConfig.PackageParameters)) + { + config.PackageParameters = originalConfig.PackageParameters; + } + + if (!string.IsNullOrWhiteSpace(originalConfig.InstallArguments)) + { + config.InstallArguments = originalConfig.InstallArguments; + } + if (!string.IsNullOrWhiteSpace(originalConfig.SourceCommand.Username)) { config.SourceCommand.Username = originalConfig.SourceCommand.Username; @@ -1988,6 +1998,16 @@ protected virtual ChocolateyConfiguration SetConfigFromRememberedArguments(Choco config.SourceCommand.CertificatePassword = originalConfig.SourceCommand.CertificatePassword; } + if (originalConfig.CacheLocationArgumentWasPassed && !string.IsNullOrWhiteSpace(originalConfig.CacheLocation)) + { + config.CacheLocation = originalConfig.CacheLocation; + } + + if (originalConfig.CommandExecutionTimeoutSecondsArgumentWasPassed) + { + config.CommandExecutionTimeoutSeconds = originalConfig.CommandExecutionTimeoutSeconds; + } + return originalConfig; } From 0907c0778a62802e84914f857e24276f2c14f497 Mon Sep 17 00:00:00 2001 From: TheCakeIsNaOH Date: Fri, 27 Jan 2023 21:57:12 -0600 Subject: [PATCH 3/3] (#2886) Switch remembered args to only change local configuration This adds a new method, GetPackageConfigFromRememberedArguments which is similar to SetConfigFromRememberedArguments, but operates using a different method. First, a OptionSet is set up, so only the config that is passed in is modified, instead of the config that the global options parser was set with with. Second, it returns the modified configuration instead of the original configuration, because it is the local configuration being modified. Third, it has a more general name and changes log messages to be more general, so it later can more easily be reused for uninstall and export. This change fixes remembered arguments when Chocolatey is used as a library, like in ChocolateyGUI, where the config that is passed to the install_run method is not necessarily the same config object that was used to set up the global argument parser. The downside of using a new commandline parser is that it opens up the possibility of drift between the upgrade/global arguments and this added parser. However, this is not an issue because the format of the saved arguments is known, and any added arguments there would not work without being added here as well, which would be picked up during development. --- .../services/INugetService.cs | 11 ++ .../services/NugetService.cs | 178 ++++++++++++++++-- 2 files changed, 175 insertions(+), 14 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/INugetService.cs b/src/chocolatey/infrastructure.app/services/INugetService.cs index a7a3a30f35..9ea50e0078 100644 --- a/src/chocolatey/infrastructure.app/services/INugetService.cs +++ b/src/chocolatey/infrastructure.app/services/INugetService.cs @@ -18,6 +18,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using chocolatey.infrastructure.app.configuration; +using chocolatey.infrastructure.app.domain; using chocolatey.infrastructure.results; namespace chocolatey.infrastructure.app.services @@ -67,6 +68,16 @@ public interface INugetService : ISourceRunner /// The configuration IEnumerable GetInstalledPackages(ChocolateyConfiguration config); + + /// + /// Gets the configuration from remembered arguments + /// + /// The original configuration. + /// The package information. + /// The modified configuration, so it can be used + ChocolateyConfiguration GetPackageConfigFromRememberedArguments(ChocolateyConfiguration config, + ChocolateyPackageInformation packageInfo); + #pragma warning disable IDE0022, IDE1006 [Obsolete("This overload is deprecated and will be removed in v3.")] ConcurrentDictionary get_outdated(ChocolateyConfiguration config); diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 2025d25125..6821d2a4b6 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -1216,7 +1216,7 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon continue; } - SetConfigFromRememberedArguments(config, pkgInfo); + config = GetPackageConfigFromRememberedArguments(config, pkgInfo); var pathResolver = NugetCommon.GetPathResolver(_fileSystem); var nugetProject = new FolderNuGetProject(ApplicationParameters.PackagesLocation, pathResolver, NuGetFramework.AnyFramework); @@ -1354,12 +1354,12 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon var logMessage = "{0} is pinned. Skipping pinned package.".FormatWith(packageName); packageResult.Messages.Add(new ResultMessage(ResultType.Warn, logMessage)); packageResult.Messages.Add(new ResultMessage(ResultType.Inconclusive, logMessage)); - + if (config.RegularOutput) { this.Log().Warn(ChocolateyLoggers.Important, logMessage); } - + continue; } else @@ -1371,7 +1371,7 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon { this.Log().Warn(ChocolateyLoggers.Important, logMessage); } - + config.PinPackage = true; } } @@ -1453,7 +1453,7 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon } var removedSources = new HashSet(); - + if (!config.UpgradeCommand.IgnorePinned) { removedSources.AddRange(RemovePinnedSourceDependencies(sourcePackageDependencyInfos, allLocalPackages)); @@ -1937,12 +1937,7 @@ public virtual ConcurrentDictionary GetOutdated(Chocolate return outdatedPackages; } - /// - /// Sets the configuration for the package upgrade - /// - /// The configuration. - /// The package information. - /// The original unmodified configuration, so it can be reset after upgrade + [Obsolete("This method is deprecated and will be removed in v3.")] protected virtual ChocolateyConfiguration SetConfigFromRememberedArguments(ChocolateyConfiguration config, ChocolateyPackageInformation packageInfo) { if (!config.Features.UseRememberedArgumentsForUpgrades || string.IsNullOrWhiteSpace(packageInfo.Arguments)) @@ -1968,16 +1963,16 @@ protected virtual ChocolateyConfiguration SetConfigFromRememberedArguments(Choco ConfigurationOptions.OptionSet.Parse(packageArguments); // there may be overrides from the user running upgrade - if (!string.IsNullOrWhiteSpace(originalConfig.PackageParameters)) + if (!string.IsNullOrWhiteSpace(originalConfig.PackageParameters)) { config.PackageParameters = originalConfig.PackageParameters; } - + if (!string.IsNullOrWhiteSpace(originalConfig.InstallArguments)) { config.InstallArguments = originalConfig.InstallArguments; } - + if (!string.IsNullOrWhiteSpace(originalConfig.SourceCommand.Username)) { config.SourceCommand.Username = originalConfig.SourceCommand.Username; @@ -2011,6 +2006,161 @@ protected virtual ChocolateyConfiguration SetConfigFromRememberedArguments(Choco return originalConfig; } + /// + /// Gets the configuration from remembered arguments + /// + /// The original configuration. + /// The package information. + /// The modified configuration, so it can be used + public virtual ChocolateyConfiguration GetPackageConfigFromRememberedArguments(ChocolateyConfiguration config, ChocolateyPackageInformation packageInfo) + { + if (!config.Features.UseRememberedArgumentsForUpgrades || string.IsNullOrWhiteSpace(packageInfo.Arguments)) + { + return config; + } + + var packageArgumentsUnencrypted = packageInfo.Arguments.ContainsSafe(" --") && packageInfo.Arguments.ToStringSafe().Length > 4 ? packageInfo.Arguments : NugetEncryptionUtility.DecryptString(packageInfo.Arguments); + + var sensitiveArgs = true; + if (!ArgumentsUtility.SensitiveArgumentsProvided(packageArgumentsUnencrypted)) + { + sensitiveArgs = false; + this.Log().Debug(ChocolateyLoggers.Verbose, "{0} - Adding remembered arguments: {1}".FormatWith(packageInfo.Package.Id, packageArgumentsUnencrypted.EscapeCurlyBraces())); + } + + var packageArgumentsSplit = packageArgumentsUnencrypted.Split(new[] { " --" }, StringSplitOptions.RemoveEmptyEntries); + var packageArguments = new List(); + foreach (var packageArgument in packageArgumentsSplit.OrEmpty()) + { + var packageArgumentSplit = packageArgument.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries); + var optionName = packageArgumentSplit[0].ToStringSafe(); + var optionValue = string.Empty; + if (packageArgumentSplit.Length == 2) + { + optionValue = packageArgumentSplit[1].ToStringSafe().UnquoteSafe(); + if (optionValue.StartsWith("'")) + { + optionValue.UnquoteSafe(); + } + } + + if (sensitiveArgs) + { + this.Log().Debug(ChocolateyLoggers.Verbose, "{0} - Adding '{1}' to arguments. Values not shown due to detected sensitive arguments".FormatWith(packageInfo.Package.Id, optionName.EscapeCurlyBraces())); + } + packageArguments.Add("--{0}{1}".FormatWith(optionName, string.IsNullOrWhiteSpace(optionValue) ? string.Empty : "=" + optionValue)); + } + + var originalConfig = config.DeepCopy(); + var rememberedOptionSet = new OptionSet(); + + rememberedOptionSet + .Add("pre|prerelease", + "Prerelease - Include Prereleases? Defaults to false.", + option => config.Prerelease = option != null) + .Add("i|ignoredependencies|ignore-dependencies", + "IgnoreDependencies - Ignore dependencies when installing package(s). Defaults to false.", + option => config.IgnoreDependencies = option != null) + .Add("x86|forcex86", + "ForceX86 - Force x86 (32bit) installation on 64 bit systems. Defaults to false.", + option => config.ForceX86 = option != null) + .Add("ia=|installargs=|install-args=|installarguments=|install-arguments=", + "InstallArguments - Install Arguments to pass to the native installer in the package. Defaults to unspecified.", + option => config.InstallArguments = option.UnquoteSafe()) + .Add("o|override|overrideargs|overridearguments|override-arguments", + "OverrideArguments - Should install arguments be used exclusively without appending to current package passed arguments? Defaults to false.", + option => config.OverrideArguments = option != null) + .Add("argsglobal|args-global|installargsglobal|install-args-global|applyargstodependencies|apply-args-to-dependencies|apply-install-arguments-to-dependencies", + "Apply Install Arguments To Dependencies - Should install arguments be applied to dependent packages? Defaults to false.", + option => config.ApplyInstallArgumentsToDependencies = option != null) + .Add("params=|parameters=|pkgparameters=|packageparameters=|package-parameters=", + "PackageParameters - Parameters to pass to the package. Defaults to unspecified.", + option => config.PackageParameters = option.UnquoteSafe()) + .Add("paramsglobal|params-global|packageparametersglobal|package-parameters-global|applyparamstodependencies|apply-params-to-dependencies|apply-package-parameters-to-dependencies", + "Apply Package Parameters To Dependencies - Should package parameters be applied to dependent packages? Defaults to false.", + option => config.ApplyPackageParametersToDependencies = option != null) + .Add("allowdowngrade|allow-downgrade", + "AllowDowngrade - Should an attempt at downgrading be allowed? Defaults to false.", + option => config.AllowDowngrade = option != null) + .Add("u=|user=", + "User - used with authenticated feeds. Defaults to empty.", + option => config.SourceCommand.Username = option.UnquoteSafe()) + .Add("p=|password=", + "Password - the user's password to the source. Defaults to empty.", + option => config.SourceCommand.Password = option.UnquoteSafe()) + .Add("cert=", + "Client certificate - PFX pathname for an x509 authenticated feeds. Defaults to empty. Available in 0.9.10+.", + option => config.SourceCommand.Certificate = option.UnquoteSafe()) + .Add("cp=|certpassword=", + "Certificate Password - the client certificate's password to the source. Defaults to empty. Available in 0.9.10+.", + option => config.SourceCommand.CertificatePassword = option.UnquoteSafe()) + .Add("timeout=|execution-timeout=", + "CommandExecutionTimeout (in seconds) - The time to allow a command to finish before timing out. Overrides the default execution timeout in the configuration of {0} seconds. '0' for infinite starting in 0.10.4.".FormatWith(config.CommandExecutionTimeoutSeconds.ToString()), + option => + { + var timeout = 0; + var timeoutString = option.UnquoteSafe(); + int.TryParse(timeoutString, out timeout); + if (timeout > 0 || timeoutString.IsEqualTo("0")) + { + config.CommandExecutionTimeoutSeconds = timeout; + } + }) + .Add("c=|cache=|cachelocation=|cache-location=", + "CacheLocation - Location for download cache, defaults to %TEMP% or value in chocolatey.config file.", + option => config.CacheLocation = option.UnquoteSafe()) + .Add("use-system-powershell", + "UseSystemPowerShell - Execute PowerShell using an external process instead of the built-in PowerShell host. Should only be used when internal host is failing. Available in 0.9.10+.", + option => config.Features.UsePowerShellHost = option != null); + + rememberedOptionSet.Parse(packageArguments); + + // there may be overrides from the user running upgrade + if (!string.IsNullOrWhiteSpace(originalConfig.PackageParameters)) + { + config.PackageParameters = originalConfig.PackageParameters; + } + + if (!string.IsNullOrWhiteSpace(originalConfig.InstallArguments)) + { + config.InstallArguments = originalConfig.InstallArguments; + } + + if (!string.IsNullOrWhiteSpace(originalConfig.SourceCommand.Username)) + { + config.SourceCommand.Username = originalConfig.SourceCommand.Username; + } + + if (!string.IsNullOrWhiteSpace(originalConfig.SourceCommand.Password)) + { + config.SourceCommand.Password = originalConfig.SourceCommand.Password; + } + + if (!string.IsNullOrWhiteSpace(originalConfig.SourceCommand.Certificate)) + { + config.SourceCommand.Certificate = originalConfig.SourceCommand.Certificate; + } + + if (!string.IsNullOrWhiteSpace(originalConfig.SourceCommand.CertificatePassword)) + { + config.SourceCommand.CertificatePassword = originalConfig.SourceCommand.CertificatePassword; + } + + if (originalConfig.CacheLocationArgumentWasPassed && !string.IsNullOrWhiteSpace(originalConfig.CacheLocation)) + { + config.CacheLocation = originalConfig.CacheLocation; + } + + if (originalConfig.CommandExecutionTimeoutSecondsArgumentWasPassed) + { + config.CommandExecutionTimeoutSeconds = originalConfig.CommandExecutionTimeoutSeconds; + } + + // We can't override switches because we don't know here if they were set on the command line + + return config; + } + private bool HasMissingDependency(PackageResult package, List allLocalPackages) { foreach (var dependency in package.PackageMetadata.DependencyGroups.SelectMany(d => d.Packages))