Skip to content

Commit e2e6b60

Browse files
authored
Merge pull request #4413 from Bi0T1N/dotenv_support
Support for Dotenv output format
2 parents 7347cb0 + 2602d51 commit e2e6b60

File tree

8 files changed

+177
-4
lines changed

8 files changed

+177
-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

+21
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,24 @@ 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).
24+
Below are some examples of using the Dotenv format in the Unix command line:
25+
```bash
26+
# Output version variables in Dotenv format
27+
gitversion /output dotenv
28+
29+
# Show only a subset of the version variables in Dotenv format
30+
gitversion /output dotenv | grep -i "prerelease"
31+
32+
# Show only a subset of the version variables that match the regex in Dotenv format
33+
gitversion /output dotenv | grep -iE "major|sha=|_prerelease"
34+
35+
# Write version variables in Dotenv format into a file
36+
gitversion /output dotenv > gitversion.env
37+
```

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}{SysEnv.NewLine}");
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(SysEnv.NewLine).Length - 1; // ignore last item that also ends with the newline string
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}{SysEnv.NewLine}");
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(SysEnv.NewLine).Length - 1; // ignore last item that also ends with the newline string
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)