Skip to content

Commit 22e86b0

Browse files
Copilotarturcic
andcommitted
Update Spectre.Console.Cli to latest version and split classes into separate files
Co-authored-by: arturcic <[email protected]>
1 parent d04a748 commit 22e86b0

File tree

6 files changed

+381
-363
lines changed

6 files changed

+381
-363
lines changed

src/Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
<PackageVersion Include="NUnit.Analyzers" Version="4.9.2" />
3838
<PackageVersion Include="NUnit3TestAdapter" Version="5.0.0" />
3939
<PackageVersion Include="Shouldly" Version="4.3.0" />
40-
<PackageVersion Include="Spectre.Console.Cli" Version="0.49.1" />
40+
<PackageVersion Include="Spectre.Console.Cli" Version="0.50.0" />
4141
<PackageVersion Include="System.Collections.Immutable" Version="9.0.2" />
4242
<PackageVersion Include="System.Drawing.Common" Version="9.0.6" />
4343
<PackageVersion Include="System.IO.Abstractions" Version="22.0.14" />
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
using System.IO.Abstractions;
2+
using GitVersion.Agents;
3+
using GitVersion.Extensions;
4+
using GitVersion.FileSystemGlobbing;
5+
using GitVersion.Helpers;
6+
using GitVersion.Logging;
7+
using GitVersion.OutputVariables;
8+
using Spectre.Console.Cli;
9+
10+
namespace GitVersion;
11+
12+
/// <summary>
13+
/// Interceptor to capture parsed arguments
14+
/// </summary>
15+
internal class ArgumentInterceptor : ICommandInterceptor
16+
{
17+
private readonly ParseResultStorage storage;
18+
private readonly IEnvironment environment;
19+
private readonly IFileSystem fileSystem;
20+
private readonly ICurrentBuildAgent buildAgent;
21+
private readonly IConsole console;
22+
private readonly IGlobbingResolver globbingResolver;
23+
24+
public ArgumentInterceptor(ParseResultStorage storage, IEnvironment environment, IFileSystem fileSystem, ICurrentBuildAgent buildAgent, IConsole console, IGlobbingResolver globbingResolver)
25+
{
26+
this.storage = storage;
27+
this.environment = environment;
28+
this.fileSystem = fileSystem;
29+
this.buildAgent = buildAgent;
30+
this.console = console;
31+
this.globbingResolver = globbingResolver;
32+
}
33+
34+
public void Intercept(CommandContext context, CommandSettings settings)
35+
{
36+
if (settings is GitVersionSettings gitVersionSettings)
37+
{
38+
var arguments = ConvertToArguments(gitVersionSettings);
39+
AddAuthentication(arguments);
40+
ValidateAndProcessArguments(arguments);
41+
this.storage.SetResult(arguments);
42+
}
43+
}
44+
45+
private void AddAuthentication(Arguments arguments)
46+
{
47+
var username = this.environment.GetEnvironmentVariable("GITVERSION_REMOTE_USERNAME");
48+
if (!username.IsNullOrWhiteSpace())
49+
{
50+
arguments.Authentication.Username = username;
51+
}
52+
53+
var password = this.environment.GetEnvironmentVariable("GITVERSION_REMOTE_PASSWORD");
54+
if (!password.IsNullOrWhiteSpace())
55+
{
56+
arguments.Authentication.Password = password;
57+
}
58+
}
59+
60+
private void ValidateAndProcessArguments(Arguments arguments)
61+
{
62+
// Apply default output if none specified
63+
if (arguments.Output.Count == 0)
64+
{
65+
arguments.Output.Add(OutputType.Json);
66+
}
67+
68+
// Set default output file if file output is specified
69+
if (arguments.Output.Contains(OutputType.File) && arguments.OutputFile == null)
70+
{
71+
arguments.OutputFile = "GitVersion.json";
72+
}
73+
74+
// Apply build agent settings
75+
arguments.NoFetch = arguments.NoFetch || this.buildAgent.PreventFetch();
76+
77+
// Validate configuration file
78+
ValidateConfigurationFile(arguments);
79+
80+
// Process assembly info files
81+
if (!arguments.EnsureAssemblyInfo)
82+
{
83+
arguments.UpdateAssemblyInfoFileName = ResolveFiles(arguments.TargetPath, arguments.UpdateAssemblyInfoFileName).ToHashSet();
84+
}
85+
}
86+
87+
private void ValidateConfigurationFile(Arguments arguments)
88+
{
89+
if (arguments.ConfigurationFile.IsNullOrWhiteSpace()) return;
90+
91+
if (FileSystemHelper.Path.IsPathRooted(arguments.ConfigurationFile))
92+
{
93+
if (!this.fileSystem.File.Exists(arguments.ConfigurationFile))
94+
throw new WarningException($"Could not find config file at '{arguments.ConfigurationFile}'");
95+
arguments.ConfigurationFile = FileSystemHelper.Path.GetFullPath(arguments.ConfigurationFile);
96+
}
97+
else
98+
{
99+
var configFilePath = FileSystemHelper.Path.GetFullPath(FileSystemHelper.Path.Combine(arguments.TargetPath, arguments.ConfigurationFile));
100+
if (!this.fileSystem.File.Exists(configFilePath))
101+
throw new WarningException($"Could not find config file at '{configFilePath}'");
102+
arguments.ConfigurationFile = configFilePath;
103+
}
104+
}
105+
106+
private IEnumerable<string> ResolveFiles(string workingDirectory, ISet<string>? assemblyInfoFiles)
107+
{
108+
if (assemblyInfoFiles == null || assemblyInfoFiles.Count == 0)
109+
{
110+
return [];
111+
}
112+
113+
var stringList = new List<string>();
114+
115+
foreach (var filePattern in assemblyInfoFiles)
116+
{
117+
if (FileSystemHelper.Path.IsPathRooted(filePattern))
118+
{
119+
stringList.Add(filePattern);
120+
}
121+
else
122+
{
123+
var searchRoot = FileSystemHelper.Path.GetFullPath(workingDirectory);
124+
var matchingFiles = this.globbingResolver.Resolve(searchRoot, filePattern);
125+
stringList.AddRange(matchingFiles);
126+
}
127+
}
128+
129+
return stringList;
130+
}
131+
132+
private static Arguments ConvertToArguments(GitVersionSettings settings)
133+
{
134+
var arguments = new Arguments();
135+
136+
// Set target path - prioritize explicit targetpath option over positional argument
137+
arguments.TargetPath = settings.TargetPathOption?.TrimEnd('/', '\\')
138+
?? settings.TargetPath?.TrimEnd('/', '\\')
139+
?? SysEnv.CurrentDirectory;
140+
141+
// Configuration options
142+
arguments.ConfigurationFile = settings.ConfigurationFile;
143+
arguments.ShowConfiguration = settings.ShowConfiguration;
144+
145+
// Handle override configuration
146+
if (settings.OverrideConfiguration != null && settings.OverrideConfiguration.Any())
147+
{
148+
var parser = new OverrideConfigurationOptionParser();
149+
150+
foreach (var kvp in settings.OverrideConfiguration)
151+
{
152+
// Validate the key format - Spectre.Console.Cli should have already parsed key=value correctly
153+
// but we still need to validate against supported properties
154+
var keyValueOption = $"{kvp.Key}={kvp.Value}";
155+
156+
var optionKey = kvp.Key.ToLowerInvariant();
157+
if (!OverrideConfigurationOptionParser.SupportedProperties.Contains(optionKey))
158+
{
159+
throw new WarningException($"Could not parse --override-config option: {keyValueOption}. Unsupported 'key'.");
160+
}
161+
162+
parser.SetValue(optionKey, kvp.Value);
163+
}
164+
165+
arguments.OverrideConfiguration = parser.GetOverrideConfiguration();
166+
}
167+
else
168+
{
169+
arguments.OverrideConfiguration = new Dictionary<object, object?>();
170+
}
171+
172+
// Output options
173+
if (settings.Output != null && settings.Output.Any())
174+
{
175+
foreach (var output in settings.Output)
176+
{
177+
if (Enum.TryParse<OutputType>(output, true, out var outputType))
178+
{
179+
arguments.Output.Add(outputType);
180+
}
181+
}
182+
}
183+
184+
arguments.OutputFile = settings.OutputFile;
185+
arguments.Format = settings.Format;
186+
arguments.ShowVariable = settings.ShowVariable;
187+
188+
// Repository options
189+
arguments.TargetUrl = settings.Url;
190+
arguments.TargetBranch = settings.Branch;
191+
arguments.CommitId = settings.Commit;
192+
arguments.ClonePath = settings.DynamicRepoLocation;
193+
194+
// Authentication
195+
if (!string.IsNullOrWhiteSpace(settings.Username))
196+
{
197+
arguments.Authentication.Username = settings.Username;
198+
}
199+
if (!string.IsNullOrWhiteSpace(settings.Password))
200+
{
201+
arguments.Authentication.Password = settings.Password;
202+
}
203+
204+
// Behavioral flags
205+
arguments.NoFetch = settings.NoFetch;
206+
arguments.NoCache = settings.NoCache;
207+
arguments.NoNormalize = settings.NoNormalize;
208+
arguments.AllowShallow = settings.AllowShallow;
209+
arguments.Diag = settings.Diag;
210+
211+
// Assembly info options
212+
arguments.UpdateAssemblyInfo = settings.UpdateAssemblyInfo;
213+
arguments.EnsureAssemblyInfo = settings.EnsureAssemblyInfo;
214+
arguments.UpdateProjectFiles = settings.UpdateProjectFiles;
215+
arguments.UpdateWixVersionFile = settings.UpdateWixVersionFile;
216+
217+
// Handle assembly info file names
218+
if (settings.UpdateAssemblyInfoFileName != null && settings.UpdateAssemblyInfoFileName.Any())
219+
{
220+
arguments.UpdateAssemblyInfoFileName = settings.UpdateAssemblyInfoFileName.ToHashSet();
221+
}
222+
223+
// Logging
224+
arguments.LogFilePath = settings.LogFilePath;
225+
if (Enum.TryParse<Verbosity>(settings.Verbosity, true, out var verbosity))
226+
{
227+
arguments.Verbosity = verbosity;
228+
}
229+
230+
return arguments;
231+
}
232+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.ComponentModel;
2+
using Spectre.Console.Cli;
3+
4+
namespace GitVersion;
5+
6+
/// <summary>
7+
/// Main GitVersion command with POSIX compliant options
8+
/// </summary>
9+
[Description("Generate version information based on Git repository")]
10+
internal class GitVersionCommand : Command<GitVersionSettings>
11+
{
12+
public override int Execute(CommandContext context, GitVersionSettings settings)
13+
{
14+
// The actual logic is handled by the interceptor
15+
// This just returns success to continue normal flow
16+
return 0;
17+
}
18+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System.ComponentModel;
2+
using Spectre.Console.Cli;
3+
4+
namespace GitVersion;
5+
6+
/// <summary>
7+
/// Settings class for Spectre.Console.Cli with POSIX compliant options
8+
/// </summary>
9+
internal class GitVersionSettings : CommandSettings
10+
{
11+
[CommandArgument(0, "[path]")]
12+
[Description("Path to the Git repository (defaults to current directory)")]
13+
public string? TargetPath { get; set; }
14+
15+
[CommandOption("--config")]
16+
[Description("Path to GitVersion configuration file")]
17+
public string? ConfigurationFile { get; set; }
18+
19+
[CommandOption("--show-config")]
20+
[Description("Display the effective GitVersion configuration and exit")]
21+
public bool ShowConfiguration { get; set; }
22+
23+
[CommandOption("--override-config")]
24+
[Description("Override GitVersion configuration values")]
25+
public Dictionary<string, string>? OverrideConfiguration { get; set; }
26+
27+
[CommandOption("-o|--output")]
28+
[Description("Output format (json, file, buildserver, console)")]
29+
public string[]? Output { get; set; }
30+
31+
[CommandOption("--output-file")]
32+
[Description("Output file when using file output")]
33+
public string? OutputFile { get; set; }
34+
35+
[CommandOption("-f|--format")]
36+
[Description("Format string for version output")]
37+
public string? Format { get; set; }
38+
39+
[CommandOption("--show-variable")]
40+
[Description("Show a specific GitVersion variable")]
41+
public string? ShowVariable { get; set; }
42+
43+
[CommandOption("--url")]
44+
[Description("Remote repository URL")]
45+
public string? Url { get; set; }
46+
47+
[CommandOption("-b|--branch")]
48+
[Description("Target branch name")]
49+
public string? Branch { get; set; }
50+
51+
[CommandOption("-c|--commit")]
52+
[Description("Target commit SHA")]
53+
public string? Commit { get; set; }
54+
55+
[CommandOption("--target-path")]
56+
[Description("Same as positional path argument")]
57+
public string? TargetPathOption { get; set; }
58+
59+
[CommandOption("--dynamic-repo-location")]
60+
[Description("Path to clone remote repository")]
61+
public string? DynamicRepoLocation { get; set; }
62+
63+
[CommandOption("-u|--username")]
64+
[Description("Username for remote repository authentication")]
65+
public string? Username { get; set; }
66+
67+
[CommandOption("-p|--password")]
68+
[Description("Password for remote repository authentication")]
69+
public string? Password { get; set; }
70+
71+
[CommandOption("--no-fetch")]
72+
[Description("Disable Git fetch")]
73+
public bool NoFetch { get; set; }
74+
75+
[CommandOption("--no-cache")]
76+
[Description("Disable GitVersion result caching")]
77+
public bool NoCache { get; set; }
78+
79+
[CommandOption("--no-normalize")]
80+
[Description("Disable branch name normalization")]
81+
public bool NoNormalize { get; set; }
82+
83+
[CommandOption("--allow-shallow")]
84+
[Description("Allow operation on shallow Git repositories")]
85+
public bool AllowShallow { get; set; }
86+
87+
[CommandOption("--diag")]
88+
[Description("Enable diagnostic output")]
89+
public bool Diag { get; set; }
90+
91+
[CommandOption("--update-assembly-info")]
92+
[Description("Update AssemblyInfo files")]
93+
public bool UpdateAssemblyInfo { get; set; }
94+
95+
[CommandOption("--ensure-assembly-info")]
96+
[Description("Ensure AssemblyInfo files exist")]
97+
public bool EnsureAssemblyInfo { get; set; }
98+
99+
[CommandOption("--update-assembly-info-filename")]
100+
[Description("Specific AssemblyInfo files to update")]
101+
public string[]? UpdateAssemblyInfoFileName { get; set; }
102+
103+
[CommandOption("--update-project-files")]
104+
[Description("Update MSBuild project files")]
105+
public bool UpdateProjectFiles { get; set; }
106+
107+
[CommandOption("--update-wix-version-file")]
108+
[Description("Update WiX version file")]
109+
public bool UpdateWixVersionFile { get; set; }
110+
111+
[CommandOption("-l|--log-file")]
112+
[Description("Path to log file")]
113+
public string? LogFilePath { get; set; }
114+
115+
[CommandOption("-v|--verbosity")]
116+
[Description("Logging verbosity (quiet, minimal, normal, verbose, diagnostic)")]
117+
public string? Verbosity { get; set; }
118+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace GitVersion;
2+
3+
/// <summary>
4+
/// Storage for parse results
5+
/// </summary>
6+
internal class ParseResultStorage
7+
{
8+
private Arguments? result;
9+
10+
public void SetResult(Arguments arguments) => this.result = arguments;
11+
public Arguments? GetResult() => this.result;
12+
}

0 commit comments

Comments
 (0)