Skip to content

Commit

Permalink
(chocolateyGH-1313) Allow properties to be passed to 'pack'
Browse files Browse the repository at this point in the history
Passes properties through to the nuget pack command to replace variables
in the nuspec file.
  • Loading branch information
heaths committed May 27, 2017
1 parent 237de6a commit cb3471c
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 2 deletions.
7 changes: 6 additions & 1 deletion Scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
66 changes: 66 additions & 0 deletions src/chocolatey.tests.integration/scenarios/PackScenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.PackCommand.Properties.Add("version", "0.1.0");
Configuration.PackCommand.Properties.Add("commitId", "1234abcd");
Scenario.add_files(new[] { new Tuple<string, string>("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("Overriding property 'version': 0.1.0");
messages.Contains("Setting property 'commitId': 1234abcd");
}
}

private const string NuspecContent = @"<?xml version=""1.0"" encoding=""utf-8""?>
<package xmlns=""http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"">
<metadata>
Expand All @@ -132,5 +180,23 @@ public void sources_should_be_set_to_specified_output_directory()
<releaseNotes></releaseNotes>
</metadata>
</package>";

private const string NuspecContentWithVariables = @"<?xml version=""1.0"" encoding=""utf-8""?>
<package xmlns=""http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"">
<metadata>
<id>test-package</id>
<title>Test Package</title>
<version>$version$</version>
<authors>package author</authors>
<owners>package owner</owners>
<summary>A brief summary</summary>
<description>A big description</description>
<tags>test admin</tags>
<copyright></copyright>
<licenseUrl>https://github.com/chocolatey/choco/tree/$commitId$/LICENSE</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes></releaseNotes>
</metadata>
</package>";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace chocolatey.tests.infrastructure.app.commands
using chocolatey.infrastructure.commandline;
using Moq;
using Should;
using System;

public class ChocolateyPackCommandSpecs
{
Expand Down Expand Up @@ -82,6 +83,12 @@ public void should_add_outputdirectory_to_the_option_set()
{
optionSet.Contains("outputdirectory").ShouldBeTrue();
}

[Fact]
public void should_add_properties_to_the_option_set()
{
optionSet.Contains("properties").ShouldBeTrue();
}
}

public class when_handling_additional_argument_parsing : ChocolateyPackCommandSpecsBase
Expand Down Expand Up @@ -148,7 +155,7 @@ public override void Context()

public override void Because()
{
optionSet.Parse(new[] { "--version", "0.42.0", "--outputdirectory", "c:\\packages" });
optionSet.Parse(new[] { "--version", "0.42.0", "--outputdirectory", "c:\\packages", "--properties", "foo=1;bar='baz'" });
}

[Fact]
Expand All @@ -162,6 +169,58 @@ public void should_outputdirectory_equal_packages()
{
configuration.OutputDirectory.ShouldEqual("c:\\packages");
}

[Fact]
public void should_properties_contain_foo()
{
configuration.PackCommand.Properties.ShouldContain(new KeyValuePair<string, string>("foo", "1"));
}

[Fact]
public void should_properties_contain_bar()
{
configuration.PackCommand.Properties.ShouldContain(new KeyValuePair<string, string>("bar", "baz"));
}
}

public class when_handling_arguments_parsing_properties_error : ChocolateyPackCommandSpecsBase
{
private OptionSet optionSet;
private Action because;

public override void Context()
{
base.Context();
optionSet = new OptionSet();
command.configure_argument_parser(optionSet, configuration);
}

public override void Because()
{
because = () => optionSet.Parse(new[] { "--properties", "foo" });
}

[Fact]
public void should_throw_on_ill_formed_property()
{
var errored = false;
Exception error = null;

try
{
because();
}
catch (Exception ex)
{
errored = true;
error = ex;
}

errored.ShouldBeTrue();
error.ShouldNotBeNull();
error.ShouldBeType<ArgumentException>();
error.Message.ShouldContain("Ill-formed name=value pair in token: foo");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace chocolatey.infrastructure.app.commands
using infrastructure.commands;
using logging;
using services;
using System;

[CommandFor("pack", "packages up a nuspec to a compiled nupkg")]
public class ChocolateyPackCommand : ICommand
Expand All @@ -43,6 +44,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon
.Add("out=|outdir=|outputdirectory=|output-directory=",
"OutputDirectory - Specifies the directory for the created Chocolatey package file. If not specified, uses the current directory.",
option => configuration.OutputDirectory = option)
.Add("properties=",
"Properties - A semicolon-delimited list of name=value pairs that will replace $name$ with value in a nuspec.",
option => parse_properties(option, configuration.PackCommand.Properties))
;
}

Expand Down Expand Up @@ -104,5 +108,25 @@ public virtual bool may_require_admin_access()
{
return false;
}

private void parse_properties(string input, IDictionary<string, string> properties)
{
input = input.remove_surrounding_quotes();
if (string.IsNullOrWhiteSpace(input)) return;

var tokens = input.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var token in tokens)
{
var pair = token.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (pair.Length != 2)
{
throw new ArgumentException(
string.Format("Ill-formed name=value pair in token: {0}", token),
"properties");
}

properties.Add(pair[0], pair[1].remove_surrounding_quotes());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -300,6 +301,14 @@ private void append_output(StringBuilder propertyValues, string append)
/// </remarks>
public ApiKeyCommandConfiguration ApiKeyCommand { get; set; }

/// <summary>
/// Configuration related specifically to the Pack command.
/// </summary>
/// <remarks>
/// On .NET 4.0, get error CS0200 when private set - see http://stackoverflow.com/a/23809226/18475
/// </remarks>
public PackCommandConfiguration PackCommand { get; set; }

/// <summary>
/// Configuration related specifically to Push command
/// </summary>
Expand Down Expand Up @@ -496,6 +505,17 @@ public sealed class ApiKeyCommandConfiguration
public string Key { get; set; }
}

[Serializable]
public sealed class PackCommandConfiguration
{
public PackCommandConfiguration()
{
Properties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}

public IDictionary<string, string> Properties { get; private set; }
}

[Serializable]
public sealed class PushCommandConfiguration
{
Expand Down
11 changes: 11 additions & 0 deletions src/chocolatey/infrastructure.app/services/NugetService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ public void pack_run(ChocolateyConfiguration config)
properties["version"] = config.Version;
}

// Add any other properties passed to the pack command overriding any present.
foreach (var property in config.PackCommand.Properties)
{
this.Log().Debug(() => "{0} property '{1}': {2}".format_with(
properties.ContainsKey(property.Key) ? "Overriding" : "Setting",
property.Key,
property.Value));

properties[property.Key] = property.Value;
}

// Initialize the property provider based on what was passed in using the properties flag
var propertyProvider = new DictionaryPropertyProvider(properties);

Expand Down

0 comments on commit cb3471c

Please sign in to comment.