Skip to content

Commit bafb3a6

Browse files
Bi0T1Narturcic
authored andcommitted
Support for dotenv output format
closes #4174
1 parent 7347cb0 commit bafb3a6

File tree

9 files changed

+175
-4
lines changed

9 files changed

+175
-4
lines changed

docs/input/docs/usage/cli/arguments.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ GitVersion [path]
3030

3131
/targetpath Same as 'path', but not positional
3232
/output Determines the output to the console. Can be either 'json',
33-
'file' or 'buildserver', will default to 'json'.
33+
'file', 'buildserver' or 'dotenv', will default to 'json'.
3434
/outputfile Path to output file. It is used in combination with /output
3535
'file'.
3636
/showvariable Used in conjunction with /output json, will output just a

docs/input/docs/usage/cli/output.md

+7
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ out the variables to whatever build server it is running in. You can then use
1414
those variables in your build scripts or run different tools to create versioned
1515
NuGet packages or whatever you would like to do. See [build
1616
servers](/docs/reference/build-servers) for more information about this.
17+
18+
You can even store the [variables](/docs/reference/variables) in a Dotenv file
19+
and load it to have the variables available in your environment.
20+
For that you have to run `GitVersion.exe /output dotenv` and store the output
21+
into e.g. a `gitversion.env` file. These files can also be passed around in CI environments
22+
like [GitHub](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#passing-values-between-steps-and-jobs-in-a-workflow)
23+
or [GitLab](https://docs.gitlab.com/ee/ci/variables/#pass-an-environment-variable-to-another-job).

new-cli/command.md

+12
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ cat gitversion.json | gitversion output buildserver
5353
# Read version variables from stdin and write to Jenkins.
5454
cat gitversion.json | gitversion output buildserver --buildserver Jenkins
5555

56+
# Output version variables in Dotenv format
57+
gitversion /output dotenv
58+
59+
# Show only a subset of the version variables in Dotenv format (Unix syntax)
60+
gitversion /output dotenv | grep -i "prerelease"
61+
62+
# Show only a subset of the version variables that match the regex in Dotenv format (Unix syntax)
63+
gitversion /output dotenv | grep -iE "major|sha=|_prerelease"
64+
65+
# Write version variables in Dotenv format into a file
66+
gitversion /output dotenv > gitversion.env
67+
5668
# Read version variables from stdin and write to globbed .wxi files.
5769
cat gitversion.json | gitversion output wix --path ./**/*.wxi
5870

src/GitVersion.App.Tests/ArgumentParserTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public void UnknownOutputShouldThrow()
103103
{
104104
var exception = Assert.Throws<WarningException>(() => this.argumentParser.ParseArguments("targetDirectoryPath -output invalid_value"));
105105
exception.ShouldNotBeNull();
106-
exception.Message.ShouldBe("Value 'invalid_value' cannot be parsed as output type, please use 'json', 'file' or 'buildserver'");
106+
exception.Message.ShouldBe("Value 'invalid_value' cannot be parsed as output type, please use 'json', 'file', 'buildserver' or 'dotenv'");
107107
}
108108

109109
[Test]

src/GitVersion.App/ArgumentParser.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ private static void ParseOutput(Arguments arguments, IEnumerable<string>? values
432432
{
433433
if (!Enum.TryParse(v, true, out OutputType outputType))
434434
{
435-
throw new WarningException($"Value '{v}' cannot be parsed as output type, please use 'json', 'file' or 'buildserver'");
435+
throw new WarningException($"Value '{v}' cannot be parsed as output type, please use 'json', 'file', 'buildserver' or 'dotenv'");
436436
}
437437

438438
arguments.Output.Add(outputType);

src/GitVersion.Core/Options/OutputType.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ public enum OutputType
44
{
55
BuildServer,
66
Json,
7-
File
7+
File,
8+
DotEnv
89
}

src/GitVersion.Core/PublicAPI.Shipped.txt

+1
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ GitVersion.OutputType
417417
GitVersion.OutputType.BuildServer = 0 -> GitVersion.OutputType
418418
GitVersion.OutputType.File = 2 -> GitVersion.OutputType
419419
GitVersion.OutputType.Json = 1 -> GitVersion.OutputType
420+
GitVersion.OutputType.DotEnv = 3 -> GitVersion.OutputType
420421
GitVersion.OutputVariables.GitVersionVariables
421422
GitVersion.OutputVariables.GitVersionVariables.AssemblySemFileVer.get -> string?
422423
GitVersion.OutputVariables.GitVersionVariables.AssemblySemFileVer.init -> void

src/GitVersion.Output.Tests/Output/FormatArgumentTests.cs

+127
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,126 @@ public void ShouldOutputFormatWithEnvironmentVariablesTests(string format, strin
6868
output.ShouldBeEquivalentTo(expectedValue);
6969
}
7070

71+
[TestCase("Major", "1")]
72+
[TestCase("MajorMinorPatch", "1.1.0")]
73+
[TestCase("SemVer", "1.1.0-foo.1")]
74+
[TestCase("PreReleaseTagWithDash", "-foo.1")]
75+
[TestCase("AssemblySemFileVer", "1.1.0.0")]
76+
[TestCase("BranchName", "feature/foo")]
77+
[TestCase("FullSemVer", "1.1.0-foo.1+1")]
78+
public void ShouldOutputDotEnvEntries(string variableName, string expectedValue)
79+
{
80+
var fixture = CreateTestRepository();
81+
82+
var consoleBuilder = new StringBuilder();
83+
IConsole consoleAdapter = new TestConsoleAdapter(consoleBuilder);
84+
85+
var sp = ConfigureServices(services =>
86+
{
87+
var options = Options.Create(new GitVersionOptions { WorkingDirectory = fixture.RepositoryPath, RepositoryInfo = { TargetBranch = fixture.Repository.Head.CanonicalName }, Output = { OutputType.DotEnv } });
88+
var repository = fixture.Repository.ToGitRepository();
89+
90+
services.AddSingleton(options);
91+
services.AddSingleton(repository);
92+
services.AddSingleton(consoleAdapter);
93+
});
94+
95+
var versionVariables = sp.GetRequiredService<IGitVersionCalculateTool>().CalculateVersionVariables();
96+
var outputGenerator = sp.GetRequiredService<IOutputGenerator>();
97+
98+
outputGenerator.Execute(versionVariables, new());
99+
var output = consoleBuilder.ToString();
100+
output.ShouldContain("GitVersion_" + variableName + "=" + expectedValue + "\n");
101+
}
102+
103+
[TestCase]
104+
public void ShouldOutputAllCalculatedVariablesAsDotEnvEntries()
105+
{
106+
var fixture = CreateTestRepository();
107+
108+
var consoleBuilder = new StringBuilder();
109+
IConsole consoleAdapter = new TestConsoleAdapter(consoleBuilder);
110+
111+
var sp = ConfigureServices(services =>
112+
{
113+
var options = Options.Create(new GitVersionOptions { WorkingDirectory = fixture.RepositoryPath, RepositoryInfo = { TargetBranch = fixture.Repository.Head.CanonicalName }, Output = { OutputType.DotEnv } });
114+
var repository = fixture.Repository.ToGitRepository();
115+
116+
services.AddSingleton(options);
117+
services.AddSingleton(repository);
118+
services.AddSingleton(consoleAdapter);
119+
});
120+
121+
var versionVariables = sp.GetRequiredService<IGitVersionCalculateTool>().CalculateVersionVariables();
122+
var outputGenerator = sp.GetRequiredService<IOutputGenerator>();
123+
124+
outputGenerator.Execute(versionVariables, new());
125+
var output = consoleBuilder.ToString();
126+
var totalOutputLines = output.Split("\n").Length - 1; // ignore last item that also ends with \n
127+
Assert.That(totalOutputLines, Is.EqualTo(versionVariables.Count()));
128+
}
129+
130+
[TestCase("Major", "0")]
131+
[TestCase("MajorMinorPatch", "0.0.1")]
132+
[TestCase("SemVer", "0.0.1-1")]
133+
[TestCase("BuildMetaData", "''")]
134+
[TestCase("AssemblySemVer", "0.0.1.0")]
135+
[TestCase("PreReleaseTagWithDash", "-1")]
136+
[TestCase("BranchName", "main")]
137+
[TestCase("PreReleaseLabel", "''")]
138+
[TestCase("PreReleaseLabelWithDash", "''")]
139+
public void ShouldOutputAllDotEnvEntriesEvenForMinimalRepositories(string variableName, string expectedValue)
140+
{
141+
var fixture = CreateMinimalTestRepository();
142+
143+
var consoleBuilder = new StringBuilder();
144+
IConsole consoleAdapter = new TestConsoleAdapter(consoleBuilder);
145+
146+
var sp = ConfigureServices(services =>
147+
{
148+
var options = Options.Create(new GitVersionOptions { WorkingDirectory = fixture.RepositoryPath, RepositoryInfo = { TargetBranch = fixture.Repository.Head.CanonicalName }, Output = { OutputType.DotEnv } });
149+
var repository = fixture.Repository.ToGitRepository();
150+
151+
services.AddSingleton(options);
152+
services.AddSingleton(repository);
153+
services.AddSingleton(consoleAdapter);
154+
});
155+
156+
var versionVariables = sp.GetRequiredService<IGitVersionCalculateTool>().CalculateVersionVariables();
157+
var outputGenerator = sp.GetRequiredService<IOutputGenerator>();
158+
159+
outputGenerator.Execute(versionVariables, new());
160+
var output = consoleBuilder.ToString();
161+
output.ShouldContain("GitVersion_" + variableName + "=" + expectedValue + "\n");
162+
}
163+
164+
[TestCase]
165+
public void ShouldOutputAllCalculatedVariablesAsDotEnvEntriesEvenForMinimalRepositories()
166+
{
167+
var fixture = CreateMinimalTestRepository();
168+
169+
var consoleBuilder = new StringBuilder();
170+
IConsole consoleAdapter = new TestConsoleAdapter(consoleBuilder);
171+
172+
var sp = ConfigureServices(services =>
173+
{
174+
var options = Options.Create(new GitVersionOptions { WorkingDirectory = fixture.RepositoryPath, RepositoryInfo = { TargetBranch = fixture.Repository.Head.CanonicalName }, Output = { OutputType.DotEnv } });
175+
var repository = fixture.Repository.ToGitRepository();
176+
177+
services.AddSingleton(options);
178+
services.AddSingleton(repository);
179+
services.AddSingleton(consoleAdapter);
180+
});
181+
182+
var versionVariables = sp.GetRequiredService<IGitVersionCalculateTool>().CalculateVersionVariables();
183+
var outputGenerator = sp.GetRequiredService<IOutputGenerator>();
184+
185+
outputGenerator.Execute(versionVariables, new());
186+
var output = consoleBuilder.ToString();
187+
var totalOutputLines = output.Split("\n").Length - 1; // ignore last item that also ends with \n
188+
Assert.That(totalOutputLines, Is.EqualTo(versionVariables.Count()));
189+
}
190+
71191
private static EmptyRepositoryFixture CreateTestRepository()
72192
{
73193
var fixture = new EmptyRepositoryFixture();
@@ -80,4 +200,11 @@ private static EmptyRepositoryFixture CreateTestRepository()
80200
_ = fixture.Repository.MakeACommit();
81201
return fixture;
82202
}
203+
204+
private static EmptyRepositoryFixture CreateMinimalTestRepository()
205+
{
206+
var fixture = new EmptyRepositoryFixture();
207+
_ = fixture.Repository.MakeACommit();
208+
return fixture;
209+
}
83210
}

src/GitVersion.Output/OutputGenerator/OutputGenerator.cs

+23
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,34 @@ internal sealed class OutputGenerator(
2828
public void Execute(GitVersionVariables variables, OutputContext context)
2929
{
3030
var gitVersionOptions = this.options.Value;
31+
3132
if (gitVersionOptions.Output.Contains(OutputType.BuildServer))
3233
{
3334
this.buildAgent.WriteIntegration(this.console.WriteLine, variables, context.UpdateBuildNumber ?? true);
3435
}
3536

37+
if (gitVersionOptions.Output.Contains(OutputType.DotEnv))
38+
{
39+
List<string> dotEnvEntries = [];
40+
foreach (var (key, value) in variables.OrderBy(x => x.Key))
41+
{
42+
string prefixedKey = "GitVersion_" + key;
43+
string environmentValue = "''";
44+
if (!value.IsNullOrEmpty())
45+
{
46+
environmentValue = value;
47+
}
48+
dotEnvEntries.Add(prefixedKey + "=" + environmentValue);
49+
}
50+
51+
foreach(var dotEnvEntry in dotEnvEntries)
52+
{
53+
this.console.WriteLine(dotEnvEntry);
54+
}
55+
56+
return;
57+
}
58+
3659
var json = this.serializer.ToJson(variables);
3760
if (gitVersionOptions.Output.Contains(OutputType.File))
3861
{

0 commit comments

Comments
 (0)