Skip to content

Commit

Permalink
Add nuke temp file command line argument provider.
Browse files Browse the repository at this point in the history
Change ParameterService to singleton.
Add get positional argument to parameter service.
Add IEnumerable.SingleOrDefaultOrError. Update FlluentAssertions.
  • Loading branch information
arodus committed Mar 22, 2018
1 parent cfc43c6 commit d11072b
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 22 deletions.
31 changes: 31 additions & 0 deletions source/Nuke.Core.Tests/EnumerableExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright Matthias Koch, Sebastian Karasek 2018.
// Distributed under the MIT License.
// https://github.com/nuke-build/nuke/blob/master/LICENSE

using System;
using System.Linq;
using FluentAssertions;
using Nuke.Core.Utilities.Collections;
using Xunit;

namespace Nuke.Core.Tests
{
public class EnumerableExtensionsTest
{
[Fact]
public void SingleOrDefaultOrError_ThrowsExceptionWithMessage()
{
var x = new[] { "a", "a" };
Action a = () => x.SingleOrDefaultOrError("error");
a.Should().Throw<InvalidOperationException>().WithMessage("error");
}

[Theory]
[InlineData(new[] { "a", "b", "c" }, "a", "a")]
[InlineData(new[] { "a", "b", "c" }, "d", null)]
public void SingleOrDefaultOrError(string[] enumerable, string equalsWith, string expectedValue)
{
enumerable.SingleOrDefaultOrError(x => x.Equals(equalsWith), "error").Should().Be(expectedValue);
}
}
}
2 changes: 1 addition & 1 deletion source/Nuke.Core.Tests/Nuke.Core.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Import Project="..\..\shared\Configuration.props" />

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="4.19.2" />
<PackageReference Include="FluentAssertions" Version="5.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170601-03" />
<PackageReference Include="xunit" Version="2.3.0-beta2-build3683" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" />
Expand Down
11 changes: 11 additions & 0 deletions source/Nuke.Core.Tests/ParameterServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,16 @@ public void TestConversionCollections()
service.GetParameter<string[]>("files").Should().BeEquivalentTo("C:\\new folder\\file.txt", "C:\\file.txt");
service.GetParameter<string[]>("values", separator: '+').Should().BeEquivalentTo("A", "B", "C");
}

[Theory]
[InlineData(new[] { "arg1" }, 0, typeof(string), "arg1")]
[InlineData(new[] { "true" }, 0, typeof(bool), true)]
[InlineData(new[] { "arg1" }, 1, typeof(string), null)]
[InlineData(new[] { "arg1", "arg2" }, 1, typeof(string), "arg2")]
public void TestPositionalCommandLineArguments(string[] commandLineArgs, int position, Type destinationType, object expectedValue)
{
var service = GetService(commandLineArgs);
service.GetCommandLineArgument(position, destinationType).Should().Be(expectedValue);
}
}
}
6 changes: 3 additions & 3 deletions source/Nuke.Core/EnvironmentInfo.Internal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ internal static string[] InvokedTargets
{
get
{
var argument = Environment.GetCommandLineArgs()
.Skip(count: 1).Take(count: 1)
.SingleOrDefault(x => !x.StartsWith("-"));
var argument = ParameterService.Instance.GetCommandLineArgument<string>(position: 1);
argument = argument == null || argument.StartsWith("-") ? null : argument;

if (argument != null)
return argument.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries);

Expand Down
26 changes: 12 additions & 14 deletions source/Nuke.Core/EnvironmentInfo.Parameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ namespace Nuke.Core
[DebuggerStepThrough]
public static partial class EnvironmentInfo
{
private static readonly ParameterService s_parameterService = new ParameterService();

public static void SetVariable(string name, string value)
{
Environment.SetEnvironmentVariable(name, value);
Expand All @@ -36,7 +34,7 @@ public static void SetVariable<T>(string name, IEnumerable<T> values, char separ
/// </summary>
public static bool ParameterSwitch(string name)
{
return s_parameterService.GetParameter<bool>(name);
return ParameterService.Instance.GetParameter<bool>(name);
}

/// <summary>
Expand All @@ -45,7 +43,7 @@ public static bool ParameterSwitch(string name)
[CanBeNull]
public static string Parameter(string name)
{
return s_parameterService.GetParameter<string>(name);
return ParameterService.Instance.GetParameter<string>(name);
}

/// <summary>
Expand All @@ -54,7 +52,7 @@ public static string Parameter(string name)
[CanBeNull]
public static T Parameter<T>(string name)
{
return s_parameterService.GetParameter<T>(name);
return ParameterService.Instance.GetParameter<T>(name);
}

/// <summary>
Expand All @@ -63,7 +61,7 @@ public static T Parameter<T>(string name)
[CanBeNull]
public static T[] ParameterSet<T>(string name, char? separator = null)
{
return s_parameterService.GetParameter<T[]>(name, separator);
return ParameterService.Instance.GetParameter<T[]>(name, separator);
}

/// <summary>
Expand Down Expand Up @@ -99,7 +97,7 @@ public static T[] EnsureParameterSet<T>(string name, char? separator = null)
/// </summary>
public static bool VariableSwitch(string name)
{
return s_parameterService.GetEnvironmentVariable<bool>(name);
return ParameterService.Instance.GetEnvironmentVariable<bool>(name);
}

/// <summary>
Expand All @@ -108,7 +106,7 @@ public static bool VariableSwitch(string name)
[CanBeNull]
public static string Variable(string name)
{
return s_parameterService.GetEnvironmentVariable<string>(name);
return ParameterService.Instance.GetEnvironmentVariable<string>(name);
}

/// <summary>
Expand All @@ -117,7 +115,7 @@ public static string Variable(string name)
[CanBeNull]
public static T Variable<T>(string name)
{
return s_parameterService.GetEnvironmentVariable<T>(name);
return ParameterService.Instance.GetEnvironmentVariable<T>(name);
}

/// <summary>
Expand All @@ -126,7 +124,7 @@ public static T Variable<T>(string name)
[CanBeNull]
public static T[] VariableSet<T>(string name, char? separator = null)
{
return s_parameterService.GetEnvironmentVariable<T[]>(name, separator);
return ParameterService.Instance.GetEnvironmentVariable<T[]>(name, separator);
}

/// <summary>
Expand Down Expand Up @@ -162,7 +160,7 @@ public static T[] EnsureVariableSet<T>(string name, char? separator = null)
/// </summary>
public static bool ArgumentSwitch(string name)
{
return s_parameterService.GetCommandLineArgument<bool>(name);
return ParameterService.Instance.GetCommandLineArgument<bool>(name);
}

/// <summary>
Expand All @@ -171,7 +169,7 @@ public static bool ArgumentSwitch(string name)
[CanBeNull]
public static string Argument(string name)
{
return s_parameterService.GetCommandLineArgument<string>(name);
return ParameterService.Instance.GetCommandLineArgument<string>(name);
}

/// <summary>
Expand All @@ -180,7 +178,7 @@ public static string Argument(string name)
[CanBeNull]
public static T Argument<T>(string name)
{
return s_parameterService.GetCommandLineArgument<T>(name);
return ParameterService.Instance.GetCommandLineArgument<T>(name);
}

/// <summary>
Expand All @@ -189,7 +187,7 @@ public static T Argument<T>(string name)
[CanBeNull]
public static T[] ArgumentSet<T>(string name, char? separator = null)
{
return s_parameterService.GetCommandLineArgument<T[]>(name, separator);
return ParameterService.Instance.GetCommandLineArgument<T[]>(name, separator);
}

/// <summary>
Expand Down
62 changes: 62 additions & 0 deletions source/Nuke.Core/Execution/NukeTempfileArgumentProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Nuke.Core.Utilities.Collections;

namespace Nuke.Core.Execution
{
public static class NukeTempfileArgumentProvider
{
[CanBeNull] private static string[] s_arguments;
[CanBeNull] private static readonly FileInfo s_tempConfigFile;

static NukeTempfileArgumentProvider()
{
try
{
s_tempConfigFile = new FileInfo(EnvironmentInfo.BuildDirectory / "nuke.tmp");
}
catch (Exception)
{
s_tempConfigFile = null;
}
}

public static bool IsAvailable
{
get
{
if (s_tempConfigFile == null || !s_tempConfigFile.Exists) return false;
if (s_tempConfigFile.LastWriteTime.AddMinutes(value: 1) >= DateTime.Now) return true;

s_tempConfigFile.Delete();
return false;
}
}

public static string[] Execute()
{
if (s_arguments != null) return s_arguments;

var args = new List<string> { nameof(NukeTempfileArgumentProvider) };
args.AddRange(ReadAndDeleteTempFile().Split(' '));
return s_arguments = args.ToArray();
}

private static string ReadAndDeleteTempFile()
{
ControlFlow.Assert(IsAvailable, "nuke.tmp file was deleted");

var lines = File.ReadAllLines(s_tempConfigFile.NotNull().FullName);

s_tempConfigFile.Delete();

var configLine = lines.ToList()
.SingleOrDefaultOrError(x => !string.IsNullOrWhiteSpace(x), "nuke.tmp must not contain more than one line.");
ControlFlow.Assert(configLine != null, "No configuration values found");
return configLine;
}
}
}
42 changes: 41 additions & 1 deletion source/Nuke.Core/Execution/ParameterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Nuke.Core.Utilities;
Expand All @@ -15,6 +16,8 @@ namespace Nuke.Core.Execution
{
public class ParameterService
{
private static ParameterService s_instance;

private readonly Func<string[]> _commandLineArgumentsProvider;
private readonly Func<IDictionary> _environmentVariablesProvider;

Expand All @@ -25,6 +28,18 @@ public ParameterService(Func<string[]> commandLineArgumentsProvider = null, Func
_commandLineArgumentsProvider = commandLineArgumentsProvider ?? Environment.GetCommandLineArgs;
}

public static ParameterService Instance
{
get
{
if (s_instance != null) return s_instance;
Func<string[]> commandLineArgumentsProvider = null;
if (NukeTempfileArgumentProvider.IsAvailable) commandLineArgumentsProvider = NukeTempfileArgumentProvider.Execute;

return s_instance = new ParameterService(commandLineArgumentsProvider);
}
}

[CanBeNull]
public T GetParameter<T>(string parameterName, char? separator = null)
{
Expand All @@ -37,6 +52,12 @@ public T GetCommandLineArgument<T>(string parameterName, char? separator = null)
return (T) GetCommandLineArgument(parameterName, typeof(T), separator);
}

[CanBeNull]
public T GetCommandLineArgument<T>(int position, char? separator = null)
{
return (T) GetCommandLineArgument(position, typeof(T), separator);
}

[CanBeNull]
public T GetEnvironmentVariable<T>(string parameterName, char? separator = null)
{
Expand All @@ -60,6 +81,25 @@ public object GetCommandLineArgument(string argumentName, Type destinationType,
return GetDefaultValue(destinationType);

var values = args.Skip(index + 1).TakeWhile(x => !x.StartsWith("-")).ToArray();
return ConvertCommandLineArguments(argumentName, values, destinationType, args, separator);
}

[CanBeNull]
public object GetCommandLineArgument(int position, Type destinationType, char? separator = null)
{
var args = _commandLineArgumentsProvider.Invoke();
if (args.Length <= position) return null;
return ConvertCommandLineArguments($"positional[{position}]", new[] { args[position] }, destinationType, args, separator);
}

[CanBeNull]
public object ConvertCommandLineArguments(
string argumentName,
string[] values,
Type destinationType,
string[] commandLineArguments,
char? separator = null)
{
ControlFlow.Assert(values.Length == 1 || !separator.HasValue || values.All(x => !x.Contains(separator.Value)),
$"Command-line argument '{argumentName}' with value [ {values.JoinComma()} ] cannot be split with separator '{separator}'.");
values = separator.HasValue && values.Any(x => x.Contains(separator.Value))
Expand All @@ -74,7 +114,7 @@ public object GetCommandLineArgument(string argumentName, Type destinationType,
{
ControlFlow.Fail(
new[] { ex.Message, "Command-line arguments were:" }
.Concat(args.Select((x, i) => $" [{i}] = {x}"))
.Concat(commandLineArguments.Select((x, i) => $" [{i}] = {x}"))
.JoinNewLine());
// ReSharper disable once HeuristicUnreachableCode
return null;
Expand Down
4 changes: 1 addition & 3 deletions source/Nuke.Core/ParameterAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ namespace Nuke.Core
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
public class ParameterAttribute : InjectionAttributeBase
{
private static readonly ParameterService s_parameterService = new ParameterService();

public ParameterAttribute(string description = null)
{
Description = description;
Expand All @@ -56,7 +54,7 @@ public override object GetValue(string memberName, Type memberType)
!memberType.IsArray
? typeof(Nullable<>).MakeGenericType(memberType)
: memberType;
return s_parameterService.GetParameter(Name ?? memberName, memberType, (Separator ?? string.Empty).SingleOrDefault());
return ParameterService.Instance.GetParameter(Name ?? memberName, memberType, (Separator ?? string.Empty).SingleOrDefault());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;

namespace Nuke.Core.Utilities.Collections
{

public static partial class EnumerableExtensions
{
public static T SingleOrDefaultOrError<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate, string message)
{
if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
if (string.IsNullOrEmpty(message)) throw new ArgumentException("message must not be null or empty", nameof(message));


try
{
return enumerable.SingleOrDefault(predicate);
}
catch (InvalidOperationException ex)
{
throw new InvalidOperationException(message, ex);
}
}

public static T SingleOrDefaultOrError<T>(this IEnumerable<T> enumerable, string message)
{
if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));
if (string.IsNullOrEmpty(message)) throw new ArgumentException("message must not be null or empty", nameof(message));

try
{
return enumerable.SingleOrDefault();
}
catch (InvalidOperationException ex)
{
throw new InvalidOperationException(message, ex);
}

}
}
}

0 comments on commit d11072b

Please sign in to comment.