diff --git a/src/chocolatey.console/Program.cs b/src/chocolatey.console/Program.cs index 68b441834a..31157b1d50 100644 --- a/src/chocolatey.console/Program.cs +++ b/src/chocolatey.console/Program.cs @@ -30,7 +30,6 @@ namespace chocolatey.console using infrastructure.filesystem; using infrastructure.licensing; using infrastructure.logging; - using infrastructure.platforms; using infrastructure.registration; using infrastructure.services; using resources; diff --git a/src/chocolatey.resources/helpers/functions/Get-CheckSumValid.ps1 b/src/chocolatey.resources/helpers/functions/Get-CheckSumValid.ps1 index f71df1c10a..8dfb96e62f 100644 --- a/src/chocolatey.resources/helpers/functions/Get-CheckSumValid.ps1 +++ b/src/chocolatey.resources/helpers/functions/Get-CheckSumValid.ps1 @@ -20,6 +20,10 @@ param( [string] $checksumType = 'md5' ) Write-Debug "Running 'Get-ChecksumValid' with file:`'$file`', checksum: `'$checksum`', checksumType: `'$checksumType`'"; + if ($env:chocolateyIgnoreChecksums -eq 'true') { + Write-Warning "Ignoring checksums due to feature checksumFiles = false or config ignoreChecksums = true." + return + } if ($checksum -eq '' -or $checksum -eq $null) { return } if (!([System.IO.File]::Exists($file))) { throw "Unable to checksum a file that doesn't exist - Could not find file `'$file`'" } diff --git a/src/chocolatey.sln.DotSettings b/src/chocolatey.sln.DotSettings index 47a2ce71dc..dc481c1cbb 100644 --- a/src/chocolatey.sln.DotSettings +++ b/src/chocolatey.sln.DotSettings @@ -6,6 +6,7 @@ <?xml version="1.0" encoding="utf-16"?><Profile name="sensible"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>IMPLICIT_WHEN_INITIALIZER_HAS_TYPE</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSharpFormatDocComments>True</CSharpFormatDocComments><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile> sensible False + DO_NOT_CHANGE True True True diff --git a/src/chocolatey.tests/chocolatey.tests.csproj b/src/chocolatey.tests/chocolatey.tests.csproj index d079de1326..ad8b4b20f5 100644 --- a/src/chocolatey.tests/chocolatey.tests.csproj +++ b/src/chocolatey.tests/chocolatey.tests.csproj @@ -65,6 +65,7 @@ + diff --git a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyConfigCommandSpecs.cs b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyConfigCommandSpecs.cs new file mode 100644 index 0000000000..a0b1d63c32 --- /dev/null +++ b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyConfigCommandSpecs.cs @@ -0,0 +1,136 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.tests.infrastructure.app.commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Moq; + using Should; + using chocolatey.infrastructure.app.attributes; + using chocolatey.infrastructure.app.commands; + using chocolatey.infrastructure.app.configuration; + using chocolatey.infrastructure.app.domain; + using chocolatey.infrastructure.app.services; + using chocolatey.infrastructure.commandline; + + public class ChocolateyConfigCommandSpecs + { + public abstract class ChocolateyConfigCommandSpecsBase : TinySpec + { + protected ChocolateyConfigCommand command; + protected Mock configSettingsService = new Mock(); + protected ChocolateyConfiguration configuration = new ChocolateyConfiguration(); + + public override void Context() + { + command = new ChocolateyConfigCommand(configSettingsService.Object); + } + } + + public class when_implementing_command_for : ChocolateyConfigCommandSpecsBase + { + private List results; + + public override void Because() + { + results = command.GetType().GetCustomAttributes(typeof(CommandForAttribute), false).Cast().Select(a => a.CommandName).ToList(); + } + + [Fact] + public void should_implement_config() + { + results.ShouldContain(CommandNameType.config.to_string()); + } + } + + public class when_configurating_the_argument_parser : ChocolateyConfigCommandSpecsBase + { + private OptionSet optionSet; + + public override void Context() + { + base.Context(); + optionSet = new OptionSet(); + } + + public override void Because() + { + command.configure_argument_parser(optionSet, configuration); + } + + [Fact] + public void should_add_name_to_the_option_set() + { + optionSet.Contains("name").ShouldBeTrue(); + } + + [Fact] + public void should_add_value_to_the_option_set() + { + optionSet.Contains("value").ShouldBeTrue(); + } + } + + public class when_noop_is_called : ChocolateyConfigCommandSpecsBase + { + public override void Because() + { + command.noop(configuration); + } + + [Fact] + public void should_call_service_noop() + { + configSettingsService.Verify(c => c.noop(configuration), Times.Once); + } + } + + public class when_run_is_called : ChocolateyConfigCommandSpecsBase + { + private Action because; + + public override void Because() + { + because = () => command.run(configuration); + } + + [Fact] + public void should_call_service_source_list_when_command_is_list() + { + configuration.ConfigCommand.Command = ConfigCommandType.list; + because(); + configSettingsService.Verify(c => c.config_list(configuration), Times.Once); + } + + [Fact] + public void should_call_service_source_disable_when_command_is_disable() + { + configuration.ConfigCommand.Command = ConfigCommandType.get; + because(); + configSettingsService.Verify(c => c.config_get(configuration), Times.Once); + } + + [Fact] + public void should_call_service_source_enable_when_command_is_enable() + { + configuration.ConfigCommand.Command = ConfigCommandType.set; + because(); + configSettingsService.Verify(c => c.config_set(configuration), Times.Once); + } + } + } +} diff --git a/src/chocolatey/StringExtensions.cs b/src/chocolatey/StringExtensions.cs index 1cdbce2f38..62ab04ff7e 100644 --- a/src/chocolatey/StringExtensions.cs +++ b/src/chocolatey/StringExtensions.cs @@ -113,6 +113,18 @@ public static bool is_equal_to(this string input, string other) return string.Compare(input, other, ignoreCase: true, culture: CultureInfo.InvariantCulture) == 0; } + /// + /// Determines whether a string value contains a search value. + /// + /// The input. + /// The value to search for. + /// The comparison. + /// True if the value to search for is in the input string + public static bool contains(this string input, string search, StringComparison comparison = StringComparison.OrdinalIgnoreCase) + { + return input.to_string().IndexOf(search, 0, comparison) >= 0; + } + /// /// Removes quotes or apostrophes surrounding a string /// diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 467a02df80..0cf1733747 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -79,6 +79,7 @@ + @@ -92,9 +93,11 @@ + + @@ -285,4 +288,4 @@ --> - \ No newline at end of file + diff --git a/src/chocolatey/infrastructure.app/ApplicationParameters.cs b/src/chocolatey/infrastructure.app/ApplicationParameters.cs index 35f0ae91ea..5733edfcf2 100644 --- a/src/chocolatey/infrastructure.app/ApplicationParameters.cs +++ b/src/chocolatey/infrastructure.app/ApplicationParameters.cs @@ -84,6 +84,13 @@ public static class Tools public static readonly string ShimGenExe = _fileSystem.combine_paths(InstallLocation, "tools", "shimgen.exe"); } + public static class ConfigSettings + { + public static readonly string CacheLocation = "cacheLocation"; + public static readonly string ContainsLegacyPackageInstalls = "containsLegacyPackageInstalls"; + public static readonly string CommandExecutionTimeoutSeconds = "commandExecutionTimeoutSeconds"; + } + public static class Features { public static readonly string CheckSumFiles = "checksumFiles"; diff --git a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs index e0ed4215c5..fba140317c 100644 --- a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs +++ b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs @@ -1,12 +1,12 @@ // Copyright © 2011 - Present RealDimensions Software, LLC -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. -// +// // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -73,7 +73,15 @@ private static void set_file_configuration(ChocolateyConfiguration config, IFile var configFileSettings = xmlService.deserialize(globalConfigPath); var sources = new StringBuilder(); - foreach (var source in configFileSettings.Sources.Where(s => !s.Disabled).or_empty_list_if_null()) + + var defaultSourcesInOrder = configFileSettings.Sources.Where(s => !s.Disabled).or_empty_list_if_null().ToList(); + if (configFileSettings.Sources.Any(s => s.Priority > 0)) + { + defaultSourcesInOrder = configFileSettings.Sources.Where(s => !s.Disabled && s.Priority != 0).OrderBy(s => s.Priority).or_empty_list_if_null().ToList(); + defaultSourcesInOrder.AddRange(configFileSettings.Sources.Where(s => !s.Disabled && s.Priority == 0 ).or_empty_list_if_null().ToList()); + } + + foreach (var source in defaultSourcesInOrder) { sources.AppendFormat("{0};", source.Value); } @@ -84,24 +92,13 @@ private static void set_file_configuration(ChocolateyConfiguration config, IFile set_machine_sources(config, configFileSettings); - config.CacheLocation = !string.IsNullOrWhiteSpace(configFileSettings.CacheLocation) ? configFileSettings.CacheLocation : System.Environment.GetEnvironmentVariable("TEMP"); - if (string.IsNullOrWhiteSpace(config.CacheLocation)) - { - config.CacheLocation = fileSystem.combine_paths(ApplicationParameters.InstallLocation, "temp"); - } + set_config_items(config, configFileSettings, fileSystem); FaultTolerance.try_catch_with_logging_exception( () => fileSystem.create_directory_if_not_exists(config.CacheLocation), "Could not create temp directory at '{0}'".format_with(config.CacheLocation), logWarningInsteadOfError: true); - config.ContainsLegacyPackageInstalls = configFileSettings.ContainsLegacyPackageInstalls; - if (configFileSettings.CommandExecutionTimeoutSeconds <= 0) - { - configFileSettings.CommandExecutionTimeoutSeconds = ApplicationParameters.DefaultWaitForExitInSeconds; - } - config.CommandExecutionTimeoutSeconds = configFileSettings.CommandExecutionTimeoutSeconds; - set_feature_flags(config, configFileSettings); // save so all updated configuration items get set to existing config @@ -120,26 +117,88 @@ private static void set_machine_sources(ChocolateyConfiguration config, ConfigFi Key = source.Value, Name = source.Id, Username = source.UserName, - EncryptedPassword = source.Password + EncryptedPassword = source.Password, + Priority = source.Priority }); } } + private static void set_config_items(ChocolateyConfiguration config, ConfigFileSettings configFileSettings, IFileSystem fileSystem) + { + var cacheLocation = set_config_item(ApplicationParameters.ConfigSettings.CacheLocation, configFileSettings, string.IsNullOrWhiteSpace(configFileSettings.CacheLocation) ? string.Empty : configFileSettings.CacheLocation, "Cache location if not TEMP folder."); + config.CacheLocation = !string.IsNullOrWhiteSpace(cacheLocation) ? cacheLocation : System.Environment.GetEnvironmentVariable("TEMP"); + if (string.IsNullOrWhiteSpace(config.CacheLocation)) + { + config.CacheLocation = fileSystem.combine_paths(ApplicationParameters.InstallLocation, "temp"); + } + + var originalCommandTimeout = configFileSettings.CommandExecutionTimeoutSeconds; + var commandExecutionTimeoutSeconds = -1; + int.TryParse( + set_config_item( + ApplicationParameters.ConfigSettings.CommandExecutionTimeoutSeconds, + configFileSettings, + originalCommandTimeout == 0 ? + ApplicationParameters.DefaultWaitForExitInSeconds.to_string() + : originalCommandTimeout.to_string(), + "Default timeout for command execution."), + out commandExecutionTimeoutSeconds); + config.CommandExecutionTimeoutSeconds = commandExecutionTimeoutSeconds; + if (configFileSettings.CommandExecutionTimeoutSeconds <= 0) + { + set_config_item(ApplicationParameters.ConfigSettings.CommandExecutionTimeoutSeconds, configFileSettings, ApplicationParameters.DefaultWaitForExitInSeconds.to_string(), "Default timeout for command execution.", forceSettingValue: true); + config.CommandExecutionTimeoutSeconds = ApplicationParameters.DefaultWaitForExitInSeconds; + } + + config.ContainsLegacyPackageInstalls = set_config_item(ApplicationParameters.ConfigSettings.ContainsLegacyPackageInstalls, configFileSettings, "true", "Install has packages installed prior to 0.9.9 series.").is_equal_to(bool.TrueString); + } + + private static string set_config_item(string configName, ConfigFileSettings configFileSettings, string defaultValue, string description, bool forceSettingValue = false) + { + var config = configFileSettings.ConfigSettings.FirstOrDefault(f => f.Key.is_equal_to(configName)); + if (config == null) + { + config = new ConfigFileConfigSetting + { + Key = configName, + Value = defaultValue, + Description = description + }; + + configFileSettings.ConfigSettings.Add(config); + } + if (forceSettingValue) + { + config.Value = defaultValue; + } + + config.Description = description; + + return config.Value; + } + private static void set_feature_flags(ChocolateyConfiguration config, ConfigFileSettings configFileSettings) { - config.Features.CheckSumFiles = set_feature_flag(ApplicationParameters.Features.CheckSumFiles, configFileSettings, defaultEnabled: true); - config.Features.AutoUninstaller = set_feature_flag(ApplicationParameters.Features.AutoUninstaller, configFileSettings, defaultEnabled: true); - config.Features.FailOnAutoUninstaller = set_feature_flag(ApplicationParameters.Features.FailOnAutoUninstaller, configFileSettings, defaultEnabled: false); - config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false); + config.Features.CheckSumFiles = set_feature_flag(ApplicationParameters.Features.CheckSumFiles, configFileSettings, defaultEnabled: true, "Checksum files when pulled in from internet (based on package)."); + config.Features.AutoUninstaller = set_feature_flag(ApplicationParameters.Features.AutoUninstaller, configFileSettings, defaultEnabled: true, "Uninstall from programs and features without requiring an explicit uninstall script."); + config.Features.FailOnAutoUninstaller = set_feature_flag(ApplicationParameters.Features.FailOnAutoUninstaller, configFileSettings, defaultEnabled: false, "Fail if automatic uninstaller fails."); + config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false, "Prompt for confirmation in scripts or bypass."); } - private static bool set_feature_flag(string featureName, ConfigFileSettings configFileSettings, bool defaultEnabled) + private static bool set_feature_flag(string featureName, ConfigFileSettings configFileSettings, bool defaultEnabled, string description) { var feature = configFileSettings.Features.FirstOrDefault(f => f.Name.is_equal_to(featureName)); if (feature == null) { - configFileSettings.Features.Add(new ConfigFileFeatureSetting {Name = featureName, Enabled = defaultEnabled}); + feature = new ConfigFileFeatureSetting + { + Name = featureName, + Enabled = defaultEnabled, + Description = description + }; + + configFileSettings.Features.Add(feature); } else { @@ -149,6 +208,8 @@ private static bool set_feature_flag(string featureName, ConfigFileSettings conf } } + feature.Description = description; + return feature != null ? feature.Enabled : defaultEnabled; } @@ -219,7 +280,7 @@ private static void set_global_options(IList args, ChocolateyConfigurati "chocolatey".Log().Info(@" {0} -Please run chocolatey with `choco command -help` for specific help on +Please run chocolatey with `choco command -help` for specific help on each command. ".format_with(commandsLog.ToString())); "chocolatey".Log().Info(ChocolateyLoggers.Important, @"How To Pass Options / Switches"); @@ -228,24 +289,24 @@ each command. * `-`, `/`, or `--` (one character switches should not use `--`) * **Option Bundling / Bundled Options**: One character switches can be - bundled. e.g. `-d` (debug), `-f` (force), `-v` (verbose), and `-y` + bundled. e.g. `-d` (debug), `-f` (force), `-v` (verbose), and `-y` (confirm yes) can be bundled as `-dfvy`. - * ***Note:*** If `debug` or `verbose` are bundled with local options + * ***Note:*** If `debug` or `verbose` are bundled with local options (not the global ones above), some logging may not show up until after the local options are parsed. - * **Use Equals**: You can also include or not include an equals sign + * **Use Equals**: You can also include or not include an equals sign `=` between options and values. - * **Quote Values**: When you need to quote things, such as when using - spaces, please use apostrophes (`'value'`). In cmd.exe you may be - able to use just double quotes (`""value""`) but in powershell.exe - you may need to either escape the quotes with backticks - (`` `""value`"" ``) or use a combination of double quotes and - apostrophes (`""'value'""`). This is due to the hand off to + * **Quote Values**: When you need to quote things, such as when using + spaces, please use apostrophes (`'value'`). In cmd.exe you may be + able to use just double quotes (`""value""`) but in powershell.exe + you may need to either escape the quotes with backticks + (`` `""value`"" ``) or use a combination of double quotes and + apostrophes (`""'value'""`). This is due to the hand off to PowerShell - it seems to strip off the outer set of quotes. - * Options and switches apply to all items passed, so if you are - installing multiple packages, and you use `--version=1.0.0`, choco - is going to look for and try to install version 1.0.0 of every - package passed. So please split out multiple package calls when + * Options and switches apply to all items passed, so if you are + installing multiple packages, and you use `--version=1.0.0`, choco + is going to look for and try to install version 1.0.0 of every + package passed. So please split out multiple package calls when wanting to pass specific options. "); "chocolatey".Log().Info(ChocolateyLoggers.Important, "Default Options and Switches"); @@ -266,4 +327,4 @@ private static void set_environment_options(ChocolateyConfiguration config) config.Information.IsProcessElevated = ProcessInformation.process_is_elevated(); } } -} \ No newline at end of file +} diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs new file mode 100644 index 0000000000..dcc86652b5 --- /dev/null +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs @@ -0,0 +1,141 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using attributes; + using commandline; + using configuration; + using domain; + using infrastructure.commands; + using logging; + using services; + + [CommandFor(CommandNameType.config)] + public sealed class ChocolateyConfigCommand : ICommand + { + private readonly IChocolateyConfigSettingsService _configSettingsService; + + public ChocolateyConfigCommand(IChocolateyConfigSettingsService configSettingsService) + { + _configSettingsService = configSettingsService; + } + + public void configure_argument_parser(OptionSet optionSet, ChocolateyConfiguration configuration) + { + configuration.Sources = string.Empty; + + optionSet + .Add( + "name=", + "Name - the name of the config setting. Required with some actions. Defaults to empty.", + option => configuration.ConfigCommand.Name = option.remove_surrounding_quotes()) + .Add( + "value=", + "Value - the value of the config setting. Required with some actions. Defaults to empty.", + option => configuration.ConfigCommand.ConfigValue = option.remove_surrounding_quotes()) + ; + } + + public void handle_additional_argument_parsing(IList unparsedArguments, ChocolateyConfiguration configuration) + { + configuration.Input = string.Join(" ", unparsedArguments); + var command = ConfigCommandType.unknown; + string unparsedCommand = unparsedArguments.DefaultIfEmpty(string.Empty).FirstOrDefault().to_string().Replace("-",string.Empty); + Enum.TryParse(unparsedCommand, true, out command); + if (command == ConfigCommandType.unknown) + { + if (!string.IsNullOrWhiteSpace(unparsedCommand)) this.Log().Warn("Unknown command {0}. Setting to list.".format_with(unparsedCommand)); + command = ConfigCommandType.list; + } + + configuration.ConfigCommand.Command = command; + + if ((configuration.ConfigCommand.Command == ConfigCommandType.list + || !string.IsNullOrWhiteSpace(configuration.ConfigCommand.Name) + ) + && unparsedArguments.Count > 1) throw new ApplicationException("A single features command must be listed. Please see the help menu for those commands"); + + if (string.IsNullOrWhiteSpace(configuration.ConfigCommand.Name) && unparsedArguments.Count >=2) + { + configuration.ConfigCommand.Name = unparsedArguments[1]; + } + if (string.IsNullOrWhiteSpace(configuration.ConfigCommand.ConfigValue) && unparsedArguments.Count >= 3) + { + configuration.ConfigCommand.ConfigValue = unparsedArguments[2]; + } + } + + public void handle_validation(ChocolateyConfiguration configuration) + { + if (configuration.ConfigCommand.Command != ConfigCommandType.list && string.IsNullOrWhiteSpace(configuration.ConfigCommand.Name)) throw new ApplicationException("When specifying the subcommand '{0}', you must also specify --name by option or position.".format_with(configuration.ConfigCommand.Command.to_string())); + if (configuration.ConfigCommand.Command == ConfigCommandType.set && string.IsNullOrWhiteSpace(configuration.ConfigCommand.ConfigValue)) throw new ApplicationException("When specifying the subcommand '{0}', you must also specify --value by option or position.".format_with(configuration.ConfigCommand.Command.to_string())); + } + + public void help_message(ChocolateyConfiguration configuration) + { + this.Log().Info(ChocolateyLoggers.Important, "Config Command"); + this.Log().Info(@" +Chocolatey will allow you to interact with the configuration file settings. +"); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Usage"); + "chocolatey".Log().Info(@" + choco config [list]|get|set [] +"); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Examples"); + "chocolatey".Log().Info(@" + choco config + choco config list + choco config get cacheLocation + choco config get --name cacheLocation + choco config set cacheLocation c:\temp\choco + choco config set --name cacheLocation --value c:\temp\choco +"); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Options and Switches"); + } + + public void noop(ChocolateyConfiguration configuration) + { + _configSettingsService.noop(configuration); + } + + public void run(ChocolateyConfiguration configuration) + { + switch (configuration.ConfigCommand.Command) + { + case ConfigCommandType.list: + _configSettingsService.config_list(configuration); + break; + case ConfigCommandType.get: + _configSettingsService.config_get(configuration); + break; + case ConfigCommandType.set: + _configSettingsService.config_set(configuration); + break; + } + } + + public bool may_require_admin_access() + { + return true; + } + } +} diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs index 3aaee56679..e63eead688 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs @@ -83,6 +83,12 @@ public void configure_argument_parser(OptionSet optionSet, ChocolateyConfigurati .Add("p=|password=", "Password - the user's password to the source. Defaults to empty.", option => configuration.SourceCommand.Password = option.remove_surrounding_quotes()) + .Add("ignorechecksums|ignore-checksums", + "IgnoreChecksums - Ignore checksums provided by the package", + option => + { + if (option != null) configuration.Features.CheckSumFiles = false; + }) ; //todo: Checksum / ChecksumType defaults to md5 / package name can be a url / installertype diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs index adfac294f7..3a76134a76 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs @@ -58,12 +58,16 @@ public void help_message(ChocolateyConfiguration configuration) this.Log().Info(@" Chocolatey will attempt to package a nuspec into a compiled nupkg. Some may prefer to use `cpack` as a shortcut for `choco pack`. + +NOTE: `cpack` has been deprecated as it has a name collision with CMake. Please + use `choco pack` instead. The shortcut will be removed in v1. + "); "chocolatey".Log().Info(ChocolateyLoggers.Important, "Usage"); "chocolatey".Log().Info(@" choco pack [] [] - cpack [] [] + cpack [] [] (DEPRECATED) "); "chocolatey".Log().Info(ChocolateyLoggers.Important, "Examples"); diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs index baca7f45c4..d82876812a 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs @@ -53,7 +53,10 @@ public void configure_argument_parser(OptionSet optionSet, ChocolateyConfigurati option => configuration.SourceCommand.Username = option.remove_surrounding_quotes()) .Add("p=|password=", "Password - the user's password to the source. Encrypted in chocolatey.config file.", - option => configuration.SourceCommand.Password = option.remove_surrounding_quotes()) + option => configuration.SourceCommand.Password = option.remove_surrounding_quotes()) + .Add("priority=", + "Priority - The priority order of this source as compared to other sources, lower is better. Defaults to 0 (no priority). All priorities above 0 will be evaluated first, then zero-based values will be evaluated in config file order.", + option => configuration.SourceCommand.Priority = int.Parse(option.remove_surrounding_quotes())) ; } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs index 399d50703c..edd510f6e5 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs @@ -88,6 +88,12 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("p=|password=", "Password - the user's password to the source. Defaults to empty.", option => configuration.SourceCommand.Password = option.remove_surrounding_quotes()) + .Add("ignorechecksums|ignore-checksums", + "IgnoreChecksums - Ignore checksums provided by the package", + option => + { + if (option != null) configuration.Features.CheckSumFiles = false; + }) ; } diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index 1484a42393..ee01b1bd99 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -42,6 +42,7 @@ public ChocolateyConfiguration() SourceCommand = new SourcesCommandConfiguration(); MachineSources = new List(); FeatureCommand = new FeatureCommandConfiguration(); + ConfigCommand = new ConfigCommandConfiguration(); ApiKeyCommand = new ApiKeyCommandConfiguration(); PushCommand = new PushCommandConfiguration(); PinCommand = new PinCommandConfiguration(); @@ -55,7 +56,7 @@ public override string ToString() { var properties = new StringBuilder(); - this.Log().Debug(ChocolateyLoggers.Important,@" + this.Log().Debug(ChocolateyLoggers.Important, @" NOTE: Hiding sensitive configuration data! Please double and triple check to be sure no sensitive data is shown, especially if copying output to a gist for review."); @@ -69,7 +70,7 @@ private void output_tostring(StringBuilder propertyValues, IEnumerable public SourcesCommandConfiguration SourceCommand { get; set; } + /// /// Default Machine Sources Configuration /// @@ -266,7 +268,15 @@ private void append_output(StringBuilder propertyValues, string append) /// /// On .NET 4.0, get error CS0200 when private set - see http://stackoverflow.com/a/23809226/18475 /// - public FeatureCommandConfiguration FeatureCommand { get; set; } + public FeatureCommandConfiguration FeatureCommand { get; set; } + + /// + /// Configuration related to the configuration file. + /// + /// + /// On .NET 4.0, get error CS0200 when private set - see http://stackoverflow.com/a/23809226/18475 + /// + public ConfigCommandConfiguration ConfigCommand { get; set; } /// /// Configuration related specifically to ApiKey command @@ -326,8 +336,8 @@ public sealed class ListCommandConfiguration // list public bool LocalOnly { get; set; } public bool IncludeRegistryPrograms { get; set; } - } - + } + [Serializable] public sealed class UpgradeCommandConfiguration { @@ -357,6 +367,7 @@ public sealed class SourcesCommandConfiguration public SourceCommandType Command { get; set; } public string Username { get; set; } public string Password { get; set; } + public int Priority { get; set; } } [Serializable] @@ -366,6 +377,7 @@ public sealed class MachineSourceConfiguration public string Key { get; set; } public string Username { get; set; } public string EncryptedPassword { get; set; } + public int Priority { get; set; } } [Serializable] @@ -373,6 +385,14 @@ public sealed class FeatureCommandConfiguration { public string Name { get; set; } public FeatureCommandType Command { get; set; } + } + + [Serializable] + public sealed class ConfigCommandConfiguration + { + public string Name { get; set; } + public string ConfigValue { get; set; } + public ConfigCommandType Command { get; set; } } [Serializable] diff --git a/src/chocolatey/infrastructure.app/configuration/ConfigFileConfigSetting.cs b/src/chocolatey/infrastructure.app/configuration/ConfigFileConfigSetting.cs new file mode 100644 index 0000000000..0f88350f49 --- /dev/null +++ b/src/chocolatey/infrastructure.app/configuration/ConfigFileConfigSetting.cs @@ -0,0 +1,37 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.configuration +{ + using System; + using System.Xml.Serialization; + + /// + /// XML config file config element + /// + [Serializable] + [XmlType("add")] + public sealed class ConfigFileConfigSetting + { + [XmlAttribute(AttributeName = "key")] + public string Key { get; set; } + + [XmlAttribute(AttributeName = "value")] + public string Value { get; set; } + + [XmlAttribute(AttributeName = "description")] + public string Description { get; set; } + } +} diff --git a/src/chocolatey/infrastructure.app/configuration/ConfigFileFeatureSetting.cs b/src/chocolatey/infrastructure.app/configuration/ConfigFileFeatureSetting.cs index fefbc1b127..c5cf1715c1 100644 --- a/src/chocolatey/infrastructure.app/configuration/ConfigFileFeatureSetting.cs +++ b/src/chocolatey/infrastructure.app/configuration/ConfigFileFeatureSetting.cs @@ -33,5 +33,8 @@ public sealed class ConfigFileFeatureSetting [XmlAttribute(AttributeName = "setExplicitly")] public bool SetExplicitly { get; set; } + + [XmlAttribute(AttributeName = "description")] + public string Description { get; set; } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/configuration/ConfigFileSettings.cs b/src/chocolatey/infrastructure.app/configuration/ConfigFileSettings.cs index 355804ac06..d23730f9af 100644 --- a/src/chocolatey/infrastructure.app/configuration/ConfigFileSettings.cs +++ b/src/chocolatey/infrastructure.app/configuration/ConfigFileSettings.cs @@ -34,7 +34,10 @@ public class ConfigFileSettings [XmlElement(ElementName = "commandExecutionTimeoutSeconds")] public int CommandExecutionTimeoutSeconds { get; set; } - + + [XmlArray("config")] + public HashSet ConfigSettings { get; set; } + [XmlArray("sources")] public HashSet Sources { get; set; } diff --git a/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs b/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs index e4e26a09ba..b3d0d4b856 100644 --- a/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs +++ b/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs @@ -38,6 +38,9 @@ public sealed class ConfigFileSourceSetting public string UserName { get; set; } [XmlAttribute(AttributeName = "password")] - public string Password { get; set; } + public string Password { get; set; } + + [XmlAttribute(AttributeName = "priority")] + public int Priority { get; set; } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/configuration/chocolatey.config b/src/chocolatey/infrastructure.app/configuration/chocolatey.config index 03881ef5ea..9c70d8d353 100644 --- a/src/chocolatey/infrastructure.app/configuration/chocolatey.config +++ b/src/chocolatey/infrastructure.app/configuration/chocolatey.config @@ -1,7 +1,10 @@  - - true + + + + + diff --git a/src/chocolatey/infrastructure.app/domain/CommandNameType.cs b/src/chocolatey/infrastructure.app/domain/CommandNameType.cs index d2e54a032f..03fc056d07 100644 --- a/src/chocolatey/infrastructure.app/domain/CommandNameType.cs +++ b/src/chocolatey/infrastructure.app/domain/CommandNameType.cs @@ -43,5 +43,7 @@ public enum CommandNameType [Description("apikey - retrieves or saves an apikey for a particular source")] apikey, [Description("setapikey - retrieves or saves an apikey for a particular source (alias for apikey)")] setapikey, + [Description("config - Retrieve and configure config file settings")] + config, } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/ConfigCommandType.cs b/src/chocolatey/infrastructure.app/domain/ConfigCommandType.cs new file mode 100644 index 0000000000..aeb3a5a749 --- /dev/null +++ b/src/chocolatey/infrastructure.app/domain/ConfigCommandType.cs @@ -0,0 +1,25 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.domain +{ + public enum ConfigCommandType + { + unknown, + list, + get, + set, + } +} diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index 2620ac7ff5..34ff70eece 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -80,6 +80,7 @@ public void RegisterComponents(Container container) new ChocolateyPushCommand(container.GetInstance(), container.GetInstance()), new ChocolateyNewCommand(container.GetInstance()), new ChocolateySourceCommand(container.GetInstance()), + new ChocolateyConfigCommand(container.GetInstance()), new ChocolateyFeatureCommand(container.GetInstance()), new ChocolateyApiKeyCommand(container.GetInstance()), new ChocolateyUnpackSelfCommand(container.GetInstance()), diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs index 41a3e8e9b0..9093ec0d55 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs @@ -1,12 +1,12 @@ // Copyright © 2011 - Present RealDimensions Software, LLC -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. -// +// // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,6 +20,7 @@ namespace chocolatey.infrastructure.app.services using System.Linq; using configuration; using infrastructure.services; + using logging; using nuget; internal class ChocolateyConfigSettingsService : IChocolateyConfigSettingsService @@ -49,8 +50,13 @@ public IEnumerable source_list(ChocolateyConfiguration configu var list = new List(); foreach (var source in configFileSettings.Sources) { - if (configuration.RegularOutput) { - this.Log().Info(() => "{0}{1} - {2}".format_with(source.Id, source.Disabled ? " [Disabled]" : string.Empty, source.Value)); + if (configuration.RegularOutput) { + this.Log().Info(() => "{0}{1} - {2} {3}| Priority {4}.".format_with( + source.Id, + source.Disabled ? " [Disabled]" : string.Empty, + source.Value, + string.IsNullOrWhiteSpace(source.UserName) ? string.Empty : "(Authenticated)", + source.Priority)); } list.Add(new ChocolateySource { Id = source.Id, @@ -67,23 +73,40 @@ public void source_add(ChocolateyConfiguration configuration) var source = configFileSettings.Sources.FirstOrDefault(p => p.Id.is_equal_to(configuration.SourceCommand.Name)); if (source == null) { - configFileSettings.Sources.Add(new ConfigFileSourceSetting - { - Id = configuration.SourceCommand.Name, - Value = configuration.Sources, - UserName = configuration.SourceCommand.Username, - Password = NugetEncryptionUtility.EncryptString(configuration.SourceCommand.Password), - }); + source = new ConfigFileSourceSetting + { + Id = configuration.SourceCommand.Name, + Value = configuration.Sources, + UserName = configuration.SourceCommand.Username, + Password = NugetEncryptionUtility.EncryptString(configuration.SourceCommand.Password), + Priority = configuration.SourceCommand.Priority + }; + configFileSettings.Sources.Add(source); _xmlService.serialize(configFileSettings, ApplicationParameters.GlobalConfigFileLocation); - - this.Log().Warn(() => "Added {0} - {1}".format_with(configuration.SourceCommand.Name, configuration.Sources)); + this.Log().Warn(() => "Added {0} - {1} (Priority {2})".format_with(source.Id, source.Value, source.Priority)); } else { - this.Log().Warn(@" -No changes made. If you are trying to change an existing source, please - remove it first."); + var currentPassword = string.IsNullOrWhiteSpace(source.Password) ? null : NugetEncryptionUtility.DecryptString(source.Password); + if (configuration.Sources.is_equal_to(source.Value) && + configuration.SourceCommand.Priority == source.Priority && + configuration.SourceCommand.Username.is_equal_to(source.UserName) && + configuration.SourceCommand.Password.is_equal_to(currentPassword) + ) + { + this.Log().Warn(NO_CHANGE_MESSAGE); + } + else + { + source.Value = configuration.Sources; + source.Priority = configuration.SourceCommand.Priority; + source.UserName = configuration.SourceCommand.Username; + source.Password = NugetEncryptionUtility.EncryptString(configuration.SourceCommand.Password); + + _xmlService.serialize(configFileSettings, ApplicationParameters.GlobalConfigFileLocation); + this.Log().Warn(() => "Updated {0} - {1} (Priority {2})".format_with(source.Id, source.Value, source.Priority)); + } } } @@ -137,7 +160,7 @@ public void feature_list(ChocolateyConfiguration configuration) { foreach (var feature in configFileSettings.Features) { - this.Log().Info(() => "{0} - {1}".format_with(feature.Name, !feature.Enabled ? "[Disabled]" : "[Enabled]")); + this.Log().Info(() => "{0} - {1} | {2}".format_with(feature.Name, !feature.Enabled ? "[Disabled]" : "[Enabled]", feature.Description)); } } @@ -247,10 +270,89 @@ public void set_api_key(ChocolateyConfiguration configuration) _xmlService.serialize(configFileSettings, ApplicationParameters.GlobalConfigFileLocation); this.Log().Info(() => "Updated ApiKey for {0}".format_with(configuration.Sources)); } - else + else this.Log().Warn(NO_CHANGE_MESSAGE); + } + } + + public void config_list(ChocolateyConfiguration configuration) + { + this.Log().Info(ChocolateyLoggers.Important, "Settings"); + foreach (var config in configFileSettings.ConfigSettings) + { + this.Log().Info(() => "{0} = {1} | {2}".format_with(config.Key, config.Value, config.Description)); + } + + this.Log().Info(""); + this.Log().Info(ChocolateyLoggers.Important, "Sources"); + source_list(configuration); + this.Log().Info(""); + this.Log().Info(@"NOTE: Use choco source to interact with sources."); + this.Log().Info(""); + this.Log().Info(ChocolateyLoggers.Important, "Features"); + feature_list(configuration); + this.Log().Info(""); + this.Log().Info(@"NOTE: Use choco feature to interact with features."); + ; + this.Log().Info(""); + this.Log().Info(ChocolateyLoggers.Important, "API Keys"); + this.Log().Info(@"NOTE: Api Keys are not shown through this command. + Use choco apikey to interact with API keys."); + } + + public void config_get(ChocolateyConfiguration configuration) + { + var config = config_get(configuration.ConfigCommand.Name); + if (config == null) throw new ApplicationException("No configuration value by the name '{0}'".format_with(configuration.ConfigCommand.Name)); + this.Log().Info("{0}".format_with(config.Value)); + } + + public ConfigFileConfigSetting config_get(string configKeyName) + { + var config = configFileSettings.ConfigSettings.FirstOrDefault(p => p.Key.is_equal_to(configKeyName)); + if (config == null) return null; + + return config; + } + + public void config_set(ChocolateyConfiguration configuration) + { + var encryptValue = configuration.ConfigCommand.Name.contains("password"); + var config = config_get(configuration.ConfigCommand.Name); + var configValue = encryptValue + ? NugetEncryptionUtility.EncryptString(configuration.ConfigCommand.ConfigValue) + : configuration.ConfigCommand.ConfigValue; + + if (config == null) + { + var setting = new ConfigFileConfigSetting + { + Key = configuration.ConfigCommand.Name, + Value = configValue, + }; + + configFileSettings.ConfigSettings.Add(setting); + + _xmlService.serialize(configFileSettings, ApplicationParameters.GlobalConfigFileLocation); + + this.Log().Warn(() => "Added {0} = {1}".format_with(setting.Key, setting.Value)); + } + else + { + var currentValue = encryptValue && !string.IsNullOrWhiteSpace(config.Value) + ? NugetEncryptionUtility.DecryptString(config.Value) + : config.Value; + + if (configuration.ConfigCommand.ConfigValue.is_equal_to(currentValue.to_string())) { this.Log().Warn(NO_CHANGE_MESSAGE); } + else + { + config.Value = configValue; + _xmlService.serialize(configFileSettings, ApplicationParameters.GlobalConfigFileLocation); + + this.Log().Warn(() => "Updated {0} = {1}".format_with(config.Key, config.Value)); + } } } } diff --git a/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs index 80628a4281..16aa416cd0 100644 --- a/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs +++ b/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs @@ -32,5 +32,8 @@ public interface IChocolateyConfigSettingsService void feature_enable(ChocolateyConfiguration configuration); string get_api_key(ChocolateyConfiguration configuration, Action keyAction); void set_api_key(ChocolateyConfiguration configuration); + void config_list(ChocolateyConfiguration configuration); + void config_get(ChocolateyConfiguration configuration); + void config_set(ChocolateyConfiguration configuration); } } diff --git a/src/chocolatey/infrastructure.app/services/PowershellService.cs b/src/chocolatey/infrastructure.app/services/PowershellService.cs index 44d3937686..6df127fa7a 100644 --- a/src/chocolatey/infrastructure.app/services/PowershellService.cs +++ b/src/chocolatey/infrastructure.app/services/PowershellService.cs @@ -208,6 +208,10 @@ public bool run_action(ChocolateyConfiguration configuration, PackageResult pack if (configuration.Verbose) { Environment.SetEnvironmentVariable("ChocolateyEnvironmentVerbose", "true"); + } + if (!configuration.Features.CheckSumFiles) + { + Environment.SetEnvironmentVariable("ChocolateyIgnoreChecksums", "true"); } //todo:if (configuration.NoOutput) //{