Skip to content
Merged
Prev Previous commit
Next Next commit
Take magic bootstrapper arguments from dependencies file
The bootstrapper in magic mode can now take it's arguments from a special
line in 'paket.dependencies' the syntax is:

    # bootstrapper: <arguments>
  • Loading branch information
vbfox committed Oct 27, 2016
commit 56e36d850add4493be501bdd02e4ea42324db152
3 changes: 2 additions & 1 deletion src/Paket.Bootstrapper/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static class EnvArgs
public const string PaketVersionEnv = "PAKET.VERSION";
}

public static BootstrapperOptions ParseArgumentsAndConfigurations(IEnumerable<string> arguments, NameValueCollection appSettings, IDictionary envVariables, IFileSystemProxy fileSystemProxy)
public static BootstrapperOptions ParseArgumentsAndConfigurations(IEnumerable<string> arguments, NameValueCollection appSettings, IDictionary envVariables, IFileSystemProxy fileSystemProxy, IEnumerable<string> argumentsInDependenciesFile = null)
{
var options = new BootstrapperOptions();
var commandArgs = arguments.ToList();
Expand Down Expand Up @@ -86,6 +86,7 @@ public static BootstrapperOptions ParseArgumentsAndConfigurations(IEnumerable<st
commandArgs = EvaluateDownloadOptions(options.DownloadArguments, commandArgs, appSettings, envVariables, magicMode, fileSystemProxy).ToList();

options.UnprocessedCommandArgs = commandArgs;

return options;
}

Expand Down
1 change: 1 addition & 0 deletions src/Paket.Bootstrapper/Paket.Bootstrapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<Compile Include="ArgumentParser.cs" />
<Compile Include="BootstrapperHelper.cs" />
<Compile Include="BootstrapperOptions.cs" />
<Compile Include="PaketDependencies.cs" />
<Compile Include="PaketRunner.cs" />
<Compile Include="WindowsProcessArguments.cs" />
<Compile Include="DownloadStrategies\CacheDownloadStrategy.cs" />
Expand Down
54 changes: 54 additions & 0 deletions src/Paket.Bootstrapper/PaketDependencies.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.IO;
using System.Text.RegularExpressions;

namespace Paket.Bootstrapper
{
static class PaketDependencies
{
private static readonly Regex bootstrapperArgsLine =
new Regex("^(#|//)\\s*bootstrapper:(?<args>.*)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

const string DEPENDENCY_FILE = "paket.dependencies";

public static string GetBootstrapperArgs(TextReader reader)
{
var line = reader.ReadLine();
while (line != null)
{
var match = bootstrapperArgsLine.Match(line);
if (match.Success)
{
return match.Groups["args"].Value;
}
line = reader.ReadLine();
}
return null;
}

public static string GetBootstrapperArgsForFolder(string folder)
{
try
{
var path = Path.Combine(folder, DEPENDENCY_FILE);
if (!File.Exists(path))
{
return null;
}

using (var fileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var reader = new StreamReader(fileStream))
{
return GetBootstrapperArgs(reader);
}
}
}
catch (Exception)
{
// ¯\_(ツ)_/¯
return null;
}
}
}
}
6 changes: 4 additions & 2 deletions src/Paket.Bootstrapper/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
Expand All @@ -20,8 +19,11 @@ static void Main(string[] args)
Console.CancelKeyPress += CancelKeyPressed;

var fileProxy = new FileSystemProxy();
var argumentsFromDependenciesFile =
WindowsProcessArguments.Parse(
PaketDependencies.GetBootstrapperArgsForFolder(Environment.CurrentDirectory));
var options = ArgumentParser.ParseArgumentsAndConfigurations(args, ConfigurationManager.AppSettings,
Environment.GetEnvironmentVariables(), fileProxy);
Environment.GetEnvironmentVariables(), fileProxy, argumentsFromDependenciesFile);
if (options.ShowHelp)
{
ConsoleImpl.WriteDebug(BootstrapperHelper.HelpText);
Expand Down
124 changes: 124 additions & 0 deletions src/Paket.Bootstrapper/WindowsProcessArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,129 @@ public static string ToString(IEnumerable<string> args)
}
return builder.ToString();
}

private static bool ParseBackslashes(ref int backslashes, StringBuilder buffer, char c)
{
if (c == '\\')
{
backslashes += 1;
return true;
}

if (backslashes <= 0)
{
return false;
}

if (c == '"')
{
if (backslashes % 2 == 0)
{
// Even number of backslashes, the backslashes are considered escaped but not the quote
buffer.Append('\\', backslashes/2);
}
else
{
// Odd number of backslashes, the backslashes are considered escaped and the quote too
buffer.Append('\\', (backslashes-1)/2);
buffer.Append(c);
backslashes = 0;
return true;
}
}
else
{
// Backslashes not followed by a quote are interpreted literally
buffer.Append('\\', backslashes);
}
backslashes = 0;

return false;
}

public static List<string> Parse(string args)
{
if (args == null)
{
return null;
}

var result = new List<string>();
var buffer = new StringBuilder(args.Length);
var inParameter = false;
var quoted = false;
var backslashes = 0;

for(int i = 0; i < args.Length; i++)
{
var c = args[i];
if (!inParameter && c != ' ' && c != '\t')
{
inParameter = true;
}

if (!inParameter)
{
continue;
}

if (ParseBackslashes(ref backslashes, buffer, c))
{
continue;
}

if (quoted)
{
if (c == '"')
{
if (i + 1 < args.Length)
{
var nextC = args[i + 1];
if (nextC == '"')
{
// Special double quote case, insert only one quote and continue the double quoted part
// "post 2008" behavior in http://www.daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
i += 1;
buffer.Append(c);
continue;
}
}

// All escapes have been handled so it ends a quoted part
quoted = false;
}
else
{
buffer.Append(c);
}
}
else
{
if (c == ' ' || c == '\t')
{
inParameter = false;
result.Add(buffer.ToString());
buffer.Clear();
}
else if (c == '"')
{
// All escapes have been handled so it start a quoted part
quoted = true;
}
else
{
buffer.Append(c);
}
}
}

if (inParameter)
{
buffer.Append('\\', backslashes);
result.Add(buffer.ToString());
}

return result;
}
}
}
77 changes: 73 additions & 4 deletions tests/Paket.Bootstrapper.Tests/ArgumentParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ public string GetTempPath()
private static readonly IFileSystemProxy NormalModeFileSystemSystem = new DummyFileSystemProxy("C:\\repo\\.paket\\paket.bootstrapper.exe");
private static readonly IFileSystemProxy MagicModeFileSystemSystem = new DummyFileSystemProxy("C:\\repo\\paket.exe");

private static BootstrapperOptions Parse(IEnumerable<string> arguments, NameValueCollection appSettings, IDictionary envVariables)
private static BootstrapperOptions Parse(IEnumerable<string> arguments, NameValueCollection appSettings, IDictionary envVariables, IEnumerable<string> argumentsInDependenciesFile = null)
{
return ArgumentParser.ParseArgumentsAndConfigurations(arguments, appSettings, envVariables, NormalModeFileSystemSystem);
return ArgumentParser.ParseArgumentsAndConfigurations(arguments, appSettings, envVariables, NormalModeFileSystemSystem, argumentsInDependenciesFile);
}

private static BootstrapperOptions ParseMagic(IEnumerable<string> arguments, NameValueCollection appSettings, IDictionary envVariables)
private static BootstrapperOptions ParseMagic(IEnumerable<string> arguments, NameValueCollection appSettings, IDictionary envVariables, IEnumerable<string> argumentsInDependenciesFile = null)
{
return ArgumentParser.ParseArgumentsAndConfigurations(arguments, appSettings, envVariables, MagicModeFileSystemSystem);
return ArgumentParser.ParseArgumentsAndConfigurations(arguments, appSettings, envVariables, MagicModeFileSystemSystem, argumentsInDependenciesFile);
}

[Test]
Expand Down Expand Up @@ -520,5 +520,74 @@ public void Magic_WithRun()
Assert.That(result.UnprocessedCommandArgs, Is.Empty);
Assert.That(result.DownloadArguments.Target, Does.StartWith(MagicModeFileSystemSystem.GetTempPath()).And.EndsWith(".exe"));
}

[Test]
public void Magic_Dependencies_Empty_Args()
{
//arrange

//act
var result = ParseMagic(new[] {"-s", "--help", "foo"}, null, null,
new string[0] );

//assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Silent, Is.True);
Assert.That(result.UnprocessedCommandArgs, Is.Empty);
Assert.That(result.Run, Is.True);
Assert.That(result.RunArgs, Is.Not.Empty.And.EqualTo(new[] {"-s", "--help", "foo"}));
Assert.That(result.DownloadArguments.MaxFileAgeInMinutes, Is.EqualTo(720));
Assert.That(result.DownloadArguments.Target, Does.StartWith(Path.GetTempPath()).And.EndsWith(".exe"));
}

[Test]
public void Magic_Dependencies_Args()
{
//arrange

//act
var result = ParseMagic(new[] {"-s", "--help", "foo"}, null, null,
new [] { "prerelease", "--max-file-age=42", "--nuget-source=http://local.site/path/here", "--force-nuget", "--prefer-nuget", "-f" } );

//assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Silent, Is.True);
Assert.That(result.UnprocessedCommandArgs, Is.Empty);
Assert.That(result.Run, Is.True);
Assert.That(result.RunArgs, Is.Not.Empty.And.EqualTo(new[] {"-s", "--help", "foo"}));
Assert.That(result.DownloadArguments.MaxFileAgeInMinutes, Is.EqualTo(42));
Assert.That(result.DownloadArguments.Target, Does.StartWith(Path.GetTempPath()).And.EndsWith(".exe"));
Assert.That(result.DownloadArguments.IgnorePrerelease, Is.False);
Assert.That(result.DownloadArguments.NugetSource, Is.EqualTo("http://local.site/path/here"));
Assert.That(result.DownloadArguments.IgnoreCache, Is.True);
Assert.That(result.ForceNuget, Is.True);
Assert.That(result.PreferNuget, Is.True);
}

[Test]
public void Magic_WithRun_Dependencies_Args()
{
//arrange

//act
var result = ParseMagic(
new[]
{
ArgumentParser.CommandArgs.Silent,
ArgumentParser.CommandArgs.Run,
"-s",
"--help",
"foo"
}, null, null,
new [] { "prerelease", "--max-file-age=42", "--nuget-source=http://local.site/path/here", "--force-nuget", "--prefer-nuget", "-f" });

//assert
Assert.That(result.Run, Is.True);
Assert.That(result.Silent, Is.True);
Assert.That(result.RunArgs, Is.Not.Empty.And.EqualTo(new[] {"-s", "--help", "foo"}));
Assert.That(result.UnprocessedCommandArgs, Is.Empty);
Assert.That(result.DownloadArguments.Target, Does.StartWith(Path.GetTempPath()).And.EndsWith(".exe"));
Assert.That(result.DownloadArguments.MaxFileAgeInMinutes, Is.Null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
<Compile Include="SemVerTests.cs" />
<Compile Include="StartPaketBootstrappingTests.cs" />
<Compile Include="DownloadStrategies\TemporarilyIgnoreUpdatesDownloadStrategyTest.cs" />
<Compile Include="WindowsProcessArguments.cs" />
<Compile Include="WindowsProcessArgumentsParseTests.cs" />
<Compile Include="WindowsProcessArgumentsToStringTests.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.paket\paket.targets" />
Expand Down
Loading