From 9da1a11835582f557fbddd6e724292090c704ae1 Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Sat, 27 May 2017 11:54:17 -0700 Subject: [PATCH] (GH-1313) Allow properties to be passed to 'pack' Passes properties through to the nuget pack command to replace variables in the nuspec file. --- Scenarios.md | 7 +- .../scenarios/PackScenarios.cs | 66 +++++++++++++++++++ .../commands/ChocolateyPackCommandSpecs.cs | 26 ++++++++ .../commands/ChocolateyNewCommand.cs | 4 +- .../commands/ChocolateyPackCommand.cs | 31 ++++++++- .../configuration/ChocolateyConfiguration.cs | 20 ++++++ .../services/NugetService.cs | 19 +++++- 7 files changed, 165 insertions(+), 8 deletions(-) diff --git a/Scenarios.md b/Scenarios.md index 8adf82e714..a96c2be8f3 100644 --- a/Scenarios.md +++ b/Scenarios.md @@ -473,13 +473,18 @@ * should contain tags * should not contain packages and versions with a pipe between them -### ChocolateyPackCommand [ 2 Scenario(s), 4 Observation(s) ] +### ChocolateyPackCommand [ 3 Scenario(s), 6 Observation(s) ] #### when packing with an output directory * generated package should be in specified output directory * sources should be set to specified output directory +#### when packing with properties + + * generated package should be in current directory + * property settings should be logged as debug messages + #### when packing without specifying an output directory * generated package should be in current directory diff --git a/src/chocolatey.tests.integration/scenarios/PackScenarios.cs b/src/chocolatey.tests.integration/scenarios/PackScenarios.cs index 046c6b73e2..5603b1b17e 100644 --- a/src/chocolatey.tests.integration/scenarios/PackScenarios.cs +++ b/src/chocolatey.tests.integration/scenarios/PackScenarios.cs @@ -115,6 +115,54 @@ public void sources_should_be_set_to_specified_output_directory() } } + [Concern(typeof(ChocolateyPackCommand))] + public class when_packing_with_properties : ScenariosBase + { + private readonly string package_path = Path.Combine(Scenario.get_top_level(), "test-package.0.1.0.nupkg"); + + public override void Context() + { + base.Context(); + + Scenario.reset(Configuration); + + Configuration.Version = "0.1.0"; + Configuration.PackCommand.Properties.Add("commitId", "1234abcd"); + Scenario.add_files(new[] { new Tuple("myPackage.nuspec", NuspecContentWithVariables) }); + + if (File.Exists(package_path)) + { + File.Delete(package_path); + } + } + + public override void Because() + { + MockLogger.reset(); + Service.pack_run(Configuration); + } + + [Fact] + public void generated_package_should_be_in_current_directory() + { + var infos = MockLogger.MessagesFor(LogLevel.Info); + infos.Count.ShouldEqual(2); + infos[0].ShouldEqual("Attempting to build package from 'myPackage.nuspec'."); + infos[1].ShouldEqual(string.Concat("Successfully created package '", package_path, "'")); + + File.Exists(package_path).ShouldBeTrue(); + } + + [Fact] + public void property_settings_should_be_logged_as_debug_messages() + { + var messages = MockLogger.MessagesFor(LogLevel.Debug); + messages.Count.ShouldEqual(2); + messages.Contains("Setting property 'commitId': 1234abcd"); + messages.Contains("Setting property 'version': 0.1.0"); + } + } + private const string NuspecContent = @" @@ -132,5 +180,23 @@ public void sources_should_be_set_to_specified_output_directory() "; + + private const string NuspecContentWithVariables = @" + + + test-package + Test Package + $version$ + package author + package owner + A brief summary + A big description + test admin + + https://github.com/chocolatey/choco/tree/$commitId$/LICENSE + false + + +"; } } diff --git a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPackCommandSpecs.cs b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPackCommandSpecs.cs index 8d188b943c..2010ce2f2e 100644 --- a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPackCommandSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPackCommandSpecs.cs @@ -25,6 +25,7 @@ namespace chocolatey.tests.infrastructure.app.commands using chocolatey.infrastructure.commandline; using Moq; using Should; + using System; public class ChocolateyPackCommandSpecs { @@ -93,6 +94,11 @@ public override void Context() { base.Context(); unparsedArgs.Add(nuspecPath); + unparsedArgs.Add("foo=1"); + unparsedArgs.Add("bar='baz'"); + + // Make sure we storing only the first property name specified regardless of case. + unparsedArgs.Add("Foo=2"); } public override void Because() @@ -105,6 +111,26 @@ public void should_allow_a_path_to_the_nuspec_to_be_passed_in() { configuration.Input.ShouldEqual(nuspecPath); } + + [Fact] + public void should_property_foo_equal_1() + { + configuration.PackCommand.Properties["foo"].ShouldEqual("1"); + } + + [Fact] + public void should_property_bar_equal_baz() + { + configuration.PackCommand.Properties["bar"].ShouldEqual("baz"); + } + + [Fact] + public void should_log_warning_on_duplicate_foo() + { + var warnings = MockLogger.MessagesFor(LogLevel.Warn); + warnings.Count.ShouldEqual(1); + warnings[0].ShouldEqual("A value for 'foo' has already been added with the value '1'. Ignoring foo='2'.", StringComparer.OrdinalIgnoreCase); + } } public class when_noop_is_called : ChocolateyPackCommandSpecsBase diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyNewCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyNewCommand.cs index 4a885ba8ce..6fdd00079a 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyNewCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyNewCommand.cs @@ -77,7 +77,7 @@ public virtual void handle_additional_argument_parsing(IList unparsedArg { configuration.NewCommand.Name = unparsedArguments.DefaultIfEmpty(string.Empty).FirstOrDefault(arg => !arg.StartsWith("-") && !arg.contains("=")); var property = unparsedArguments.DefaultIfEmpty(string.Empty).FirstOrDefault().Split(new[] {'='}, StringSplitOptions.RemoveEmptyEntries); - if (property.Count() == 1) + if (property.Length == 1) { configuration.NewCommand.TemplateProperties.Add(TemplateValues.NamePropertyName, configuration.NewCommand.Name); } @@ -86,7 +86,7 @@ public virtual void handle_additional_argument_parsing(IList unparsedArg foreach (var unparsedArgument in unparsedArguments.or_empty_list_if_null()) { var property = unparsedArgument.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries); - if (property.Count() == 2) + if (property.Length == 2) { var propName = property[0].trim_safe(); var propValue = property[1].trim_safe().remove_surrounding_quotes(); diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs index 1f149faff7..f12abe137c 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyPackCommand.cs @@ -16,7 +16,9 @@ namespace chocolatey.infrastructure.app.commands { + using System; using System.Collections.Generic; + using System.Linq; using attributes; using commandline; using configuration; @@ -48,7 +50,27 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon public virtual void handle_additional_argument_parsing(IList unparsedArguments, ChocolateyConfiguration configuration) { - configuration.Input = string.Join(" ", unparsedArguments); + // First non-switch argument that is not a name=value pair will be treated as the nuspec file to pack. + configuration.Input = unparsedArguments.DefaultIfEmpty(string.Empty).FirstOrDefault(arg => !arg.StartsWith("-") && !arg.contains("=")); + + foreach (var unparsedArgument in unparsedArguments.or_empty_list_if_null()) + { + var property = unparsedArgument.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries); + if (property.Length == 2) + { + var propName = property[0].trim_safe(); + var propValue = property[1].trim_safe().remove_surrounding_quotes(); + + if (configuration.PackCommand.Properties.ContainsKey(propName)) + { + this.Log().Warn(() => "A value for '{0}' has already been added with the value '{1}'. Ignoring {0}='{2}'.".format_with(propName, configuration.PackCommand.Properties[propName], propValue)); + } + else + { + configuration.PackCommand.Properties.Add(propName, propValue); + } + } + } } public virtual void handle_validation(ChocolateyConfiguration configuration) @@ -67,6 +89,9 @@ with options and switches. In most cases you can still pass options and switches with one dash (`-`). For more details, see the command reference (`choco -?`). +NOTE: You can pass arbitrary property value pairs through to nuspecs. + These will replace variables formatted as `$property$` with the value passed. + 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. @@ -74,14 +99,14 @@ the command reference (`choco -?`). "chocolatey".Log().Info(ChocolateyLoggers.Important, "Usage"); "chocolatey".Log().Info(@" - choco pack [] [] + choco pack [] [] [] cpack [] [] (DEPRECATED) "); "chocolatey".Log().Info(ChocolateyLoggers.Important, "Examples"); "chocolatey".Log().Info(@" choco pack - choco pack --version 1.2.3 + choco pack --version 1.2.3 configuration=release choco pack path/to/nuspec choco pack --outputdirectory build diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index 9eaa88e223..edef848a41 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -45,6 +45,7 @@ public ChocolateyConfiguration() FeatureCommand = new FeatureCommandConfiguration(); ConfigCommand = new ConfigCommandConfiguration(); ApiKeyCommand = new ApiKeyCommandConfiguration(); + PackCommand = new PackCommandConfiguration(); PushCommand = new PushCommandConfiguration(); PinCommand = new PinCommandConfiguration(); OutdatedCommand = new OutdatedCommandConfiguration(); @@ -300,6 +301,14 @@ private void append_output(StringBuilder propertyValues, string append) /// public ApiKeyCommandConfiguration ApiKeyCommand { get; set; } + /// + /// Configuration related specifically to the Pack command. + /// + /// + /// On .NET 4.0, get error CS0200 when private set - see http://stackoverflow.com/a/23809226/18475 + /// + public PackCommandConfiguration PackCommand { get; set; } + /// /// Configuration related specifically to Push command /// @@ -496,6 +505,17 @@ public sealed class ApiKeyCommandConfiguration public string Key { get; set; } } + [Serializable] + public sealed class PackCommandConfiguration + { + public PackCommandConfiguration() + { + Properties = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public IDictionary Properties { get; private set; } + } + [Serializable] public sealed class PushCommandConfiguration { diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index cdc4b904ad..5a511a11a2 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -257,14 +257,29 @@ public string validate_and_return_package_file(ChocolateyConfiguration config, s public void pack_run(ChocolateyConfiguration config) { - string nuspecFilePath = validate_and_return_package_file(config, Constants.ManifestExtension); + var nuspecFilePath = validate_and_return_package_file(config, Constants.ManifestExtension); var nuspecDirectory = _fileSystem.get_full_path(_fileSystem.get_directory_name(nuspecFilePath)); if (string.IsNullOrWhiteSpace(nuspecDirectory)) nuspecDirectory = _fileSystem.get_current_directory(); - IDictionary properties = new Dictionary(); + // Use case-insensitive properties like "nuget pack". + var properties = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Add any other properties passed to the pack command overriding any present. + foreach (var property in config.PackCommand.Properties) + { + this.Log().Debug(() => "Setting property '{0}': {1}".format_with( + property.Key, + property.Value)); + + properties[property.Key] = property.Value; + } + // Set the version property if the flag is set if (!string.IsNullOrWhiteSpace(config.Version)) { + this.Log().Debug(() => "Setting property 'version': {0}".format_with( + config.Version)); + properties["version"] = config.Version; }