Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Verbose bootstrapper #2007

Merged
merged 4 commits into from
Nov 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions docs/content/bootstrapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ Cached paket.exe versions are removed when the NuGet cache folder is [cleared](p

`--self`: Self updating the paket.bootstrapper.exe. When this option is used the download of paket.exe will be skipped. (Can be combined with `--prefer-nuget`)

`-s`: If this flag is set the bootstrapper will not perform any output.
`-s`: Make the boostrapper more silent. Use it once to display display only errors or twice to supress all output.

`-v`: Make the boostrapper more verbose. Display more information about the boostrapper process, including operation timings.

`-f`: Forces the bootstrapper to ignore any cached paket.exe versions and go directly to github.com or nuget.org based on other flags.

Expand Down Expand Up @@ -83,7 +85,7 @@ nuget FAKE
nuget FSharp.Core ~> 4
```

or
or

```paket
version 3.24.1 --prefer-nuget
Expand Down Expand Up @@ -112,5 +114,10 @@ Using this feature paket can be used simply by committing a ~50KB `paket.exe` to
The fact that a boostrapper exists is completely hidden and become an implementation detail that contributors to your
repository won't have to know — or care — about.

While command line bootstrapper options can't be used (Except if `--run` is passed) the other sources
(AppSettings, Environment Variables & paket.dependencies) can still be used to configure the bootstrapper.
While command line bootstrapper options can't be used the other sources (AppSettings, Environment Variables
& paket.dependencies) can still be used to configure the bootstrapper.

A few default settings are applied:
* The boostrapper is silent and only errors are displayed. `-v` can be used once to restore normal output or twice
to show more details.
* If no version is specified a default `--max-file-age` of 12 Hours is used.
14 changes: 10 additions & 4 deletions src/Paket.Bootstrapper/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static class CommandArgs
public const string NugetSourceArgPrefix = "--nuget-source=";
public const string SelfUpdate = "--self";
public const string Silent = "-s";
public const string Verbose = "-v";
public const string IgnoreCache = "-f";
public const string MaxFileAge = "--max-file-age=";
public const string Run = "--run";
Expand Down Expand Up @@ -69,8 +70,8 @@ public static BootstrapperOptions ParseArgumentsAndConfigurations(IEnumerable<st
{
// Transparent magic mode mean that we're renamed 'paket.exe' and --run wasn't passed

// Enforce silence
options.Silent = SilentMode.ErrorsOnly;
// Virtually add a '-s'
options.Verbosity -= 1;

// Assume --run and that all arguments are for the real paket binary
options.Run = true;
Expand Down Expand Up @@ -157,11 +158,16 @@ private static void FillNonRunOptionsFromArguments(BootstrapperOptions options,
options.ForceNuget = true;
commandArgs.Remove(CommandArgs.ForceNuget);
}
if (commandArgs.Contains(CommandArgs.Silent))
while (commandArgs.Contains(CommandArgs.Silent))
{
options.Silent = SilentMode.Silent;
options.Verbosity -= 1;
commandArgs.Remove(CommandArgs.Silent);
}
while (commandArgs.Contains(CommandArgs.Verbose))
{
options.Verbosity += 1;
commandArgs.Remove(CommandArgs.Verbose);
}
if (commandArgs.Contains(CommandArgs.Help))
{
options.ShowHelp = true;
Expand Down
5 changes: 3 additions & 2 deletions src/Paket.Bootstrapper/BootstrapperHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ NUGET_SOURCE can also be a filepath
older than <IN MINUTES> all checks will be skipped.
--self downloads and updates paket.bootstrapper
-f don't use local cache; always downloads
-s silent mode; no output
--run run the downloaded paket.exe with all following arguments";
-s silent mode; errors only. Use twice for no output
-v verbose; show more information on console.
--run <other args> run the downloaded paket.exe with all following arguments";
const string PaketBootstrapperUserAgent = "Paket.Bootstrapper";

internal static string GetLocalFileVersion(string target)
Expand Down
3 changes: 2 additions & 1 deletion src/Paket.Bootstrapper/BootstrapperOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ public class BootstrapperOptions
{
public BootstrapperOptions()
{
Verbosity = Verbosity.Normal;
DownloadArguments = new DownloadArguments();
RunArgs = Enumerable.Empty<string>();
UnprocessedCommandArgs = Enumerable.Empty<string>();
}

public DownloadArguments DownloadArguments { get; set; }

public SilentMode Silent { get; set; }
public Verbosity Verbosity { get; set; }
public bool ForceNuget { get; set; }
public bool PreferNuget { get; set; }
public bool ShowHelp { get; set; }
Expand Down
39 changes: 24 additions & 15 deletions src/Paket.Bootstrapper/ConsoleImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,53 @@ namespace Paket.Bootstrapper
{
public static class ConsoleImpl
{
public static SilentMode Silent { get; set; }
public static Verbosity Verbosity { get; set; }

public static bool IsTraceEnabled => Verbosity >= Verbosity.Trace;

internal static void WriteError(string message, params object[] parameters)
{
WriteError(string.Format(message, parameters));
WriteConsole(string.Format(message, parameters), ConsoleColor.Red, Verbosity.ErrorsOnly);
}

internal static void WriteError(string message)
{
WriteConsole(message, ConsoleColor.Red, true);
WriteConsole(message, ConsoleColor.Red, Verbosity.ErrorsOnly);
}

internal static void WriteInfo(string message, params object[] parameters)
internal static void WriteWarning(string message, params object[] parameters)
{
WriteInfo(string.Format(message, parameters));
WriteConsole(string.Format(message, parameters), ConsoleColor.Yellow);
}

internal static void WriteInfo(string message)
internal static void WriteWarning(string message)
{
WriteConsole(message, ConsoleColor.Yellow);
}
internal static void WriteDebug(string message, params object[] parameters)

internal static void WriteInfo(string message, params object[] parameters)
{
WriteDebug(string.Format(message, parameters));
WriteConsole(string.Format(message, parameters), Console.ForegroundColor);
}

internal static void WriteDebug(string message)
internal static void WriteInfo(string message)
{
WriteConsole(message, Console.ForegroundColor);
}

private static void WriteConsole(string message, ConsoleColor consoleColor, bool error = false)
internal static void WriteTrace(string message, params object[] parameters)
{
if (Silent == SilentMode.Silent)
{
return;
}
if (Silent == SilentMode.ErrorsOnly && !error)
WriteConsole(string.Format(message, parameters), ConsoleColor.DarkGray, Verbosity.Trace);
}

internal static void WriteTrace(string message)
{
WriteConsole(message, ConsoleColor.DarkGray, Verbosity.Trace);
}

private static void WriteConsole(string message, ConsoleColor consoleColor, Verbosity minVerbosity = Verbosity.Normal)
{
if (Verbosity < minVerbosity)
{
return;
}
Expand Down
21 changes: 11 additions & 10 deletions src/Paket.Bootstrapper/DownloadStrategies/CacheDownloadStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@

namespace Paket.Bootstrapper.DownloadStrategies
{
internal class CacheDownloadStrategy : IHaveEffectiveStrategy
internal class CacheDownloadStrategy : DownloadStrategy, IHaveEffectiveStrategy
{
public string Name => $"{EffectiveStrategy.Name} - cached";
public IDownloadStrategy FallbackStrategy { get; set; }
public override string Name => $"{EffectiveStrategy.Name} - cached";

private readonly string _paketCacheDir =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "NuGet", "Cache", "Paket");
Expand All @@ -29,7 +28,7 @@ public CacheDownloadStrategy(IDownloadStrategy effectiveStrategy, IFileSystemPro
}


public string GetLatestVersion(bool ignorePrerelease)
protected override string GetLatestVersionCore(bool ignorePrerelease)
{
try
{
Expand All @@ -41,35 +40,37 @@ public string GetLatestVersion(bool ignorePrerelease)
{
var latestVersion = GetLatestVersionInCache(ignorePrerelease);

ConsoleImpl.WriteDebug("Unable to look up the latest version online, the cache contains version {0}.", latestVersion);
ConsoleImpl.WriteInfo("Unable to look up the latest version online, the cache contains version {0}.", latestVersion);

return latestVersion;
}
throw;
}
}

public void DownloadVersion(string latestVersion, string target)
protected override void DownloadVersionCore(string latestVersion, string target)
{
var cached = Path.Combine(_paketCacheDir, latestVersion, "paket.exe");

if (!FileSystemProxy.FileExists(cached))
{
ConsoleImpl.WriteDebug("Version {0} not found in cache.", latestVersion);
ConsoleImpl.WriteInfo("Version {0} not found in cache.", latestVersion);

EffectiveStrategy.DownloadVersion(latestVersion, target);

ConsoleImpl.WriteTrace("Caching version {0} for later", latestVersion);
FileSystemProxy.CreateDirectory(Path.GetDirectoryName(cached));
FileSystemProxy.CopyFile(target, cached);
}
else
{
ConsoleImpl.WriteDebug("Copying version {0} from cache.", latestVersion);

ConsoleImpl.WriteInfo("Copying version {0} from cache.", latestVersion);
ConsoleImpl.WriteTrace("{0} -> {1}", cached, target);
FileSystemProxy.CopyFile(cached, target, true);
}
}

public void SelfUpdate(string latestVersion)
protected override void SelfUpdateCore(string latestVersion)
{
EffectiveStrategy.SelfUpdate(latestVersion);
}
Expand Down
67 changes: 67 additions & 0 deletions src/Paket.Bootstrapper/DownloadStrategies/DownloadStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Diagnostics;

namespace Paket.Bootstrapper.DownloadStrategies
{
public abstract class DownloadStrategy : IDownloadStrategy
{
public abstract string Name { get; }
public IDownloadStrategy FallbackStrategy { get; set; }
public string GetLatestVersion(bool ignorePrerelease)
{
return Wrap(() => GetLatestVersionCore(ignorePrerelease), "GetLatestVersion");
}

public void DownloadVersion(string latestVersion, string target)
{
Wrap(() => DownloadVersionCore(latestVersion, target), "DownloadVersion");
}

public void SelfUpdate(string latestVersion)
{
Wrap(() => SelfUpdateCore(latestVersion), "SelfUpdate");
}

protected abstract string GetLatestVersionCore(bool ignorePrerelease);
protected abstract void DownloadVersionCore(string latestVersion, string target);
protected abstract void SelfUpdateCore(string latestVersion);

private void Wrap(Action action, string actionName)
{
if (!ConsoleImpl.IsTraceEnabled)
{
action();
return;
}

Wrap(() => {
action();
return "void";
}, actionName);
}

private TResult Wrap<TResult>(Func<TResult> func, string actionName)
{
if (!ConsoleImpl.IsTraceEnabled)
{
return func();
}

ConsoleImpl.WriteTrace("[{0}] {1}...", Name, actionName);
var watch = Stopwatch.StartNew();
try
{
var result = func();
watch.Stop();
ConsoleImpl.WriteTrace("[{0}] {1} took {2:0.##} second(s) and returned {3}.", Name, actionName, watch.Elapsed.TotalSeconds, result);
return result;
}
catch (Exception exception)
{
watch.Stop();
ConsoleImpl.WriteTrace("[{0}] {1} took {2:0.##} second(s) and failed with {3}.", Name, actionName, watch.Elapsed.TotalSeconds, exception.Message);
throw;
}
}
}
}
21 changes: 10 additions & 11 deletions src/Paket.Bootstrapper/DownloadStrategies/GitHubDownloadStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Paket.Bootstrapper.DownloadStrategies
{
public class GitHubDownloadStrategy : IDownloadStrategy
public class GitHubDownloadStrategy : DownloadStrategy
{
public static class Constants
{
Expand All @@ -17,16 +17,15 @@ public static class Constants

private IWebRequestProxy WebRequestProxy { get; set; }
private IFileSystemProxy FileSystemProxy { get; set; }
public string Name { get { return "Github"; } }
public IDownloadStrategy FallbackStrategy { get; set; }
public override string Name { get { return "Github"; } }

public GitHubDownloadStrategy(IWebRequestProxy webRequestProxy, IFileSystemProxy fileSystemProxy)
{
WebRequestProxy = webRequestProxy;
FileSystemProxy = fileSystemProxy;
}

public string GetLatestVersion(bool ignorePrerelease)
protected override string GetLatestVersionCore(bool ignorePrerelease)
{
var latestStable = GetLatestStable();
if (ignorePrerelease)
Expand Down Expand Up @@ -70,10 +69,10 @@ private List<string> GetVersions(string data)
return versions;
}

public void DownloadVersion(string latestVersion, string target)
protected override void DownloadVersionCore(string latestVersion, string target)
{
var url = String.Format(Constants.PaketExeDownloadUrlTemplate, latestVersion);
ConsoleImpl.WriteDebug("Starting download from {0}", url);
ConsoleImpl.WriteInfo("Starting download from {0}", url);

var tmpFile = BootstrapperHelper.GetTempFile("paket");
WebRequestProxy.DownloadFile(url, tmpFile);
Expand All @@ -82,19 +81,19 @@ public void DownloadVersion(string latestVersion, string target)
FileSystemProxy.DeleteFile(tmpFile);
}

public void SelfUpdate(string latestVersion)
protected override void SelfUpdateCore(string latestVersion)
{
var executingAssembly = Assembly.GetExecutingAssembly();
string exePath = executingAssembly.Location;
var localVersion = FileSystemProxy.GetLocalFileVersion(exePath);
if (localVersion.StartsWith(latestVersion))
{
ConsoleImpl.WriteDebug("Bootstrapper is up to date. Nothing to do.");
ConsoleImpl.WriteInfo("Bootstrapper is up to date. Nothing to do.");
return;
}

var url = String.Format("https://github.com/fsprojects/Paket/releases/download/{0}/paket.bootstrapper.exe", latestVersion);
ConsoleImpl.WriteDebug("Starting download of bootstrapper from {0}", url);
ConsoleImpl.WriteInfo("Starting download of bootstrapper from {0}", url);

string renamedPath = BootstrapperHelper.GetTempFile("oldBootstrapper");
string tmpDownloadPath = BootstrapperHelper.GetTempFile("newBootstrapper");
Expand All @@ -104,11 +103,11 @@ public void SelfUpdate(string latestVersion)
{
FileSystemProxy.MoveFile(exePath, renamedPath);
FileSystemProxy.MoveFile(tmpDownloadPath, exePath);
ConsoleImpl.WriteDebug("Self update of bootstrapper was successful.");
ConsoleImpl.WriteInfo("Self update of bootstrapper was successful.");
}
catch (Exception)
{
ConsoleImpl.WriteDebug("Self update failed. Resetting bootstrapper.");
ConsoleImpl.WriteInfo("Self update failed. Resetting bootstrapper.");
FileSystemProxy.MoveFile(renamedPath, exePath);
throw;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,4 @@ public interface IDownloadStrategy
void DownloadVersion(string latestVersion, string target);
void SelfUpdate(string latestVersion);
}

public interface IHaveEffectiveStrategy : IDownloadStrategy
{
IDownloadStrategy EffectiveStrategy { get; }
}

}
Loading