diff --git a/docs/input/docs/usage/cli/arguments.md b/docs/input/docs/usage/cli/arguments.md
index 1fb995114e..93cdd7445e 100644
--- a/docs/input/docs/usage/cli/arguments.md
+++ b/docs/input/docs/usage/cli/arguments.md
@@ -37,6 +37,10 @@ GitVersion [path]
/showvariable Used in conjuntion with /output json, will output just a
particular variable. E.g. /output json /showvariable SemVer
- will output `1.2.3+beta.4`
+ /format Used in conjuntion with /output json, will output a format
+ containing version variables.
+ E.g. /output json /format {SemVer} - will output `1.2.3+beta.4`
+ /output json /format {Major}.{Minor} - will output `1.2`
/l Path to logfile.
/config Path to config file (defaults to GitVersion.yml)
/showconfig Outputs the effective GitVersion config (defaults + custom
diff --git a/src/GitVersion.App.Tests/ArgumentParserTests.cs b/src/GitVersion.App.Tests/ArgumentParserTests.cs
index 7b16cd7a83..7f31c81f21 100644
--- a/src/GitVersion.App.Tests/ArgumentParserTests.cs
+++ b/src/GitVersion.App.Tests/ArgumentParserTests.cs
@@ -726,4 +726,18 @@ public void ArbitraryArgumentsRemotePasswordDefinedSetsPassword()
var arguments = this.argumentParser.ParseArguments("-nocache");
arguments.Authentication.Password.ShouldBe("value");
}
+
+ [Test]
+ public void EnsureShowVariableIsSet()
+ {
+ var arguments = this.argumentParser.ParseArguments("-showvariable SemVer");
+ arguments.ShowVariable.ShouldBe("SemVer");
+ }
+
+ [Test]
+ public void EnsureFormatIsSet()
+ {
+ var arguments = this.argumentParser.ParseArguments("-format {Major}.{Minor}.{Patch}");
+ arguments.Format.ShouldBe("{Major}.{Minor}.{Patch}");
+ }
}
diff --git a/src/GitVersion.App.Tests/GitVersion.App.Tests.csproj b/src/GitVersion.App.Tests/GitVersion.App.Tests.csproj
index bab87578b7..f1a123a254 100644
--- a/src/GitVersion.App.Tests/GitVersion.App.Tests.csproj
+++ b/src/GitVersion.App.Tests/GitVersion.App.Tests.csproj
@@ -16,6 +16,9 @@
+
+ Helpers\TestConsoleAdapter.cs
+
diff --git a/src/GitVersion.App.Tests/HelpWriterTests.cs b/src/GitVersion.App.Tests/HelpWriterTests.cs
index 858cbe1859..df75c5510a 100644
--- a/src/GitVersion.App.Tests/HelpWriterTests.cs
+++ b/src/GitVersion.App.Tests/HelpWriterTests.cs
@@ -32,7 +32,9 @@ public void AllArgsAreInHelp()
{ nameof(Arguments.Verbosity), "/verbosity" },
{ nameof(Arguments.CommitId), "/c" },
{ nameof(Arguments.ShowConfiguration), "/showconfig" },
- { nameof(Arguments.OverrideConfiguration), "/overrideconfig" }
+ { nameof(Arguments.OverrideConfiguration), "/overrideconfig" },
+ { nameof(Arguments.ShowVariable), "/showvariable" },
+ { nameof(Arguments.Format), "/format" }
};
string helpText = string.Empty;
diff --git a/src/GitVersion.App/ArgumentParser.cs b/src/GitVersion.App/ArgumentParser.cs
index 530f18c288..60c0e79f1f 100644
--- a/src/GitVersion.App/ArgumentParser.cs
+++ b/src/GitVersion.App/ArgumentParser.cs
@@ -236,6 +236,12 @@ private static bool ParseSwitches(Arguments arguments, string? name, IReadOnlyLi
return true;
}
+ if (name.IsSwitch("format"))
+ {
+ ParseFormat(arguments, value);
+ return true;
+ }
+
if (name.IsSwitch("output"))
{
ParseOutput(arguments, values);
@@ -371,6 +377,23 @@ private static void ParseShowVariable(Arguments arguments, string? value, string
arguments.ShowVariable = versionVariable;
}
+ private static void ParseFormat(Arguments arguments, string? value)
+ {
+ if (value.IsNullOrWhiteSpace())
+ {
+ throw new WarningException("Format requires a valid format string. Available variables are: " + string.Join(", ", VersionVariables.AvailableVariables));
+ }
+
+ var foundVariable = VersionVariables.AvailableVariables.Any(variable => value.Contains(variable, StringComparison.CurrentCultureIgnoreCase));
+
+ if (!foundVariable)
+ {
+ throw new WarningException("Format requires a valid format string. Available variables are: " + string.Join(", ", VersionVariables.AvailableVariables));
+ }
+
+ arguments.Format = value;
+ }
+
private static void ParseEnsureAssemblyInfo(Arguments arguments, string? value)
{
arguments.EnsureAssemblyInfo = true;
diff --git a/src/GitVersion.App/Arguments.cs b/src/GitVersion.App/Arguments.cs
index 34b87eb791..d5c6c845e0 100644
--- a/src/GitVersion.App/Arguments.cs
+++ b/src/GitVersion.App/Arguments.cs
@@ -30,6 +30,7 @@ public class Arguments
public string? LogFilePath;
public string? ShowVariable;
+ public string? Format;
public string? OutputFile;
public ISet Output = new HashSet();
public Verbosity Verbosity = Verbosity.Normal;
@@ -92,6 +93,7 @@ public GitVersionOptions ToOptions()
LogFilePath = LogFilePath,
ShowVariable = ShowVariable,
+ Format = Format,
Verbosity = Verbosity,
Output = Output,
OutputFile = OutputFile
diff --git a/src/GitVersion.App/PublicAPI.Unshipped.txt b/src/GitVersion.App/PublicAPI.Unshipped.txt
index c77bdd66a0..db97d37558 100644
--- a/src/GitVersion.App/PublicAPI.Unshipped.txt
+++ b/src/GitVersion.App/PublicAPI.Unshipped.txt
@@ -11,6 +11,7 @@ GitVersion.Arguments.CommitId -> string?
GitVersion.Arguments.ConfigurationFile -> string?
GitVersion.Arguments.Diag -> bool
GitVersion.Arguments.EnsureAssemblyInfo -> bool
+GitVersion.Arguments.Format -> string?
GitVersion.Arguments.Init -> bool
GitVersion.Arguments.IsHelp -> bool
GitVersion.Arguments.IsVersion -> bool
diff --git a/src/GitVersion.BuildAgents.Tests/AssemblyParallelizable.cs b/src/GitVersion.BuildAgents.Tests/AssemblyParallelizable.cs
new file mode 100644
index 0000000000..fdd365b7e2
--- /dev/null
+++ b/src/GitVersion.BuildAgents.Tests/AssemblyParallelizable.cs
@@ -0,0 +1 @@
+[assembly: Parallelizable(ParallelScope.Fixtures)]
diff --git a/src/GitVersion.Core.Tests/GitVersion.Core.Tests.csproj b/src/GitVersion.Core.Tests/GitVersion.Core.Tests.csproj
index 0946d95e37..430622a90d 100644
--- a/src/GitVersion.Core.Tests/GitVersion.Core.Tests.csproj
+++ b/src/GitVersion.Core.Tests/GitVersion.Core.Tests.csproj
@@ -24,5 +24,6 @@
+
diff --git a/src/GitVersion.App.Tests/Helpers/TestConsoleAdapter.cs b/src/GitVersion.Core.Tests/Helpers/TestConsoleAdapter.cs
similarity index 93%
rename from src/GitVersion.App.Tests/Helpers/TestConsoleAdapter.cs
rename to src/GitVersion.Core.Tests/Helpers/TestConsoleAdapter.cs
index b5ce351058..557d321a78 100644
--- a/src/GitVersion.App.Tests/Helpers/TestConsoleAdapter.cs
+++ b/src/GitVersion.Core.Tests/Helpers/TestConsoleAdapter.cs
@@ -1,6 +1,6 @@
using GitVersion.Logging;
-namespace GitVersion.App.Tests;
+namespace GitVersion.Core.Tests.Helpers;
public class TestConsoleAdapter : IConsole
{
diff --git a/src/GitVersion.Core/GitVersion.Core.csproj b/src/GitVersion.Core/GitVersion.Core.csproj
index 2cbdcc09bc..3b4875ef34 100644
--- a/src/GitVersion.Core/GitVersion.Core.csproj
+++ b/src/GitVersion.Core/GitVersion.Core.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/GitVersion.Core/Options/GitVersionOptions.cs b/src/GitVersion.Core/Options/GitVersionOptions.cs
index e0a364ff67..42feb102a7 100644
--- a/src/GitVersion.Core/Options/GitVersionOptions.cs
+++ b/src/GitVersion.Core/Options/GitVersionOptions.cs
@@ -20,6 +20,7 @@ public class GitVersionOptions
public string? LogFilePath;
public string? ShowVariable;
+ public string? Format;
public string? OutputFile;
public ISet Output = new HashSet();
public Verbosity Verbosity = Verbosity.Normal;
diff --git a/src/GitVersion.Core/PublicAPI.Unshipped.txt b/src/GitVersion.Core/PublicAPI.Unshipped.txt
index 8ecc79065b..5b4c010acc 100644
--- a/src/GitVersion.Core/PublicAPI.Unshipped.txt
+++ b/src/GitVersion.Core/PublicAPI.Unshipped.txt
@@ -386,6 +386,7 @@ GitVersion.GitVersionOptions.AssemblySettingsInfo.get -> GitVersion.AssemblySett
GitVersion.GitVersionOptions.AuthenticationInfo.get -> GitVersion.AuthenticationInfo!
GitVersion.GitVersionOptions.ConfigurationInfo.get -> GitVersion.ConfigurationInfo!
GitVersion.GitVersionOptions.Diag -> bool
+GitVersion.GitVersionOptions.Format -> string?
GitVersion.GitVersionOptions.GitVersionOptions() -> void
GitVersion.GitVersionOptions.Init -> bool
GitVersion.GitVersionOptions.IsHelp -> bool
diff --git a/src/GitVersion.Output.Tests/AssemblyParallelizable.cs b/src/GitVersion.Output.Tests/AssemblyParallelizable.cs
new file mode 100644
index 0000000000..fdd365b7e2
--- /dev/null
+++ b/src/GitVersion.Output.Tests/AssemblyParallelizable.cs
@@ -0,0 +1 @@
+[assembly: Parallelizable(ParallelScope.Fixtures)]
diff --git a/src/GitVersion.Output.Tests/Output/FormatArgumentTests.cs b/src/GitVersion.Output.Tests/Output/FormatArgumentTests.cs
new file mode 100644
index 0000000000..46a65eb8f2
--- /dev/null
+++ b/src/GitVersion.Output.Tests/Output/FormatArgumentTests.cs
@@ -0,0 +1,95 @@
+using GitVersion.Core.Tests.Helpers;
+using GitVersion.Logging;
+using GitVersion.Output.OutputGenerator;
+using LibGit2Sharp;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+
+namespace GitVersion.Core.Tests;
+
+[TestFixture]
+public class FormatArgumentTests : TestBase
+{
+ [TestCase("{SemVer}", "1.1.0-foo.1")]
+ [TestCase("{Major}.{Minor}", "1.1")]
+ [TestCase("{Major}.{Minor}.{Patch}", "1.1.0")]
+ [TestCase("{Major}.{Minor}.{Patch}.{PreReleaseTag}", "1.1.0.foo.1")]
+ public void ShouldOutputFormatTests(string format, string expectedValue)
+ {
+ var fixture = CreateTestRepository();
+
+ var consoleBuilder = new StringBuilder();
+ IConsole consoleAdapter = new TestConsoleAdapter(consoleBuilder);
+
+ var sp = ConfigureServices(services =>
+ {
+ var options = Options.Create(new GitVersionOptions
+ {
+ WorkingDirectory = fixture.RepositoryPath,
+ RepositoryInfo = { TargetBranch = fixture.Repository.Head.CanonicalName },
+ Format = format,
+ Output = { OutputType.Json }
+ });
+ var repository = fixture.Repository.ToGitRepository();
+
+ services.AddSingleton(options);
+ services.AddSingleton(repository);
+ services.AddSingleton(consoleAdapter);
+ });
+
+ var versionVariables = sp.GetRequiredService().CalculateVersionVariables();
+ var outputGenerator = sp.GetRequiredService();
+
+ outputGenerator.Execute(versionVariables, new OutputContext());
+ var output = consoleBuilder.ToString().Replace("\n", "").Replace("\r", "");
+ output.ShouldBeEquivalentTo(expectedValue);
+ }
+
+ [TestCase("{Major}.{Minor}.{env:CustomVar}", "1.1.foo")]
+ [TestCase("{Major}.{Minor}.{Patch}.{env:CustomVar}", "1.1.0.foo")]
+ public void ShouldOutputFormatWithEnvironmentVariablesTests(string format, string expectedValue)
+ {
+ var fixture = CreateTestRepository();
+ var consoleBuilder = new StringBuilder();
+ IConsole console = new TestConsoleAdapter(consoleBuilder);
+ IEnvironment environment = new TestEnvironment();
+ environment.SetEnvironmentVariable("CustomVar", "foo");
+
+ var sp = ConfigureServices(services =>
+ {
+ var options = Options.Create(new GitVersionOptions
+ {
+ WorkingDirectory = fixture.RepositoryPath,
+ RepositoryInfo = { TargetBranch = fixture.Repository.Head.CanonicalName },
+ Format = format,
+ Output = { OutputType.Json }
+ });
+ var repository = fixture.Repository.ToGitRepository();
+
+ services.AddSingleton(options);
+ services.AddSingleton(repository);
+ services.AddSingleton(console);
+ services.AddSingleton(environment);
+ });
+
+ var versionVariables = sp.GetRequiredService().CalculateVersionVariables();
+ var outputGenerator = sp.GetRequiredService();
+
+ outputGenerator.Execute(versionVariables, new OutputContext());
+ var output = consoleBuilder.ToString().Replace("\n", "").Replace("\r", "");
+ output.ShouldBeEquivalentTo(expectedValue);
+ }
+
+ private static EmptyRepositoryFixture CreateTestRepository()
+ {
+ var fixture = new EmptyRepositoryFixture();
+ _ = fixture.Repository.MakeACommit();
+ Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("develop"));
+ var secondCommit = fixture.Repository.MakeACommit();
+ _ = fixture.Repository.Tags.Add("1.0.0", secondCommit);
+ var featureBranch = fixture.Repository.CreateBranch("feature/foo");
+ Commands.Checkout(fixture.Repository, featureBranch);
+ _ = fixture.Repository.MakeACommit();
+ return fixture;
+ }
+}
diff --git a/src/GitVersion.Output/OutputGenerator/OutputGenerator.cs b/src/GitVersion.Output/OutputGenerator/OutputGenerator.cs
index d324fd921e..9322ab1c5d 100644
--- a/src/GitVersion.Output/OutputGenerator/OutputGenerator.cs
+++ b/src/GitVersion.Output/OutputGenerator/OutputGenerator.cs
@@ -15,13 +15,15 @@ public sealed class OutputGenerator : IOutputGenerator
{
private readonly IConsole console;
private readonly IFileSystem fileSystem;
+ private readonly IEnvironment environment;
private readonly IOptions options;
private readonly ICurrentBuildAgent buildAgent;
- public OutputGenerator(ICurrentBuildAgent buildAgent, IConsole console, IFileSystem fileSystem, IOptions options)
+ public OutputGenerator(ICurrentBuildAgent buildAgent, IConsole console, IFileSystem fileSystem, IEnvironment environment, IOptions options)
{
this.console = console.NotNull();
this.fileSystem = fileSystem.NotNull();
+ this.environment = environment;
this.options = options.NotNull();
this.buildAgent = buildAgent.NotNull();
}
@@ -41,6 +43,34 @@ public void Execute(VersionVariables variables, OutputContext context)
if (!gitVersionOptions.Output.Contains(OutputType.Json)) return;
+ if (gitVersionOptions.ShowVariable is null && gitVersionOptions.Format is null)
+ {
+ this.console.WriteLine(variables.ToString());
+ return;
+ }
+
+ if (gitVersionOptions.ShowVariable is not null && gitVersionOptions.Format is not null)
+ {
+ throw new WarningException("Cannot specify both /showvariable and /format");
+ }
+ if (gitVersionOptions.ShowVariable is not null)
+ {
+ if (!variables.TryGetValue(gitVersionOptions.ShowVariable, out var part))
+ {
+ throw new WarningException($"'{gitVersionOptions.ShowVariable}' variable does not exist");
+ }
+
+ this.console.WriteLine(part);
+ return;
+ }
+ if (gitVersionOptions.Format is not null)
+ {
+ var format = gitVersionOptions.Format;
+ var formatted = format.FormatWith(variables, environment);
+ this.console.WriteLine(formatted);
+ return;
+ }
+
switch (gitVersionOptions.ShowVariable)
{
case null:
diff --git a/src/GitVersion.Output/PublicAPI.Unshipped.txt b/src/GitVersion.Output/PublicAPI.Unshipped.txt
index c568f0b0fb..7f53efbd7b 100644
--- a/src/GitVersion.Output/PublicAPI.Unshipped.txt
+++ b/src/GitVersion.Output/PublicAPI.Unshipped.txt
@@ -47,7 +47,7 @@ GitVersion.Output.OutputGenerator.OutputContext.WorkingDirectory.get -> string!
GitVersion.Output.OutputGenerator.OutputGenerator
GitVersion.Output.OutputGenerator.OutputGenerator.Dispose() -> void
GitVersion.Output.OutputGenerator.OutputGenerator.Execute(GitVersion.OutputVariables.VersionVariables! variables, GitVersion.Output.OutputGenerator.OutputContext context) -> void
-GitVersion.Output.OutputGenerator.OutputGenerator.OutputGenerator(GitVersion.Agents.ICurrentBuildAgent! buildAgent, GitVersion.Logging.IConsole! console, GitVersion.IFileSystem! fileSystem, Microsoft.Extensions.Options.IOptions! options) -> void
+GitVersion.Output.OutputGenerator.OutputGenerator.OutputGenerator(GitVersion.Agents.ICurrentBuildAgent! buildAgent, GitVersion.Logging.IConsole! console, GitVersion.IFileSystem! fileSystem, GitVersion.IEnvironment! environment, Microsoft.Extensions.Options.IOptions! options) -> void
GitVersion.Output.WixUpdater.IWixVersionFileUpdater
GitVersion.Output.WixUpdater.WixVersionContext
GitVersion.Output.WixUpdater.WixVersionContext.WixVersionContext() -> void