Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unique namespace generation for GitVersionInformation class #3619

Merged
12 changes: 12 additions & 0 deletions docs/input/docs/usage/msbuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,18 @@ For SDK-style projects, `UpdateVersionProperties` controls setting the default
variables: `Version`, `VersionPrefix`, `VersionSuffix`, `PackageVersion`,
`InformationalVersion`, `AssemblyVersion` and `FileVersion`.

### Namespace generation

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 `GenerateGitVersionInformationInUniqueNamespace` 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.
asbjornu marked this conversation as resolved.
Show resolved Hide resolved

```xml
<PropertyGroup>
...
<GenerateGitVersionInformationInUniqueNamespace>true</GenerateGitVersionInformationInUniqueNamespace>
asbjornu marked this conversation as resolved.
Show resolved Hide resolved
...
</PropertyGroup>
```

## Extra properties

There are properties that correspont to certain
Expand Down
2 changes: 2 additions & 0 deletions src/GitVersion.Core/Options/GitVersionOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class GitVersionOptions
public WixInfo WixInfo { get; } = new();
public Settings Settings { get; } = new();

public bool UniqueNamespace { get; set; }

public bool Init;
public bool Diag;
public bool IsVersion;
Expand Down
2 changes: 2 additions & 0 deletions src/GitVersion.Core/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -943,3 +943,5 @@ virtual GitVersion.Agents.BuildAgentBase.IsDefault.get -> bool
virtual GitVersion.Agents.BuildAgentBase.PreventFetch() -> bool
virtual GitVersion.Agents.BuildAgentBase.ShouldCleanUpRemotes() -> bool
virtual GitVersion.Agents.BuildAgentBase.WriteIntegration(System.Action<string?>! writer, GitVersion.OutputVariables.GitVersionVariables! variables, bool updateBuildNumber = true) -> void
GitVersion.GitVersionOptions.UniqueNamespace.get -> bool
GitVersion.GitVersionOptions.UniqueNamespace.set -> void
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,133 @@ public void GenerateGitVersionInformationTaskShouldCreateFileWhenRunWithMsBuildA
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.0.1-1"));
}

private static void AddGenerateGitVersionInformationTask(ProjectCreator project, string targetToRun, string taskName,
[TestCaseSource(nameof(Languages))]
public void GenerateGitVersionInformationTaskShouldCreateFileWhenRunWithMsBuildAndUniqueNamespaceIsSpecifiedAndRootNamespaceIsSet(string language)
{
const string taskName = nameof(GenerateGitVersionInformation);
const string outputProperty = nameof(GenerateGitVersionInformation.GitVersionInformationFilePath);
var randDir = Guid.NewGuid().ToString("N");

var extension = FileHelper.GetFileExtension(language);
using var result = ExecuteMsBuildExe(project =>
{
var intermediateOutputPath = Path.Combine("$(MSBuildProjectDirectory)", randDir);
AddGenerateGitVersionInformationTask(project, taskName, taskName, outputProperty, language, intermediateOutputPath).Property("GenerateGitVersionInformationInUniqueNamespace", "True").Property("RootNamespace", "Test.Root");
}, language);

result.ProjectPath.ShouldNotBeNullOrWhiteSpace();
result.MsBuild.Count.ShouldBeGreaterThan(0);
result.MsBuild.OverallSuccess.ShouldBe(true);
result.MsBuild.ShouldAllBe(x => x.Succeeded);
result.Output.ShouldNotBeNullOrWhiteSpace();

var generatedFilePath = PathHelper.Combine(Path.GetDirectoryName(result.ProjectPath), randDir, $"GitVersionInformation.g.{extension}");
result.Output.ShouldContain($"{outputProperty}: {generatedFilePath}");

var fileContent = File.ReadAllText(generatedFilePath);
TestContext.WriteLine(fileContent);
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "2"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "4"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.2.4"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.2.4-1"));
fileContent.ShouldContain("namespace Test.Root", Case.Insensitive);

}

[TestCaseSource(nameof(Languages))]
public void GenerateGitVersionInformationTaskShouldCreateFileWhenRunWithMsBuildAndUniqueNamespaceIsSpecifiedAndRootNamespaceIsNotSet(string language)
{
const string taskName = nameof(GenerateGitVersionInformation);
const string outputProperty = nameof(GenerateGitVersionInformation.GitVersionInformationFilePath);
var randDir = Guid.NewGuid().ToString("N");

var extension = FileHelper.GetFileExtension(language);
using var result = ExecuteMsBuildExeInAzurePipeline(project =>
{
var intermediateOutputPath = Path.Combine("$(MSBuildProjectDirectory)", randDir);
AddGenerateGitVersionInformationTask(project, taskName, taskName, outputProperty, language, intermediateOutputPath).Property("GenerateGitVersionInformationInUniqueNamespace", "True");
}, language);

result.ProjectPath.ShouldNotBeNullOrWhiteSpace();
result.MsBuild.Count.ShouldBeGreaterThan(0);
result.MsBuild.OverallSuccess.ShouldBe(true);
result.MsBuild.ShouldAllBe(x => x.Succeeded);
result.Output.ShouldNotBeNullOrWhiteSpace();

var generatedFilePath = PathHelper.Combine(Path.GetDirectoryName(result.ProjectPath), randDir, $"GitVersionInformation.g.{extension}");
result.Output.ShouldContain($"{outputProperty}: {generatedFilePath}");

var fileContent = File.ReadAllText(generatedFilePath);
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "0"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "1"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.0.1"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.0.1-1"));
fileContent.ShouldContain("namespace App", Case.Insensitive);
}

[TestCaseSource(nameof(Languages))]
public void GenerateGitVersionInformationTaskShouldCreateFileWithUniqueNamespaceSetAndRootNamespaceUnSet(string language)
{
var extension = FileHelper.GetFileExtension(language);
var task = new GenerateGitVersionInformation
{
Language = language,
GenerateGitVersionInformationInUniqueNamespace = "true",
ProjectFile = "App.Project.csproj",
};
using var result = ExecuteMsBuildTask(task);

result.Success.ShouldBe(true);
result.Errors.ShouldBe(0);
result.Task.GitVersionInformationFilePath.ShouldNotBeNull();
result.Task.GitVersionInformationFilePath.ShouldMatch($@"GitVersionInformation.*\.g\.{extension}");

var fileContent = File.ReadAllText(result.Task.GitVersionInformationFilePath);
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "2"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "4"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.2.4"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.2.4-1"));
fileContent.ShouldContain("namespace App.Project", Case.Insensitive);
}

[TestCaseSource(nameof(Languages))]
public void GenerateGitVersionInformationTaskShouldCreateFileWithUniqueNamespaceSetAndRootNamespaceIsSet(string language)
{

var extension = FileHelper.GetFileExtension(language);
var task = new GenerateGitVersionInformation
{
Language = language,
GenerateGitVersionInformationInUniqueNamespace = "true",
ProjectFile = "App.Project.csproj",
RootNamespace = "App.Project.RootNamespace",
};
using var result = ExecuteMsBuildTask(task);

result.Success.ShouldBe(true);
result.Errors.ShouldBe(0);
result.Task.GitVersionInformationFilePath.ShouldNotBeNull();
result.Task.GitVersionInformationFilePath.ShouldMatch($@"GitVersionInformation.*\.g\.{extension}");

var fileContent = File.ReadAllText(result.Task.GitVersionInformationFilePath);
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Major), "1"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Minor), "2"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.Patch), "4"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.MajorMinorPatch), "1.2.4"));
fileContent.ShouldMatch(string.Format(regexPattern, nameof(GitVersionVariables.FullSemVer), "1.2.4-1"));

fileContent.ShouldContain("namespace App.Project.RootNamespace");
}

private static ProjectCreator AddGenerateGitVersionInformationTask(ProjectCreator project, string targetToRun, string taskName,
string outputProperty, string language,
string intermediateOutputPath = "$(MSBuildProjectDirectory)")
{
var assemblyFileLocation = typeof(GitVersionTaskBase).Assembly.Location;
project.UsingTaskAssemblyFile(taskName, assemblyFileLocation)
return project.UsingTaskAssemblyFile(taskName, assemblyFileLocation)
.Property("ManagePackageVersionsCentrally", "false")
.Property("GenerateAssemblyInfo", "false")
.Property("Language", language)
Expand All @@ -235,6 +356,8 @@ private static void AddGenerateGitVersionInformationTask(ProjectCreator project,
{ "ProjectFile", "$(MSBuildProjectFullPath)" },
{ "Language", "$(Language)" },
{ "IntermediateOutputPath", intermediateOutputPath },
{ "GenerateGitVersionInformationInUniqueNamespace", "$(GenerateGitVersionInformationInUniqueNamespace)" },
{ "RootNamespace", "$(RootNamespace)" },
})
.TaskOutputProperty(outputProperty, outputProperty)
.ItemGroup()
Expand Down
17 changes: 16 additions & 1 deletion src/GitVersion.MsBuild/GitVersionTaskExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,23 @@ public void GenerateGitVersionInformation(GenerateGitVersionInformation task)

var gitVersionOptions = this.options.Value;
gitVersionOptions.WorkingDirectory = fileWriteInfo.WorkingDirectory;
var targetNamespace = getTargetNamespace(task);
gitVersionOutputTool.GenerateGitVersionInformation(versionVariables, fileWriteInfo, targetNamespace);

gitVersionOutputTool.GenerateGitVersionInformation(versionVariables, fileWriteInfo);
static string? getTargetNamespace(GenerateGitVersionInformation task)
{
string? targetNamespace = null;
if (string.Equals(task.GenerateGitVersionInformationInUniqueNamespace, "true", StringComparison.OrdinalIgnoreCase))
{
targetNamespace = task.RootNamespace;
if (string.IsNullOrWhiteSpace(targetNamespace))
{
targetNamespace = Path.GetFileNameWithoutExtension(task.ProjectFile);
}
}

return targetNamespace;
}
}

public void WriteVersionInfoToBuildLog(WriteVersionInfoToBuildLog task)
Expand Down
4 changes: 4 additions & 0 deletions src/GitVersion.MsBuild/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,7 @@ override GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.OnExecute() -> b
override GitVersion.MsBuild.Tasks.GetVersion.OnExecute() -> bool
override GitVersion.MsBuild.Tasks.UpdateAssemblyInfo.OnExecute() -> bool
override GitVersion.MsBuild.Tasks.WriteVersionInfoToBuildLog.OnExecute() -> bool
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.GenerateGitVersionInformationInUniqueNamespace.get -> string?
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.GenerateGitVersionInformationInUniqueNamespace.set -> void
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.RootNamespace.get -> string!
GitVersion.MsBuild.Tasks.GenerateGitVersionInformation.RootNamespace.set -> void
4 changes: 4 additions & 0 deletions src/GitVersion.MsBuild/Tasks/GenerateGitVersionInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ public class GenerateGitVersionInformation : GitVersionTaskBase
[Required]
public string Language { get; set; } = "C#";

public string? GenerateGitVersionInformationInUniqueNamespace { get; set; }

public string RootNamespace { get; set; }

[Output]
public string GitVersionInformationFilePath { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@
<GenerateGitVersionInformation SolutionDirectory="$(GitVersionPath)" VersionFile="$(GitVersionOutputFile)"
ProjectFile="$(MSBuildProjectFullPath)"
IntermediateOutputPath="$(IntermediateOutputPath)"
Language="$(Language)">
Language="$(Language)"
GenerateGitVersionInformationInUniqueNamespace="$(GenerateGitVersionInformationInUniqueNamespace)"
RootNamespace="$(RootNamespace)">

<Output TaskParameter="GitVersionInformationFilePath" PropertyName="GitVersionInformationFilePath" />
</GenerateGitVersionInformation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ namespace GitVersion.Output.GitVersionInfo;

internal readonly struct GitVersionInfoContext : IConverterContext
{
public GitVersionInfoContext(string workingDirectory, string fileName, string fileExtension)
public GitVersionInfoContext(string workingDirectory, string fileName, string fileExtension) : this(workingDirectory, fileName, fileExtension, null)
{
}

public GitVersionInfoContext(string workingDirectory, string fileName, string fileExtension, string? targetNamespace = null)
{
WorkingDirectory = workingDirectory;
FileName = fileName;
FileExtension = fileExtension;
TargetNamespace = targetNamespace;
}

public string WorkingDirectory { get; }
public string FileName { get; }
public string FileExtension { get; }
public string? TargetNamespace { get; }
}
29 changes: 27 additions & 2 deletions src/GitVersion.Output/GitVersionInfo/GitVersionInfoGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal interface IGitVersionInfoGenerator : IVersionConverter<GitVersionInfoCo

internal sealed class GitVersionInfoGenerator : IGitVersionInfoGenerator
{
private const string targetNamespaceSentinelValue = "<unset>";
private readonly IFileSystem fileSystem;
private readonly TemplateManager templateManager;

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

string? originalFileContents = null;


if (File.Exists(filePath))
{
originalFileContents = this.fileSystem.ReadAllText(filePath);
Expand All @@ -35,21 +37,44 @@ public void Execute(GitVersionVariables variables, GitVersionInfoContext context
var fileExtension = Path.GetExtension(filePath);
var template = this.templateManager.GetTemplateFor(fileExtension);
var addFormat = this.templateManager.GetAddFormatFor(fileExtension);
var targetNamespace = getTargetNamespace(fileExtension);

if (string.IsNullOrWhiteSpace(template) || string.IsNullOrWhiteSpace(addFormat))
if (string.IsNullOrWhiteSpace(template) || string.IsNullOrWhiteSpace(addFormat) || targetNamespace == targetNamespaceSentinelValue)
return;



var indentation = GetIndentation(fileExtension);
string? closeBracket = null;
string? openBracket = null;
string indent = "";

if (!string.IsNullOrWhiteSpace(targetNamespace) && fileExtension == ".cs")
{
indent = " ";
closeBracket = System.Environment.NewLine + "}";
openBracket = System.Environment.NewLine + "{";
indentation += " ";
}

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

var fileContents = string.Format(template, members);

var fileContents = string.Format(template, members, targetNamespace, openBracket, closeBracket, indent);

if (fileContents != originalFileContents)
{
this.fileSystem.WriteAllText(filePath, fileContents);
}

string getTargetNamespace(string fileExtension) => fileExtension switch
{
".vb" => context.TargetNamespace ?? "Global",
".cs" => context.TargetNamespace != null ? $"{System.Environment.NewLine}namespace {context.TargetNamespace};" : "",
".fs" => context.TargetNamespace ?? "global",
_ => targetNamespaceSentinelValue,
};
}

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ namespace System.Diagnostics.CodeAnalysis
internal sealed class ExcludeFromCodeCoverageAttribute : global::System.Attribute {{ }}
}}
#endif

[global::System.Runtime.CompilerServices.CompilerGenerated]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
static class GitVersionInformation
{{
{1}{2}
{4}[global::System.Runtime.CompilerServices.CompilerGenerated]
{4}[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
{4}static class GitVersionInformation
{4}{{
{0}
}}
{4}}}{3}
Comment on lines +26 to +32
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part here is the most clunky, I would much prefer a cleaner way of handling the indent/non-indent and brackets needed for the csharp namespace.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is not very elegant. Pull requests to improve it are welcome.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace System.Diagnostics.CodeAnalysis
type ExcludeFromCodeCoverageAttribute() = inherit global.System.Attribute()
#endif

namespace global
namespace {1}

[<AbstractClass; Sealed>]
[<global.System.Runtime.CompilerServices.CompilerGenerated>]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Namespace Global.System.Diagnostics.CodeAnalysis
End Namespace
#End If

Namespace Global
Namespace {1}

<Global.System.Runtime.CompilerServices.CompilerGenerated()>
<Global.System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage()>
Expand Down
6 changes: 4 additions & 2 deletions src/GitVersion.Output/GitVersionOutputTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ public void UpdateWixVersionFile(GitVersionVariables variables)
}
}

public void GenerateGitVersionInformation(GitVersionVariables variables, FileWriteInfo fileWriteInfo)
public void GenerateGitVersionInformation(GitVersionVariables variables, FileWriteInfo fileWriteInfo, string? targetNamespace = null)
{
using (this.gitVersionInfoGenerator)
{
this.gitVersionInfoGenerator.Execute(variables, new GitVersionInfoContext(gitVersionOptions.WorkingDirectory, fileWriteInfo.FileName, fileWriteInfo.FileExtension));
this.gitVersionInfoGenerator.Execute(variables, new GitVersionInfoContext(gitVersionOptions.WorkingDirectory, fileWriteInfo.FileName, fileWriteInfo.FileExtension, targetNamespace));
}
}

public void GenerateGitVersionInformation(GitVersionVariables variables, FileWriteInfo fileWriteInfo) => GenerateGitVersionInformation(variables, fileWriteInfo, null);
}
1 change: 1 addition & 0 deletions src/GitVersion.Output/IGitVersionOutputTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public interface IGitVersionOutputTool
void UpdateAssemblyInfo(GitVersionVariables variables);
void UpdateWixVersionFile(GitVersionVariables variables);
void GenerateGitVersionInformation(GitVersionVariables variables, FileWriteInfo fileWriteInfo);
void GenerateGitVersionInformation(GitVersionVariables variables, FileWriteInfo fileWriteInfo, string? targetNamespace = null);
}
Loading
Loading