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/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 7e4251846e..6821d2a4b6 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); @@ -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,6 +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)) + { + 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,9 +1993,174 @@ 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; } + /// + /// 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))