Skip to content

Commit fdae670

Browse files
authored
Merge pull request #3619 from Applesauce314/feature/uniqueNamespaceGeneration
Unique namespace generation for GitVersionInformation class
2 parents d24e970 + ad52bec commit fdae670

File tree

15 files changed

+218
-21
lines changed

15 files changed

+218
-21
lines changed

docs/input/docs/usage/msbuild.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,14 +258,26 @@ For SDK-style projects, `UpdateVersionProperties` controls setting the default
258258
variables: `Version`, `VersionPrefix`, `VersionSuffix`, `PackageVersion`,
259259
`InformationalVersion`, `AssemblyVersion` and `FileVersion`.
260260

261+
### Namespace generation
262+
263+
You can configure GitVersion to generate the `GitVersionInformation` class in a namespace that matches the current assembly. By default this class is created in the global namespace. If `UseProjectNamespaceForGitVersionInformation` is set to true, the `GitVersionInfomation` class will instead be generated in a namespace matching the current project. If the property `<RootNamespace>` is set that value will be used, otherwise the name of the project file is used.
264+
265+
```xml
266+
<PropertyGroup>
267+
...
268+
<UseProjectNamespaceForGitVersionInformation>true</UseProjectNamespaceForGitVersionInformation>
269+
...
270+
</PropertyGroup>
271+
```
272+
261273
## Extra properties
262274

263-
There are properties that correspont to certain
275+
There are properties that correspont to certain
264276
[command line arguments](/docs/usage/cli/arguments) for GetVersion task.
265-
In particular, setting `GitVersion_NoFetchEnabled` to `true` disables `git fetch`
266-
during version calculation, setting `GitVersion_NoNormalizeEnabled` to `true` disables
277+
In particular, setting `GitVersion_NoFetchEnabled` to `true` disables `git fetch`
278+
during version calculation, setting `GitVersion_NoNormalizeEnabled` to `true` disables
267279
normalize step on a build server, setting `GitVersion_NoCacheEnabled` to `true`
268-
makes GetVersion ignore cache. All the rest command line arguments can be passed via
280+
makes GetVersion ignore cache. All the rest command line arguments can be passed via
269281
`GitVersion_CommandLineArguments` variable.
270282

271283
## My Git repository requires authentication. What should I do?

src/GitVersion.Core/Options/GitVersionOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class GitVersionOptions
1313
public WixInfo WixInfo { get; } = new();
1414
public Settings Settings { get; } = new();
1515

16+
17+
1618
public bool Init;
1719
public bool Diag;
1820
public bool IsVersion;

src/GitVersion.MsBuild.Tests/Tasks/GenerateGitVersionInformationTest.cs

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,133 @@ public void GenerateGitVersionInformationTaskShouldCreateFileWhenRunWithMsBuildA
218218
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.0.1-1"));
219219
}
220220

221-
private static void AddGenerateGitVersionInformationTask(ProjectCreator project, string targetToRun, string taskName,
221+
[TestCaseSource(nameof(Languages))]
222+
public void GenerateGitVersionInformationTaskShouldCreateFileWhenRunWithMsBuildAndUseProjectNamespaceIsSpecifiedAndRootNamespaceIsSet(string language)
223+
{
224+
const string taskName = nameof(GenerateGitVersionInformation);
225+
const string outputProperty = nameof(GenerateGitVersionInformation.GitVersionInformationFilePath);
226+
var randDir = Guid.NewGuid().ToString("N");
227+
228+
var extension = FileHelper.GetFileExtension(language);
229+
using var result = ExecuteMsBuildExe(project =>
230+
{
231+
var intermediateOutputPath = Path.Combine("$(MSBuildProjectDirectory)", randDir);
232+
AddGenerateGitVersionInformationTask(project, taskName, taskName, outputProperty, language, intermediateOutputPath).Property("UseProjectNamespaceForGitVersionInformation", "True").Property("RootNamespace", "Test.Root");
233+
}, language);
234+
235+
result.ProjectPath.ShouldNotBeNullOrWhiteSpace();
236+
result.MsBuild.Count.ShouldBeGreaterThan(0);
237+
result.MsBuild.OverallSuccess.ShouldBe(true);
238+
result.MsBuild.ShouldAllBe(x => x.Succeeded);
239+
result.Output.ShouldNotBeNullOrWhiteSpace();
240+
241+
var generatedFilePath = PathHelper.Combine(Path.GetDirectoryName(result.ProjectPath), randDir, $"GitVersionInformation.g.{extension}");
242+
result.Output.ShouldContain($"{outputProperty}: {generatedFilePath}");
243+
244+
var fileContent = File.ReadAllText(generatedFilePath);
245+
TestContext.WriteLine(fileContent);
246+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
247+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "2"));
248+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "4"));
249+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.2.4"));
250+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.2.4-1"));
251+
fileContent.ShouldContain("namespace Test.Root", Case.Insensitive);
252+
253+
}
254+
255+
[TestCaseSource(nameof(Languages))]
256+
public void GenerateGitVersionInformationTaskShouldCreateFileWhenRunWithMsBuildAndUseProjectNamespaceIsSpecifiedAndRootNamespaceIsNotSet(string language)
257+
{
258+
const string taskName = nameof(GenerateGitVersionInformation);
259+
const string outputProperty = nameof(GenerateGitVersionInformation.GitVersionInformationFilePath);
260+
var randDir = Guid.NewGuid().ToString("N");
261+
262+
var extension = FileHelper.GetFileExtension(language);
263+
using var result = ExecuteMsBuildExeInAzurePipeline(project =>
264+
{
265+
var intermediateOutputPath = Path.Combine("$(MSBuildProjectDirectory)", randDir);
266+
AddGenerateGitVersionInformationTask(project, taskName, taskName, outputProperty, language, intermediateOutputPath).Property("UseProjectNamespaceForGitVersionInformation", "True");
267+
}, language);
268+
269+
result.ProjectPath.ShouldNotBeNullOrWhiteSpace();
270+
result.MsBuild.Count.ShouldBeGreaterThan(0);
271+
result.MsBuild.OverallSuccess.ShouldBe(true);
272+
result.MsBuild.ShouldAllBe(x => x.Succeeded);
273+
result.Output.ShouldNotBeNullOrWhiteSpace();
274+
275+
var generatedFilePath = PathHelper.Combine(Path.GetDirectoryName(result.ProjectPath), randDir, $"GitVersionInformation.g.{extension}");
276+
result.Output.ShouldContain($"{outputProperty}: {generatedFilePath}");
277+
278+
var fileContent = File.ReadAllText(generatedFilePath);
279+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
280+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "0"));
281+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "1"));
282+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.0.1"));
283+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.0.1-1"));
284+
fileContent.ShouldContain("namespace App", Case.Insensitive);
285+
}
286+
287+
[TestCaseSource(nameof(Languages))]
288+
public void GenerateGitVersionInformationTaskShouldCreateFileWithUseProjectNamespaceSetAndRootNamespaceUnSet(string language)
289+
{
290+
var extension = FileHelper.GetFileExtension(language);
291+
var task = new GenerateGitVersionInformation
292+
{
293+
Language = language,
294+
UseProjectNamespaceForGitVersionInformation = "true",
295+
ProjectFile = "App.Project.csproj",
296+
};
297+
using var result = ExecuteMsBuildTask(task);
298+
299+
result.Success.ShouldBe(true);
300+
result.Errors.ShouldBe(0);
301+
result.Task.GitVersionInformationFilePath.ShouldNotBeNull();
302+
result.Task.GitVersionInformationFilePath.ShouldMatch($@"GitVersionInformation.*\.g\.{extension}");
303+
304+
var fileContent = File.ReadAllText(result.Task.GitVersionInformationFilePath);
305+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
306+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "2"));
307+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "4"));
308+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.2.4"));
309+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.2.4-1"));
310+
fileContent.ShouldContain("namespace App.Project", Case.Insensitive);
311+
}
312+
313+
[TestCaseSource(nameof(Languages))]
314+
public void GenerateGitVersionInformationTaskShouldCreateFileWithUseProjectNamespaceSetAndRootNamespaceIsSet(string language)
315+
{
316+
317+
var extension = FileHelper.GetFileExtension(language);
318+
var task = new GenerateGitVersionInformation
319+
{
320+
Language = language,
321+
UseProjectNamespaceForGitVersionInformation = "true",
322+
ProjectFile = "App.Project.csproj",
323+
RootNamespace = "App.Project.RootNamespace",
324+
};
325+
using var result = ExecuteMsBuildTask(task);
326+
327+
result.Success.ShouldBe(true);
328+
result.Errors.ShouldBe(0);
329+
result.Task.GitVersionInformationFilePath.ShouldNotBeNull();
330+
result.Task.GitVersionInformationFilePath.ShouldMatch($@"GitVersionInformation.*\.g\.{extension}");
331+
332+
var fileContent = File.ReadAllText(result.Task.GitVersionInformationFilePath);
333+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
334+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "2"));
335+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "4"));
336+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.2.4"));
337+
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.2.4-1"));
338+
339+
fileContent.ShouldContain("namespace App.Project.RootNamespace");
340+
}
341+
342+
private static ProjectCreator AddGenerateGitVersionInformationTask(ProjectCreator project, string targetToRun, string taskName,
222343
string outputProperty, string language,
223344
string intermediateOutputPath = "$(MSBuildProjectDirectory)")
224345
{
225346
var assemblyFileLocation = typeof(GitVersionTaskBase).Assembly.Location;
226-
project.UsingTaskAssemblyFile(taskName, assemblyFileLocation)
347+
return project.UsingTaskAssemblyFile(taskName, assemblyFileLocation)
227348
.Property("ManagePackageVersionsCentrally", "false")
228349
.Property("GenerateAssemblyInfo", "false")
229350
.Property("Language", language)
@@ -235,6 +356,8 @@ private static void AddGenerateGitVersionInformationTask(ProjectCreator project,
235356
{ "ProjectFile", "$(MSBuildProjectFullPath)" },
236357
{ "Language", "$(Language)" },
237358
{ "IntermediateOutputPath", intermediateOutputPath },
359+
{ "UseProjectNamespaceForGitVersionInformation", "$(UseProjectNamespaceForGitVersionInformation)" },
360+
{ "RootNamespace", "$(RootNamespace)" },
238361
})
239362
.TaskOutputProperty(outputProperty, outputProperty)
240363
.ItemGroup()

src/GitVersion.MsBuild/GitVersionTaskExecutor.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,23 @@ public void GenerateGitVersionInformation(GenerateGitVersionInformation task)
7272

7373
var gitVersionOptions = this.options.Value;
7474
gitVersionOptions.WorkingDirectory = fileWriteInfo.WorkingDirectory;
75+
var targetNamespace = getTargetNamespace(task);
76+
gitVersionOutputTool.GenerateGitVersionInformation(versionVariables, fileWriteInfo, targetNamespace);
7577

76-
gitVersionOutputTool.GenerateGitVersionInformation(versionVariables, fileWriteInfo);
78+
static string? getTargetNamespace(GenerateGitVersionInformation task)
79+
{
80+
string? targetNamespace = null;
81+
if (string.Equals(task.UseProjectNamespaceForGitVersionInformation, "true", StringComparison.OrdinalIgnoreCase))
82+
{
83+
targetNamespace = task.RootNamespace;
84+
if (string.IsNullOrWhiteSpace(targetNamespace))
85+
{
86+
targetNamespace = Path.GetFileNameWithoutExtension(task.ProjectFile);
87+
}
88+
}
89+
90+
return targetNamespace;
91+
}
7792
}
7893

7994
public void WriteVersionInfoToBuildLog(WriteVersionInfoToBuildLog task)

src/GitVersion.MsBuild/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,7 @@ override GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.OnExecute() -> b
9898
override GitVersion.MsBuild.Tasks.GetVersion.OnExecute() -> bool
9999
override GitVersion.MsBuild.Tasks.UpdateAssemblyInfo.OnExecute() -> bool
100100
override GitVersion.MsBuild.Tasks.WriteVersionInfoToBuildLog.OnExecute() -> bool
101+
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.UseProjectNamespaceForGitVersionInformation.get -> string?
102+
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.UseProjectNamespaceForGitVersionInformation.set -> void
103+
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.RootNamespace.get -> string!
104+
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.RootNamespace.set -> void

src/GitVersion.MsBuild/Tasks/GenerateGitVersionInformation.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ public class GenerateGitVersionInformation : GitVersionTaskBase
1313
[Required]
1414
public string Language { get; set; } = "C#";
1515

16+
public string? UseProjectNamespaceForGitVersionInformation { get; set; }
17+
18+
public string RootNamespace { get; set; }
19+
1620
[Output]
1721
public string GitVersionInformationFilePath { get; set; }
1822

src/GitVersion.MsBuild/msbuild/tools/GitVersion.MsBuild.targets

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@
100100
<GenerateGitVersionInformation SolutionDirectory="$(GitVersionPath)" VersionFile="$(GitVersionOutputFile)"
101101
ProjectFile="$(MSBuildProjectFullPath)"
102102
IntermediateOutputPath="$(IntermediateOutputPath)"
103-
Language="$(Language)">
103+
Language="$(Language)"
104+
UseProjectNamespaceForGitVersionInformation="$(UseProjectNamespaceForGitVersionInformation)"
105+
RootNamespace="$(RootNamespace)">
104106

105107
<Output TaskParameter="GitVersionInformationFilePath" PropertyName="GitVersionInformationFilePath" />
106108
</GenerateGitVersionInformation>

src/GitVersion.Output/GitVersionInfo/GitVersionInfoContext.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ namespace GitVersion.Output.GitVersionInfo;
22

33
internal readonly struct GitVersionInfoContext : IConverterContext
44
{
5-
public GitVersionInfoContext(string workingDirectory, string fileName, string fileExtension)
5+
public GitVersionInfoContext(string workingDirectory, string fileName, string fileExtension) : this(workingDirectory, fileName, fileExtension, null)
6+
{
7+
}
8+
9+
public GitVersionInfoContext(string workingDirectory, string fileName, string fileExtension, string? targetNamespace = null)
610
{
711
WorkingDirectory = workingDirectory;
812
FileName = fileName;
913
FileExtension = fileExtension;
14+
TargetNamespace = targetNamespace;
1015
}
1116

1217
public string WorkingDirectory { get; }
1318
public string FileName { get; }
1419
public string FileExtension { get; }
20+
public string? TargetNamespace { get; }
1521
}

src/GitVersion.Output/GitVersionInfo/GitVersionInfoGenerator.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ internal interface IGitVersionInfoGenerator : IVersionConverter<GitVersionInfoCo
1010

1111
internal sealed class GitVersionInfoGenerator : IGitVersionInfoGenerator
1212
{
13+
private const string targetNamespaceSentinelValue = "<unset>";
1314
private readonly IFileSystem fileSystem;
1415
private readonly TemplateManager templateManager;
1516

@@ -27,6 +28,7 @@ public void Execute(GitVersionVariables variables, GitVersionInfoContext context
2728

2829
string? originalFileContents = null;
2930

31+
3032
if (File.Exists(filePath))
3133
{
3234
originalFileContents = this.fileSystem.ReadAllText(filePath);
@@ -35,21 +37,44 @@ public void Execute(GitVersionVariables variables, GitVersionInfoContext context
3537
var fileExtension = Path.GetExtension(filePath);
3638
var template = this.templateManager.GetTemplateFor(fileExtension);
3739
var addFormat = this.templateManager.GetAddFormatFor(fileExtension);
40+
var targetNamespace = getTargetNamespace(fileExtension);
3841

39-
if (string.IsNullOrWhiteSpace(template) || string.IsNullOrWhiteSpace(addFormat))
42+
if (string.IsNullOrWhiteSpace(template) || string.IsNullOrWhiteSpace(addFormat) || targetNamespace == targetNamespaceSentinelValue)
4043
return;
4144

45+
46+
4247
var indentation = GetIndentation(fileExtension);
48+
string? closeBracket = null;
49+
string? openBracket = null;
50+
string indent = "";
51+
52+
if (!string.IsNullOrWhiteSpace(targetNamespace) && fileExtension == ".cs")
53+
{
54+
indent = " ";
55+
closeBracket = System.Environment.NewLine + "}";
56+
openBracket = System.Environment.NewLine + "{";
57+
indentation += " ";
58+
}
4359

4460
var lines = variables.OrderBy(x => x.Key).Select(v => string.Format(indentation + addFormat, v.Key, v.Value));
4561
var members = string.Join(System.Environment.NewLine, lines);
4662

47-
var fileContents = string.Format(template, members);
63+
64+
var fileContents = string.Format(template, members, targetNamespace, openBracket, closeBracket, indent);
4865

4966
if (fileContents != originalFileContents)
5067
{
5168
this.fileSystem.WriteAllText(filePath, fileContents);
5269
}
70+
71+
string getTargetNamespace(string fileExtension) => fileExtension switch
72+
{
73+
".vb" => context.TargetNamespace ?? "Global",
74+
".cs" => context.TargetNamespace != null ? $"{System.Environment.NewLine}namespace {context.TargetNamespace};" : "",
75+
".fs" => context.TargetNamespace ?? "global",
76+
_ => targetNamespaceSentinelValue,
77+
};
5378
}
5479

5580
public void Dispose()

src/GitVersion.Output/GitVersionInfo/Templates/GitVersionInformation.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ namespace System.Diagnostics.CodeAnalysis
2323
internal sealed class ExcludeFromCodeCoverageAttribute : global::System.Attribute {{ }}
2424
}}
2525
#endif
26-
27-
[global::System.Runtime.CompilerServices.CompilerGenerated]
28-
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
29-
static class GitVersionInformation
30-
{{
26+
{1}{2}
27+
{4}[global::System.Runtime.CompilerServices.CompilerGenerated]
28+
{4}[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
29+
{4}static class GitVersionInformation
30+
{4}{{
3131
{0}
32-
}}
32+
{4}}}{3}

0 commit comments

Comments
 (0)