diff --git a/docs/usage/command-line.md b/docs/usage/command-line.md index 8a0f982142..01e7636731 100644 --- a/docs/usage/command-line.md +++ b/docs/usage/command-line.md @@ -24,4 +24,34 @@ It will update the following assembly attributes: Note that contrary to when using the [MSBuild Task](msbuild-task.md) the attributes must already exist in the `AssemblyInfo.cs` or `AssemblyInfo.vb` files prior to calling GitVersion. -By adding `/updateassemblyinfofilename` the name of AssemblyInfo file to update can be set. \ No newline at end of file +By adding `/updateassemblyinfo ` the name of AssemblyInfo file to update can be set. This switch can accept multiple files with the path to the file specified relative to the working directory. + +GitVersion can generate an assembly info source file for you if it does not already exist. Use the `/ensureassemblyinfo` switch alongside `/updateassemblyinfo `, if the filename specified does not exist it will be generated based on a known template that adds: + +* `AssemblyVersion` will be set to the `AssemblySemVer` variable. +* `AssemblyFileVersion` will be set to the `MajorMinorPatch` variable with a appended `.0`. +* `AssemblyInformationalVersion` will be set to the `InformationalVersion` variable. + +This can be done for *.cs, *.vb and *.fs files. + +When requesting that GitVersion generate an assembly info file you are limited to only specifying a single `` within the `/updateassemblyinfo` switch, this is to prevent the creation of mulitple assembly info files with the same assembly version attributes. If this occurs your build will fail. + +### Example: When AssemblyInfo.cs does not exist +`GitVersion.exe /updateassemblyinfo AssemblyInfo.cs /ensureassemblyinfo` + +A file is generated that contains version attributes (`AssemblyVersion`, `AssemblyFileVersion`, `AssemblyInformationalVersion`) + +### Example: When AssemblyInfo.cs already exists +`GitVersion.exe /updateassemblyinfo AssemblyInfo.cs /ensureassemblyinfo` + +All known attributes (`AssemblyVersion`, `AssemblyFileVersion`, `AssemblyInformationalVersion`) will be updated + +### Example: When AssemblyInfo.cs and AssemblyVersionInfo.cs do not exist +`GitVersion.exe /updateassemblyinfo AssemblyInfo.cs AssemblyVersionInfo.cs /ensureassemblyinfo` + +Will result in command line argument error + +### Example: When AssemblyInfo.cs and AssemblyVersionInfo.cs already exist +`GitVersion.exe /updateassemblyinfo AssemblyInfo.cs AssemblyVersionInfo.cs` + +Will iterate through each file and update known attributes (`AssemblyVersion`, `AssemblyFileVersion`, `AssemblyInformationalVersion`). \ No newline at end of file diff --git a/src/GitVersionCore.Tests/TestFileSystem.cs b/src/GitVersionCore.Tests/TestFileSystem.cs index 2e2a83e24a..e12bee8b78 100644 --- a/src/GitVersionCore.Tests/TestFileSystem.cs +++ b/src/GitVersionCore.Tests/TestFileSystem.cs @@ -69,6 +69,19 @@ public Stream OpenRead(string path) public void CreateDirectory(string path) { + if (fileSystem.ContainsKey(path)) + { + fileSystem[path] = ""; + } + else + { + fileSystem.Add(path, ""); + } + } + + public bool DirectoryExists(string path) + { + return fileSystem.ContainsKey(path); } public long GetLastDirectoryWrite(string path) diff --git a/src/GitVersionCore/Extensions/ReadEmbeddedResourceExtensions.cs b/src/GitVersionCore/Extensions/ReadEmbeddedResourceExtensions.cs new file mode 100644 index 0000000000..a57e7ef720 --- /dev/null +++ b/src/GitVersionCore/Extensions/ReadEmbeddedResourceExtensions.cs @@ -0,0 +1,30 @@ +namespace GitVersionCore.Extensions +{ + using System.IO; + + public static class ReadEmbeddedResourceExtensions + { + /// + /// + /// + /// + /// Should include Namespace separated path to resource in assembly referenced by + /// + public static string ReadAsStringFromEmbeddedResource(this string resourceName) + { + using (var stream = resourceName.ReadFromEmbeddedResource()) + { + using (var rdr = new StreamReader(stream)) + { + return rdr.ReadToEnd(); + } + } + } + + public static Stream ReadFromEmbeddedResource(this string resourceName) + { + var assembly = typeof(T).Assembly; + return assembly.GetManifestResourceStream(resourceName); + } + } +} diff --git a/src/GitVersionCore/GitVersionCore.csproj b/src/GitVersionCore/GitVersionCore.csproj index e554cc5eab..ab496765c1 100644 --- a/src/GitVersionCore/GitVersionCore.csproj +++ b/src/GitVersionCore/GitVersionCore.csproj @@ -109,6 +109,7 @@ + @@ -121,6 +122,8 @@ + + @@ -161,6 +164,8 @@ Designer + + diff --git a/src/GitVersionCore/Helpers/FileSystem.cs b/src/GitVersionCore/Helpers/FileSystem.cs index 5348177b7b..93b9cbbba6 100644 --- a/src/GitVersionCore/Helpers/FileSystem.cs +++ b/src/GitVersionCore/Helpers/FileSystem.cs @@ -56,6 +56,11 @@ public void CreateDirectory(string path) Directory.CreateDirectory(path); } + public bool DirectoryExists(string path) + { + return Directory.Exists(path); + } + public long GetLastDirectoryWrite(string path) { return new DirectoryInfo(path) diff --git a/src/GitVersionCore/Helpers/IFileSystem.cs b/src/GitVersionCore/Helpers/IFileSystem.cs index dcc8c66ccb..ea4e7863b4 100644 --- a/src/GitVersionCore/Helpers/IFileSystem.cs +++ b/src/GitVersionCore/Helpers/IFileSystem.cs @@ -15,6 +15,7 @@ public interface IFileSystem Stream OpenWrite(string path); Stream OpenRead(string path); void CreateDirectory(string path); + bool DirectoryExists(string path); long GetLastDirectoryWrite(string path); } } \ No newline at end of file diff --git a/src/GitVersionCore/VersionAssemblyInfoResources/AssemblyVersionInfoTemplates.cs b/src/GitVersionCore/VersionAssemblyInfoResources/AssemblyVersionInfoTemplates.cs new file mode 100644 index 0000000000..3e99397e35 --- /dev/null +++ b/src/GitVersionCore/VersionAssemblyInfoResources/AssemblyVersionInfoTemplates.cs @@ -0,0 +1,38 @@ +namespace GitVersion.VersionAssemblyInfoResources +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using GitVersionCore.Extensions; + + public class AssemblyVersionInfoTemplates + { + static IDictionary assemblyInfoSourceList; + + static AssemblyVersionInfoTemplates() + { + var enclosingNamespace = typeof(AssemblyVersionInfoTemplates).Namespace; + + var files = typeof(AssemblyVersionInfoTemplates) + .Assembly + .GetManifestResourceNames() + .Where(n => n.StartsWith(enclosingNamespace ?? string.Empty)).Select(f => new FileInfo(f)); + + assemblyInfoSourceList = files.ToDictionary(k => k.Extension, v => v); + } + + public static string GetAssemblyInfoTemplateFor(string assemblyInfoFile) + { + var fi = new FileInfo(assemblyInfoFile); + if (assemblyInfoSourceList.ContainsKey(fi.Extension)) + { + var template = assemblyInfoSourceList[fi.Extension]; + if (template != null) + { + return template.Name.ReadAsStringFromEmbeddedResource(); + } + } + return null; + } + } +} diff --git a/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.cs b/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.cs new file mode 100644 index 0000000000..c46e49bf37 --- /dev/null +++ b/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.cs @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by GitVersion. +// +// You can modify this code as we will not overwrite it when re-executing GitVersion +// +//------------------------------------------------------------------------------ + +using System.Reflection; + +[assembly: AssemblyFileVersion("0.0.0.0")] +[assembly: AssemblyVersion("0.0.0.0")] +[assembly: AssemblyInformationalVersion("0.0.0.0")] \ No newline at end of file diff --git a/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.fs b/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.fs new file mode 100644 index 0000000000..e29db48538 --- /dev/null +++ b/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.fs @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by GitVersion. +// +// You can modify this code as we will not overwrite it when re-executing GitVersion +// +//------------------------------------------------------------------------------ + +open System.Reflection + +[] +[] +[] +() \ No newline at end of file diff --git a/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.vb b/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.vb new file mode 100644 index 0000000000..7449bea5c3 --- /dev/null +++ b/src/GitVersionCore/VersionAssemblyInfoResources/VersionAssemblyInfo.vb @@ -0,0 +1,13 @@ +''------------------------------------------------------------------------------ +'' +'' This code was generated by GitVersion. +'' +'' You can modify this code as we will not overwrite it when re-executing GitVersion +'' +''------------------------------------------------------------------------------ + +Imports System.Reflection + + + + \ No newline at end of file diff --git a/src/GitVersionExe.Tests/ArgumentParserTests.cs b/src/GitVersionExe.Tests/ArgumentParserTests.cs index 0169bd2f59..9c4092b333 100644 --- a/src/GitVersionExe.Tests/ArgumentParserTests.cs +++ b/src/GitVersionExe.Tests/ArgumentParserTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using GitVersion; using NUnit.Framework; using Shouldly; @@ -175,6 +176,10 @@ public void Unknown_argument_should_throw() [TestCase("-updateAssemblyInfo 1")] [TestCase("-updateAssemblyInfo")] [TestCase("-updateAssemblyInfo -proj foo.sln")] + [TestCase("-updateAssemblyInfo assemblyInfo.cs")] + [TestCase("-updateAssemblyInfo assemblyInfo.cs -ensureassemblyinfo")] + [TestCase("-updateAssemblyInfo assemblyInfo.cs otherAssemblyInfo.cs")] + [TestCase("-updateAssemblyInfo Assembly.cs Assembly.cs -ensureassemblyinfo")] public void update_assembly_info_true(string command) { var arguments = ArgumentParser.ParseArguments(command); @@ -189,12 +194,29 @@ public void update_assembly_info_false(string command) arguments.UpdateAssemblyInfo.ShouldBe(false); } + [TestCase("-updateAssemblyInfo Assembly.cs Assembly1.cs -ensureassemblyinfo")] + public void create_mulitple_assembly_info_protected(string command) + { + var exception = Assert.Throws(() => ArgumentParser.ParseArguments(command)); + exception.Message.ShouldBe("Can't specify multiple assembly info files when using -ensureassemblyinfo switch, either use a single assembly info file or do not specify -ensureassemblyinfo and create assembly info files manually"); + } + [Test] public void update_assembly_info_with_filename() { var arguments = ArgumentParser.ParseArguments("-updateAssemblyInfo CommonAssemblyInfo.cs"); arguments.UpdateAssemblyInfo.ShouldBe(true); - arguments.UpdateAssemblyInfoFileName.ShouldBe("CommonAssemblyInfo.cs"); + arguments.UpdateAssemblyInfoFileName.ShouldContain("CommonAssemblyInfo.cs"); + } + + [Test] + public void update_assembly_info_with_multiple_filenames() + { + var arguments = ArgumentParser.ParseArguments("-updateAssemblyInfo CommonAssemblyInfo.cs VersionAssemblyInfo.cs"); + arguments.UpdateAssemblyInfo.ShouldBe(true); + arguments.UpdateAssemblyInfoFileName.Count.ShouldBe(2); + arguments.UpdateAssemblyInfoFileName.ShouldContain("CommonAssemblyInfo.cs"); + arguments.UpdateAssemblyInfoFileName.ShouldContain("VersionAssemblyInfo.cs"); } [Test] @@ -202,9 +224,30 @@ public void update_assembly_info_with_relative_filename() { var arguments = ArgumentParser.ParseArguments("-updateAssemblyInfo ..\\..\\CommonAssemblyInfo.cs"); arguments.UpdateAssemblyInfo.ShouldBe(true); - arguments.UpdateAssemblyInfoFileName.ShouldBe("..\\..\\CommonAssemblyInfo.cs"); + arguments.UpdateAssemblyInfoFileName.ShouldContain("..\\..\\CommonAssemblyInfo.cs"); } + [Test] + public void ensure_assembly_info_true_when_found() + { + var arguments = ArgumentParser.ParseArguments("-ensureAssemblyInfo"); + arguments.EnsureAssemblyInfo.ShouldBe(true); + } + + [Test] + public void ensure_assembly_info_true() + { + var arguments = ArgumentParser.ParseArguments("-ensureAssemblyInfo true"); + arguments.EnsureAssemblyInfo.ShouldBe(true); + } + + [Test] + public void ensure_assembly_info_false() + { + var arguments = ArgumentParser.ParseArguments("-ensureAssemblyInfo false"); + arguments.EnsureAssemblyInfo.ShouldBe(false); + } + [Test] public void dynamicRepoLocation() { diff --git a/src/GitVersionExe.Tests/AssemblyInfoFileUpdateTests.cs b/src/GitVersionExe.Tests/AssemblyInfoFileUpdateTests.cs index 9652e93003..6ab351184a 100644 --- a/src/GitVersionExe.Tests/AssemblyInfoFileUpdateTests.cs +++ b/src/GitVersionExe.Tests/AssemblyInfoFileUpdateTests.cs @@ -1,14 +1,171 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Linq; using GitVersion; using GitVersion.Helpers; using GitVersionCore.Tests; +using GitVersion.VersionAssemblyInfoResources; using NSubstitute; using NUnit.Framework; [TestFixture] public class AssemblyInfoFileUpdateTests { + [SetUp] + public void SetLoggers() + { + Logger.SetLoggers(m => Debug.WriteLine(m), m => Debug.WriteLine(m), m => Debug.WriteLine(m)); + } + + [Test] + public void ShouldCreateCSharpAssemblyInfoFileWhenNotExistsAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet() { "VersionAssemblyInfo.cs" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { EnsureAssemblyInfo = true, UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile}, workingDir, variables, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received(1).WriteAllText(fullPath, source); + } + } + + [Test] + public void ShouldCreateCSharpAssemblyInfoFileAtPathWhenNotExistsAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { @"src\Project\Properties\VersionAssemblyInfo.cs" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { EnsureAssemblyInfo = true, UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received(1).WriteAllText(fullPath, source); + } + } + + [Test] + public void ShouldCreateCSharpAssemblyInfoFilesAtPathWhenNotExistsAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { "AssemblyInfo.cs", @"src\Project\Properties\VersionAssemblyInfo.cs" }; + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { EnsureAssemblyInfo = true, UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + foreach (var item in assemblyInfoFile) + { + var fullPath = Path.Combine(workingDir, item); + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received(1).WriteAllText(fullPath, source); + } + } + } + + [Test] + public void ShouldNotCreateCSharpAssemblyInfoFileWhenNotExistsAndNotEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { "VersionAssemblyInfo.cs" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received(0).WriteAllText(fullPath, source); + } + } + + [Test] + public void ShouldNotCreateFSharpAssemblyInfoFileWhenNotExistsAndNotEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { "VersionAssemblyInfo.fs" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received(0).WriteAllText(fullPath, source); + } + } + + [Test] + public void ShouldNotCreateVisualBasicAssemblyInfoFileWhenNotExistsAndNotEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { "VersionAssemblyInfo.vb" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received(0).WriteAllText(fullPath, source); + } + } + + [Test] + public void ShouldCreateVisualBasicAssemblyInfoFileWhenNotExistsAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { "VersionAssemblyInfo.vb" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { EnsureAssemblyInfo = true, UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received().WriteAllText(fullPath, source); + } + } + + [Test] + public void ShouldCreateFSharpAssemblyInfoFileWhenNotExistsAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { "VersionAssemblyInfo.fs" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { EnsureAssemblyInfo = true, UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + fileSystem.Received().WriteAllText(fullPath, source); + } + } + + [Test] + public void ShouldNotCreateAssemblyInfoFileForUnknownSourceCodeAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + const string workingDir = "C:\\Testing"; + ISet assemblyInfoFile = new HashSet { "VersionAssemblyInfo.js" }; + var fullPath = Path.Combine(workingDir, assemblyInfoFile.First()); + + var variables = VariableProvider.GetVariablesFor(SemanticVersion.Parse("1.0.0", "v"), new TestEffectiveConfiguration(), false); + using (new AssemblyInfoFileUpdate(new Arguments { EnsureAssemblyInfo = true, UpdateAssemblyInfo = true, UpdateAssemblyInfoFileName = assemblyInfoFile }, workingDir, variables, fileSystem)) + { + fileSystem.Received(0).WriteAllText(fullPath, Arg.Any()); + } + } + [Test] public void ShouldStartSearchFromWorkingDirectory() { @@ -49,7 +206,7 @@ public void ShouldReplaceAssemblyVersion() var args = new Arguments { UpdateAssemblyInfo = true, - UpdateAssemblyInfoFileName = "AssemblyInfo.cs" + UpdateAssemblyInfoFileName = new HashSet { "AssemblyInfo.cs" } }; using (new AssemblyInfoFileUpdate(args, workingDir, variable, fileSystem)) { @@ -60,6 +217,43 @@ public void ShouldReplaceAssemblyVersion() } } + [Test] + public void ShouldReplaceAssemblyVersionInRelativePath() + { + var fileSystem = Substitute.For(); + var version = new SemanticVersion + { + BuildMetaData = new SemanticVersionBuildMetaData(3, "foo", "hash", DateTimeOffset.Now), + Major = 2, + Minor = 3, + Patch = 1 + }; + + const string workingDir = "C:\\Testing"; + const string assemblyInfoFile = @"AssemblyVersion(""1.0.0.0""); +AssemblyInformationalVersion(""1.0.0.0""); +AssemblyFileVersion(""1.0.0.0"");"; + + fileSystem.Exists("C:\\Testing\\Project\\src\\Properties\\AssemblyInfo.cs").Returns(true); + fileSystem.ReadAllText("C:\\Testing\\Project\\src\\Properties\\AssemblyInfo.cs").Returns(assemblyInfoFile); + + var config = new TestEffectiveConfiguration(assemblyVersioningScheme: AssemblyVersioningScheme.MajorMinor); + + var variable = VariableProvider.GetVariablesFor(version, config, false); + var args = new Arguments + { + UpdateAssemblyInfo = true, + UpdateAssemblyInfoFileName = new HashSet { @"Project\src\Properties\AssemblyInfo.cs" } + }; + using (new AssemblyInfoFileUpdate(args, workingDir, variable, fileSystem)) + { + const string expected = @"AssemblyVersion(""2.3.0.0""); +AssemblyInformationalVersion(""2.3.1+3.Branch.foo.Sha.hash""); +AssemblyFileVersion(""2.3.1.0"");"; + fileSystem.Received().WriteAllText("C:\\Testing\\Project\\src\\Properties\\AssemblyInfo.cs", expected); + } + } + [Test] public void ShouldReplaceAssemblyVersionWithStar() { @@ -85,7 +279,7 @@ public void ShouldReplaceAssemblyVersionWithStar() var args = new Arguments { UpdateAssemblyInfo = true, - UpdateAssemblyInfoFileName = "AssemblyInfo.cs" + UpdateAssemblyInfoFileName = new HashSet { "AssemblyInfo.cs" } }; using (new AssemblyInfoFileUpdate(args, workingDir, variable, fileSystem)) { @@ -120,7 +314,7 @@ [Test] public void ShouldReplaceAlreadySubstitutedValues() var args = new Arguments { UpdateAssemblyInfo = true, - UpdateAssemblyInfoFileName = "AssemblyInfo.cs" + UpdateAssemblyInfoFileName = new HashSet { "AssemblyInfo.cs" } }; using (new AssemblyInfoFileUpdate(args, workingDir, variable, fileSystem)) { @@ -130,4 +324,119 @@ [Test] public void ShouldReplaceAlreadySubstitutedValues() fileSystem.Received().WriteAllText("C:\\Testing\\AssemblyInfo.cs", expected); } } -} \ No newline at end of file + + + [Test] + public void ShouldReplaceAssemblyVersionWhenCreatingCSharpAssemblyVersionFileAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + var version = new SemanticVersion + { + BuildMetaData = new SemanticVersionBuildMetaData(3, "foo", "hash", DateTimeOffset.Now), + Major = 2, + Minor = 3, + Patch = 1 + }; + + const string workingDir = "C:\\Testing"; + const string assemblyInfoFile = @"AssemblyVersion(""1.0.0.0""); +AssemblyInformationalVersion(""1.0.0.0""); +AssemblyFileVersion(""1.0.0.0"");"; + + fileSystem.Exists("C:\\Testing\\AssemblyInfo.cs").Returns(false); + fileSystem.ReadAllText("C:\\Testing\\AssemblyInfo.cs").Returns(assemblyInfoFile); + var variable = VariableProvider.GetVariablesFor(version, new TestEffectiveConfiguration(), false); + var args = new Arguments + { + EnsureAssemblyInfo = true, + UpdateAssemblyInfo = true, + UpdateAssemblyInfoFileName = new HashSet { "AssemblyInfo.cs" } + }; + using (new AssemblyInfoFileUpdate(args, workingDir, variable, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(args.UpdateAssemblyInfoFileName.First()); + + const string expected = @"AssemblyVersion(""2.3.1.0""); +AssemblyInformationalVersion(""2.3.1+3.Branch.foo.Sha.hash""); +AssemblyFileVersion(""2.3.1.0"");"; + fileSystem.Received(1).WriteAllText("C:\\Testing\\AssemblyInfo.cs", source); + fileSystem.Received(1).WriteAllText("C:\\Testing\\AssemblyInfo.cs", expected); + } + } + + [Test] + public void ShouldReplaceAssemblyVersionWhenCreatingFSharpAssemblyVersionFileAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + var version = new SemanticVersion + { + BuildMetaData = new SemanticVersionBuildMetaData(3, "foo", "hash", DateTimeOffset.Now), + Major = 2, + Minor = 3, + Patch = 1 + }; + + const string workingDir = "C:\\Testing"; + const string assemblyInfoFile = @"AssemblyVersion(""1.0.0.0""); +AssemblyInformationalVersion(""1.0.0.0""); +AssemblyFileVersion(""1.0.0.0"");"; + + fileSystem.Exists("C:\\Testing\\AssemblyInfo.fs").Returns(false); + fileSystem.ReadAllText("C:\\Testing\\AssemblyInfo.fs").Returns(assemblyInfoFile); + var variable = VariableProvider.GetVariablesFor(version, new TestEffectiveConfiguration(), false); + var args = new Arguments + { + EnsureAssemblyInfo = true, + UpdateAssemblyInfo = true, + UpdateAssemblyInfoFileName = new HashSet { "AssemblyInfo.fs" } + }; + using (new AssemblyInfoFileUpdate(args, workingDir, variable, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(args.UpdateAssemblyInfoFileName.First()); + + const string expected = @"AssemblyVersion(""2.3.1.0""); +AssemblyInformationalVersion(""2.3.1+3.Branch.foo.Sha.hash""); +AssemblyFileVersion(""2.3.1.0"");"; + fileSystem.Received(1).WriteAllText("C:\\Testing\\AssemblyInfo.fs", source); + fileSystem.Received(1).WriteAllText("C:\\Testing\\AssemblyInfo.fs", expected); + } + } + + [Test] + public void ShouldReplaceAssemblyVersionWhenCreatingVisualBasicAssemblyVersionFileAndEnsureAssemblyInfo() + { + var fileSystem = Substitute.For(); + var version = new SemanticVersion + { + BuildMetaData = new SemanticVersionBuildMetaData(3, "foo", "hash", DateTimeOffset.Now), + Major = 2, + Minor = 3, + Patch = 1 + }; + + const string workingDir = "C:\\Testing"; + const string assemblyInfoFile = @"AssemblyVersion(""1.0.0.0""); +AssemblyInformationalVersion(""1.0.0.0""); +AssemblyFileVersion(""1.0.0.0"");"; + + fileSystem.Exists("C:\\Testing\\AssemblyInfo.fs").Returns(false); + fileSystem.ReadAllText("C:\\Testing\\AssemblyInfo.vb").Returns(assemblyInfoFile); + var variable = VariableProvider.GetVariablesFor(version, new TestEffectiveConfiguration(), false); + var args = new Arguments + { + EnsureAssemblyInfo = true, + UpdateAssemblyInfo = true, + UpdateAssemblyInfoFileName = new HashSet { "AssemblyInfo.vb" } + }; + using (new AssemblyInfoFileUpdate(args, workingDir, variable, fileSystem)) + { + var source = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(args.UpdateAssemblyInfoFileName.First()); + + const string expected = @"AssemblyVersion(""2.3.1.0""); +AssemblyInformationalVersion(""2.3.1+3.Branch.foo.Sha.hash""); +AssemblyFileVersion(""2.3.1.0"");"; + fileSystem.Received(1).WriteAllText("C:\\Testing\\AssemblyInfo.vb", source); + fileSystem.Received(1).WriteAllText("C:\\Testing\\AssemblyInfo.vb", expected); + } + } +} diff --git a/src/GitVersionExe/ArgumentParser.cs b/src/GitVersionExe/ArgumentParser.cs index 7299b711be..d572eafbb3 100644 --- a/src/GitVersionExe/ArgumentParser.cs +++ b/src/GitVersionExe/ArgumentParser.cs @@ -13,6 +13,11 @@ public static Arguments ParseArguments(string commandLineArguments) return ParseArguments(commandLineArguments.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList()); } + static void EnsureArgumentValueCount(string[] values, int maxArguments = 1) + { + if(values != null && values.Length > maxArguments) throw new WarningException(string.Format("Could not parse command line parameter '{0}'.", values[1])); + } + public static Arguments ParseArguments(List commandLineArguments) { if (commandLineArguments.Count == 0) @@ -73,79 +78,91 @@ public static Arguments ParseArguments(List commandLineArguments) { //Currently, no arguments use more than one value, so having multiple values is an input error. //In the future, this exception can be removed to support multiple values for a switch. - if (values.Length > 1) throw new WarningException(string.Format("Could not parse command line parameter '{0}'.", values[1])); + // if (values.Length > 1) throw new WarningException(string.Format("Could not parse command line parameter '{0}'.", values[1])); value = values.FirstOrDefault(); } if (IsSwitch("l", name)) { + EnsureArgumentValueCount(values); arguments.LogFilePath = value; continue; } if (IsSwitch("targetpath", name)) { + EnsureArgumentValueCount(values); arguments.TargetPath = value; continue; } if (IsSwitch("dynamicRepoLocation", name)) { + EnsureArgumentValueCount(values); arguments.DynamicRepositoryLocation = value; continue; } if (IsSwitch("url", name)) { + EnsureArgumentValueCount(values); arguments.TargetUrl = value; continue; } if (IsSwitch("b", name)) { + EnsureArgumentValueCount(values); arguments.TargetBranch = value; continue; } if (IsSwitch("u", name)) { + EnsureArgumentValueCount(values); arguments.Authentication.Username = value; continue; } if (IsSwitch("p", name)) { + EnsureArgumentValueCount(values); arguments.Authentication.Password = value; continue; } if (IsSwitch("c", name)) { + EnsureArgumentValueCount(values); arguments.CommitId = value; continue; } if (IsSwitch("exec", name)) { + EnsureArgumentValueCount(values); arguments.Exec = value; continue; } if (IsSwitch("execargs", name)) { + EnsureArgumentValueCount(values); arguments.ExecArgs = value; continue; } if (IsSwitch("proj", name)) { + EnsureArgumentValueCount(values); arguments.Proj = value; continue; } if (IsSwitch("projargs", name)) { + EnsureArgumentValueCount(values); arguments.ProjArgs = value; continue; } @@ -160,15 +177,26 @@ public static Arguments ParseArguments(List commandLineArguments) { arguments.UpdateAssemblyInfo = false; } + else if (values != null && values.Length > 1) + { + arguments.UpdateAssemblyInfo = true; + foreach(var v in values) arguments.AddAssemblyInfoFileName(v); + } else if (!IsSwitchArgument(value)) { arguments.UpdateAssemblyInfo = true; - arguments.UpdateAssemblyInfoFileName = value; + arguments.AddAssemblyInfoFileName(value); } else { arguments.UpdateAssemblyInfo = true; } + + if (arguments.UpdateAssemblyInfoFileName.Count > 1 && arguments.EnsureAssemblyInfo) + { + throw new WarningException("Can't specify multiple assembly info files when using -ensureassemblyinfo switch, either use a single assembly info file or do not specify -ensureassemblyinfo and create assembly info files manually"); + } + continue; } @@ -232,6 +260,28 @@ public static Arguments ParseArguments(List commandLineArguments) continue; } + if (IsSwitch("ensureassemblyinfo", name)) + { + if (new[] { "1", "true" }.Contains(value, StringComparer.OrdinalIgnoreCase)) + { + arguments.EnsureAssemblyInfo = true; + } + else if (new[] { "0", "false" }.Contains(value, StringComparer.OrdinalIgnoreCase)) + { + arguments.EnsureAssemblyInfo = false; + } + else + { + arguments.EnsureAssemblyInfo = true; + } + + if (arguments.UpdateAssemblyInfoFileName.Count > 1 && arguments.EnsureAssemblyInfo) + { + throw new WarningException("Can't specify multiple assembly info files when using -ensureassemblyinfo switch, either use a single assembly info file or do not specify -ensureassemblyinfo and create assembly info files manually"); + } + continue; + } + throw new WarningException(string.Format("Could not parse command line parameter '{0}'.", name)); } diff --git a/src/GitVersionExe/Arguments.cs b/src/GitVersionExe/Arguments.cs index 65f7e1d8f8..90ba62962a 100644 --- a/src/GitVersionExe/Arguments.cs +++ b/src/GitVersionExe/Arguments.cs @@ -1,11 +1,14 @@ namespace GitVersion { + using System.Collections.Generic; + public class Arguments { public Arguments() { Authentication = new Authentication(); Output = OutputType.Json; + UpdateAssemblyInfoFileName = new HashSet(); } public Authentication Authentication; @@ -31,9 +34,15 @@ public Arguments() public string ExecArgs; public bool UpdateAssemblyInfo; - public string UpdateAssemblyInfoFileName; + public ISet UpdateAssemblyInfoFileName; + public bool EnsureAssemblyInfo; public bool ShowConfig; public bool NoFetch; + + public void AddAssemblyInfoFileName(string fileName) + { + UpdateAssemblyInfoFileName.Add(fileName); + } } } \ No newline at end of file diff --git a/src/GitVersionExe/AssemblyInfoFileUpdate.cs b/src/GitVersionExe/AssemblyInfoFileUpdate.cs index 99e02e70a0..226ff8803c 100644 --- a/src/GitVersionExe/AssemblyInfoFileUpdate.cs +++ b/src/GitVersionExe/AssemblyInfoFileUpdate.cs @@ -5,12 +5,13 @@ namespace GitVersion using System.IO; using System.Linq; using GitVersion.Helpers; + using GitVersion.VersionAssemblyInfoResources; class AssemblyInfoFileUpdate : IDisposable { List restoreBackupTasks = new List(); List cleanupBackupTasks = new List(); - + public AssemblyInfoFileUpdate(Arguments args, string workingDirectory, VersionVariables variables, IFileSystem fileSystem) { if (!args.UpdateAssemblyInfo) return; @@ -20,11 +21,11 @@ public AssemblyInfoFileUpdate(Arguments args, string workingDirectory, VersionVa var assemblyInfoFiles = GetAssemblyInfoFiles(workingDirectory, args, fileSystem); - foreach (var assemblyInfoFile in assemblyInfoFiles) + foreach (var assemblyInfoFile in assemblyInfoFiles.Select(f => new FileInfo(f))) { - var backupAssemblyInfo = assemblyInfoFile + ".bak"; - var localAssemblyInfo = assemblyInfoFile; - fileSystem.Copy(assemblyInfoFile, backupAssemblyInfo, true); + var backupAssemblyInfo = assemblyInfoFile.FullName + ".bak"; + var localAssemblyInfo = assemblyInfoFile.FullName; + fileSystem.Copy(assemblyInfoFile.FullName, backupAssemblyInfo, true); restoreBackupTasks.Add(() => { if (fileSystem.Exists(localAssemblyInfo)) @@ -36,29 +37,58 @@ public AssemblyInfoFileUpdate(Arguments args, string workingDirectory, VersionVa var assemblyVersion = variables.AssemblySemVer; var assemblyInfoVersion = variables.InformationalVersion; var assemblyFileVersion = variables.MajorMinorPatch + ".0"; - var fileContents = fileSystem.ReadAllText(assemblyInfoFile) + var fileContents = fileSystem.ReadAllText(assemblyInfoFile.FullName) .RegexReplace(@"AssemblyVersion\(""[^""]*""\)", string.Format("AssemblyVersion(\"{0}\")", assemblyVersion)) .RegexReplace(@"AssemblyInformationalVersion\(""[^""]*""\)", string.Format("AssemblyInformationalVersion(\"{0}\")", assemblyInfoVersion)) .RegexReplace(@"AssemblyFileVersion\(""[^""]*""\)", string.Format("AssemblyFileVersion(\"{0}\")", assemblyFileVersion)); - fileSystem.WriteAllText(assemblyInfoFile, fileContents); + fileSystem.WriteAllText(assemblyInfoFile.FullName, fileContents); } } static IEnumerable GetAssemblyInfoFiles(string workingDirectory, Arguments args, IFileSystem fileSystem) { - if (args.UpdateAssemblyInfoFileName != null) + if (args.UpdateAssemblyInfoFileName != null && args.UpdateAssemblyInfoFileName.Any(x => !string.IsNullOrWhiteSpace(x))) { - var fullPath = Path.Combine(workingDirectory, args.UpdateAssemblyInfoFileName); + foreach (var item in args.UpdateAssemblyInfoFileName) + { + var fullPath = Path.Combine(workingDirectory, item); - if (fileSystem.Exists(fullPath)) + if (EnsureVersionAssemblyInfoFile(args, fileSystem, fullPath)) + { + yield return fullPath; + } + } + } + else + { + foreach (var item in fileSystem.DirectoryGetFiles(workingDirectory, "AssemblyInfo.*", SearchOption.AllDirectories) + .Where(f => f.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".vb", StringComparison.OrdinalIgnoreCase))) { - return new[] { fullPath }; + yield return item; } } + } - return fileSystem.DirectoryGetFiles(workingDirectory, "AssemblyInfo.*", SearchOption.AllDirectories) - .Where(f => f.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".vb", StringComparison.OrdinalIgnoreCase)); + static bool EnsureVersionAssemblyInfoFile(Arguments arguments, IFileSystem fileSystem, string fullPath) + { + if (fileSystem.Exists(fullPath)) return true; + + if (!arguments.EnsureAssemblyInfo) return false; + + var assemblyInfoSource = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath); + if (!string.IsNullOrWhiteSpace(assemblyInfoSource)) + { + var fileInfo = new FileInfo(fullPath); + if (!fileSystem.DirectoryExists(fileInfo.Directory.FullName)) + { + fileSystem.CreateDirectory(fileInfo.Directory.FullName); + } + fileSystem.WriteAllText(fullPath, assemblyInfoSource); + return true; + } + Logger.WriteWarning(string.Format("No version assembly info template available to create source file '{0}'", arguments.UpdateAssemblyInfoFileName)); + return false; } public void Dispose() diff --git a/src/GitVersionExe/GitVersionExe.csproj b/src/GitVersionExe/GitVersionExe.csproj index 12c033e9c2..bf96ac79d1 100644 --- a/src/GitVersionExe/GitVersionExe.csproj +++ b/src/GitVersionExe/GitVersionExe.csproj @@ -48,6 +48,7 @@ True + diff --git a/src/GitVersionExe/HelpWriter.cs b/src/GitVersionExe/HelpWriter.cs index 2b23372eb1..41be8958c3 100644 --- a/src/GitVersionExe/HelpWriter.cs +++ b/src/GitVersionExe/HelpWriter.cs @@ -31,7 +31,11 @@ path The directory containing .git. If not defined current directory Will recursively search for all 'AssemblyInfo.cs' files in the git repo and update them /updateassemblyinfofilename Specify name of AssemblyInfo file. Can also /updateAssemblyInfo GlobalAssemblyInfo.cs as a shorthand - + /ensureassemblyinfo + If the assembly info file specified with /updateassemblyinfo or /updateassemblyinfofilename is not found, + it be created with these attributes: AssemblyFileVersion, FileVersion and AssemblyInformationalVersion + --- + Supports writing version info for: C#, F#, VB # Remote repository args /url Url to remote git repository. /b Name of the branch to use on the remote repository, must be used in combination with /url.