Skip to content

New CLI #2272

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

Closed
wants to merge 2 commits into from
Closed

New CLI #2272

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
96 changes: 96 additions & 0 deletions src/GitVersion.Cli/Commands/CalculateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using GitVersion.Logging;
using LibGit2Sharp;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Threading.Tasks;

namespace GitVersion.Cli
{
public class CalculateCommand : Command
{
private readonly Logging.IConsole console;
private readonly IGitVersionCalculator gitversionCalculator;
private readonly CommandWrapper executor;

public CalculateCommand(Logging.IConsole console, IGitVersionCalculator gitversionCalculator, CommandWrapper executor) : base("calculate", "Calculates version information from your git repository")
{
this.console = console;
this.gitversionCalculator = gitversionCalculator;
this.executor = executor;
AddOptions();

this.Handler = CommandHandler.Create<GlobalOptions, CalculateOptions>(ExecuteAsync);
}

private void AddOptions()
{
this.AddOption(new Option<bool>(
"--normalize",
"Attempt to mutate your git repository so gitversion has enough information (local branches, commit history etc) to calculate."));
this.AddOption(new Option<string>(
"--dot-git-dir",
"The path of the .git folder. If not specified, will be discovered from the envrionment current working directory."));
this.AddOption(new Option<string>(
"--no-cache",
"Whether to disable caching of the version variables. If true is specified, any previous cached calculation will be ignored and the new calculation won't be cached either."));

this.AddOption(new Option<string>(
"--branch",
"The target branch to calculate a version against."));

}

private async Task<int> ExecuteAsync(GlobalOptions globalOptions, CalculateOptions options)
{
// The executor wraps execution of the command logic inside somethng that
// will do error handling according to the old behaviour.
return await executor.Execute(globalOptions, async () =>
{
// if .git directory not specified, discover it from current environment working directory.
if (string.IsNullOrWhiteSpace(options.DotGitDir))
{
options.DotGitDir = Repository.Discover(globalOptions.WorkingDirectory);
}

if (options.Normalize ?? false)
{
Normalize();
}

//TODO: support the config override param, and also inside the CalculateVersionVariables it does a prepare - we should be able
// to pass args to control that as well. i.e using the above (options.Normalize) arg.
var variables = this.gitversionCalculator.CalculateVersionVariables(options.NoCache, null);
console.WriteLine(variables.ToString());
return 0;
});
}

private void Normalize()
{
executor.Log.Info("TODO: Implement --normalize");
// throw new NotImplementedException();
}
}

public class CalculateOptions
{
public CalculateOptions()
{

}
public CalculateOptions(bool? normalize)
{
//GlobalOptions = globalOptions;
Normalize = normalize;
}
public bool? Normalize { get; }
public bool? NoCache { get; }
/// <summary>
/// The path of the .git folder. If not specified, will be discovered from the envrionment current working directory.
/// </summary>
public string DotGitDir { get; set; }

public string Branch { get; set; }
}

}
39 changes: 39 additions & 0 deletions src/GitVersion.Cli/Commands/LogToArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.CommandLine;
using System.Linq;

namespace GitVersion.Cli
{
/// <summary>
/// This is a custom argument that parses `--log-to Console` and `--log-to File "c:/foo.txt"
/// </summary>
public class LogToArgument : Argument<LoggingOptions>
{
public LogToArgument() : base(result =>
{
var options = new LoggingOptions();
var logTargetToken = result.Tokens.First();

options.LogTo = Enum.Parse<LogToTarget>(logTargetToken.Value);
if (options.LogTo == LogToTarget.File)
{
var logFilePathToken = result.Tokens[1];
options.LogFilePath = logFilePathToken.Value;
}
else
{
// only --log-to File can have an additional symbol for the file path.
if (result.Tokens.Count > 1)
{
throw new ArgumentException();
}
}

return options;
})
{
Arity = new ArgumentArity(1, 2);
}
}

}
20 changes: 20 additions & 0 deletions src/GitVersion.Cli/Commands/RootCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.CommandLine;

namespace GitVersion.Cli
{

public class RootCommand : System.CommandLine.RootCommand
{
public RootCommand(CalculateCommand calculateCommand) : base("Versioning for your git repository, solved!")
{
AddGlobalOptions();
this.AddCommand(calculateCommand);
}

private void AddGlobalOptions()
{
var option = new Option("--log-to") { Argument = new LogToArgument() };
this.AddGlobalOption(option);
}
}
}
18 changes: 18 additions & 0 deletions src/GitVersion.Cli/GitVersion.Cli.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.3" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20253.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\GitVersionCore\GitVersionCore.csproj" />
</ItemGroup>

</Project>
19 changes: 19 additions & 0 deletions src/GitVersion.Cli/GlobalOptions/GlobalOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;

namespace GitVersion.Cli
{

public class GlobalOptions
{

public GlobalOptions(LoggingOptions logTo = null)
{
LogTo = logTo;
}

public string WorkingDirectory { get; set; } = System.Environment.CurrentDirectory;

public LoggingOptions LogTo { get; set; }
}

}
9 changes: 9 additions & 0 deletions src/GitVersion.Cli/GlobalOptions/LogToTarget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace GitVersion.Cli
{
public enum LogToTarget
{
Console = 1,
File = 2,
}

}
10 changes: 10 additions & 0 deletions src/GitVersion.Cli/GlobalOptions/LoggingOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace GitVersion.Cli
{
public class LoggingOptions
{
public LogToTarget LogTo { get; set; }

public string LogFilePath { get; set; }
}

}
48 changes: 48 additions & 0 deletions src/GitVersion.Cli/HostedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using GitVersion.Logging;
using Microsoft.Extensions.Hosting;
using System;
using System.CommandLine;
using System.Threading;
using System.Threading.Tasks;

namespace GitVersion.Cli
{
internal class HostedService : IHostedService
{
private readonly IHostApplicationLifetime applicationLifetime;
private readonly RootCommand command;
// private readonly IGitVersionExecutor gitVersionExecutor;
private readonly ILog log;
private readonly string[] arguments;

public HostedService(IHostApplicationLifetime applicationLifetime, RootCommand command, ILog log, string[] arguments)
{

this.applicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
this.command = command;
// this.gitVersionExecutor = gitVersionExecutor ?? throw new ArgumentNullException(nameof(gitVersionExecutor));
this.log = log ?? throw new ArgumentNullException(nameof(log));
this.arguments = arguments;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
System.Environment.ExitCode = await command.InvokeAsync(arguments);
// log.Verbosity = gitVersionOptions.Verbosity; // not sure how to get verbosity now.
}
catch (Exception exception)
{
Console.Error.WriteLine(exception.Message);
System.Environment.ExitCode = 1;
}

applicationLifetime.StopApplication();
}

public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
}
77 changes: 77 additions & 0 deletions src/GitVersion.Cli/Internal/CommandWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using GitVersion.Extensions;
using GitVersion.Logging;
using System;
using System.Threading.Tasks;

namespace GitVersion.Cli
{
/// <summary>
/// The purpose of this class is to wrap execution of each subcommand's logic,
/// to have a centralised location to handle exceptions, and log those exceptions,
/// based on the global option arguments used to control log output.
/// </summary>
/// <remarks>This is evolving in a similar way to <see cref="GitVersionExecutor"/> but for executing the new commands, and using the GlobalOptions for the new cli instead.
/// Allowing <see cref="GitVersionExecutor"/> to serve as a reference until the port is finished.
///</remarks>
public class CommandWrapper
{
public CommandWrapper(ILog log)
{
Log = log;
}

public async Task<int> Execute(GlobalOptions globalOptions, Func<Task<int>> execute)
{
var loggingOptions = globalOptions.LogTo;

if (loggingOptions.LogTo == LogToTarget.Console)
{
Log.AddLogAppender(new ConsoleAppender());
}
else if (loggingOptions.LogTo == LogToTarget.File && loggingOptions.LogFilePath != "console")
{
Log.AddLogAppender(new FileAppender(loggingOptions.LogFilePath));
}

try
{
//gitVersionTool.OutputVariables(variables);
//gitVersionTool.UpdateAssemblyInfo(variables);
//gitVersionTool.UpdateWixVersionFile(variables);
var result = await execute.Invoke();
return result;
}
catch (WarningException exception)
{
var error = $"An error occurred:{System.Environment.NewLine}{exception.Message}";
Log.Warning(error);
return 1;
}
catch (Exception exception)
{
var error = $"An unexpected error occurred:{System.Environment.NewLine}{exception}";
Log.Error(error);

Log.Info("Attempting to show the current git graph (please include in issue): ");
Log.Info("Showing max of 100 commits");

try
{
// I am not sure here if we should be passing in working directory, or the dot git directory?
// current behaviour was to pass in working directory (environment current directory) so sticking with that.
LibGitExtensions.DumpGraph(globalOptions.WorkingDirectory, mess => Log.Info(mess), 100);
}
catch (Exception dumpGraphException)
{
Log.Error("Couldn't dump the git graph due to the following error: " + dumpGraphException);
}
return 1;
}


}

public ILog Log { get; set; }
}

}
Loading