From 00aeb1126f24181214ff735b53061cb851b40d6e Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Thu, 28 Jul 2016 12:14:36 +1000 Subject: [PATCH 1/6] Added Project.json file replacement option to GitVersion.exe --- src/GitVersionCore.Tests/TestFileSystem.cs | 17 ++- ...ldOnlyReplaceRootVersionValue.approved.txt | 11 ++ ...mattingInWeirdlyFormattedJson.approved.txt | 19 +++ .../GitVersionExe.Tests.csproj | 5 + .../ProjectJsonFileUpdateTests.cs | 122 ++++++++++++++++++ .../ProjectJsonVersionReplacerTests.cs | 115 +++++++++++++++++ src/GitVersionExe/ArgumentParser.cs | 6 + src/GitVersionExe/Arguments.cs | 1 + src/GitVersionExe/AssemblyInfoFileUpdate.cs | 26 +--- src/GitVersionExe/Extensions.cs | 3 +- src/GitVersionExe/FileUpdateBase.cs | 32 +++++ src/GitVersionExe/GitVersionExe.csproj | 18 +-- src/GitVersionExe/HelpWriter.cs | 6 + src/GitVersionExe/ProjectJsonFileUpdate.cs | 61 +++++++++ .../ProjectJsonVersionReplacer.cs | 96 ++++++++++++++ src/GitVersionExe/SpecifiedArgumentRunner.cs | 5 +- src/GitVersionExe/packages.config | 1 + 17 files changed, 508 insertions(+), 36 deletions(-) create mode 100644 src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldOnlyReplaceRootVersionValue.approved.txt create mode 100644 src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldWorkAndPreserveFormattingInWeirdlyFormattedJson.approved.txt create mode 100644 src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs create mode 100644 src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs create mode 100644 src/GitVersionExe/FileUpdateBase.cs create mode 100644 src/GitVersionExe/ProjectJsonFileUpdate.cs create mode 100644 src/GitVersionExe/ProjectJsonVersionReplacer.cs diff --git a/src/GitVersionCore.Tests/TestFileSystem.cs b/src/GitVersionCore.Tests/TestFileSystem.cs index f78d9c890c..630b041bdc 100644 --- a/src/GitVersionCore.Tests/TestFileSystem.cs +++ b/src/GitVersionCore.Tests/TestFileSystem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using GitVersion.Helpers; @@ -61,7 +62,21 @@ public void WriteAllText(string file, string fileContents) public IEnumerable DirectoryGetFiles(string directory, string searchPattern, SearchOption searchOption) { - throw new NotImplementedException(); + var files = searchOption == SearchOption.TopDirectoryOnly + ? fileSystem.Keys.Where(f => Path.GetDirectoryName(f).Equals(directory, StringComparison.CurrentCultureIgnoreCase)) + : fileSystem.Keys.Where(f => Path.GetDirectoryName(f).StartsWith(directory, StringComparison.CurrentCultureIgnoreCase)); + + if (searchPattern.StartsWith("*")) + { + var endsWith = searchPattern.Substring(1); + if(endsWith.Contains('*') || endsWith.Contains('?')) + throw new NotImplementedException(); + + return files.Where(f => f.EndsWith(endsWith, StringComparison.CurrentCultureIgnoreCase)); + } + if (searchPattern.Contains('*') || searchPattern.Contains('?')) + throw new NotImplementedException(); + return files.Where(f => Path.GetFileName(f).Equals(searchPattern, StringComparison.CurrentCultureIgnoreCase)); } public Stream OpenWrite(string path) diff --git a/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldOnlyReplaceRootVersionValue.approved.txt b/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldOnlyReplaceRootVersionValue.approved.txt new file mode 100644 index 0000000000..f1992dce0c --- /dev/null +++ b/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldOnlyReplaceRootVersionValue.approved.txt @@ -0,0 +1,11 @@ +{ + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.0" + } + }, + "version": "1.2.3-foo.4+2.Branch.alpha.Sha.ADF.bar", + "runtimes": { + "win8-x64": {} + } +} diff --git a/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldWorkAndPreserveFormattingInWeirdlyFormattedJson.approved.txt b/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldWorkAndPreserveFormattingInWeirdlyFormattedJson.approved.txt new file mode 100644 index 0000000000..4945c3fc83 --- /dev/null +++ b/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldWorkAndPreserveFormattingInWeirdlyFormattedJson.approved.txt @@ -0,0 +1,19 @@ +{ + "dependencies" + : +{ + "Microsoft.NETCore.App": { + "version": "1.0.0" + } + }, + "version" + : +"1.2.3-foo.4+2.Branch.alpha.Sha.ADF.bar" + +, + + +"runtimes": { + "win8-x64": {} + } +} diff --git a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj index c1b178c8e0..0b32a99099 100644 --- a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj +++ b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj @@ -114,6 +114,8 @@ + + @@ -126,6 +128,9 @@ + + + diff --git a/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs new file mode 100644 index 0000000000..0a5e425975 --- /dev/null +++ b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs @@ -0,0 +1,122 @@ +namespace GitVersionExe.Tests +{ + using System; + using System.IO; + using GitVersion; + using GitVersionCore.Tests; + using NUnit.Framework; + using Shouldly; + + public class ProjectJsonFileUpdateTests + { + private const string _testPath = @"x:\TestPath"; + private const string _projectJson = @"{ +""version"": """" +}"; + private const string _replacedJson = @"{ +""version"": ""1.2.3-foo.4+2.Branch.alpha.Sha.ADF.bar"" +}"; + + [Test] + public void ShouldCreateBackupsOfTheOriginalFilesAndRemoveThem() + { + var fs = new TestFileSystem(); + var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + using (CreateTestProjectJsonFileUpdate(fs)) + { + fs.ReadAllText(filename + ".bak").ShouldBe(_projectJson); + } + fs.Exists(filename + ".bak").ShouldBe(false); + } + + [Test] + public void ShouldReplaceJsonAndThenRestore() + { + var fs = new TestFileSystem(); + var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + using (CreateTestProjectJsonFileUpdate(fs)) + { + fs.ReadAllText(filename).ShouldBe(_replacedJson); + } + fs.ReadAllText(filename).ShouldBe(_projectJson); + } + + + [Test] + public void ShouldReplaceJsonAndNotRestoreIfDoNotRestoreFilesCalled() + { + var fs = new TestFileSystem(); + var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + using (var update = CreateTestProjectJsonFileUpdate(fs)) + { + fs.ReadAllText(filename).ShouldBe(_replacedJson); + update.DoNotRestoreFiles(); + } + fs.ReadAllText(filename).ShouldBe(_replacedJson); + } + + [Test] + public void ShouldRemoveBackupsIfDoNotRestoreFilesCalled() + { + var fs = new TestFileSystem(); + var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + using (var update = CreateTestProjectJsonFileUpdate(fs)) + { + fs.Exists(filename + ".bak").ShouldBe(true); + update.DoNotRestoreFiles(); + } + fs.Exists(filename + ".bak").ShouldBe(false); + } + + [Test] + public void ShouldNotReplaceJsonOutsideTestPath() + { + var fs = new TestFileSystem(); + var filename = CreateProjectJsonAndXProj(fs, ".."); + using (CreateTestProjectJsonFileUpdate(fs)) + { + fs.ReadAllText(filename).ShouldBe(_projectJson); + } + } + + [Test] + public void ShouldNotReplaceJsonIfNoCorrespondingXProjFile() + { + var fs = new TestFileSystem(); + var filename = Path.GetFullPath(Path.Combine(_testPath, "foo", "project.json")); + fs.WriteAllText(filename, _projectJson); + using (CreateTestProjectJsonFileUpdate(fs)) + { + fs.ReadAllText(filename).ShouldBe(_projectJson); + } + } + + + private string CreateProjectJsonAndXProj(TestFileSystem fs, string subdir) + { + var projectJsonFileName = Path.GetFullPath(Path.Combine(_testPath, subdir, "project.json")); + fs.WriteAllText(projectJsonFileName, _projectJson); + fs.WriteAllText(Path.Combine(_testPath, subdir, "foo.xproj"), _projectJson); + return projectJsonFileName; + } + + + private ProjectJsonFileUpdate CreateTestProjectJsonFileUpdate(TestFileSystem fs) + { + + var semVer = new SemanticVersion(1, 2, 3) + { + BuildMetaData = new SemanticVersionBuildMetaData(2, "alpha", "ADF", new DateTimeOffset(2011, 2, 3, 4, 5, 6, 7, TimeSpan.FromHours(2)), "bar"), + PreReleaseTag = new SemanticVersionPreReleaseTag("foo", 4) + }; + var variables = VariableProvider.GetVariablesFor(semVer, new TestEffectiveConfiguration(), true); + + return new ProjectJsonFileUpdate( + new Arguments() { UpdateProjectJson = true }, + _testPath, + variables, + fs + ); + } + } +} \ No newline at end of file diff --git a/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs b/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs new file mode 100644 index 0000000000..f3ac58632b --- /dev/null +++ b/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs @@ -0,0 +1,115 @@ +namespace GitVersionExe.Tests +{ + using System; + using GitVersion; + using GitVersionCore.Tests; + using NUnit.Framework; + using Shouldly; + + public class ProjectJsonVersionReplacerTests + { + [Test] + public void ShouldOnlyReplaceRootVersionValue() + { + var json = +@"{ + ""dependencies"": { + ""Microsoft.NETCore.App"": { + ""version"": ""1.0.0"" + } + }, + ""version"": ""1.1.0"", + ""runtimes"": { + ""win8-x64"": {} + } +} +"; + var result = ProjectJsonVersionReplacer.Replace(json, GetVariables()); + result.HasError.ShouldBe(false); + result.VersionElementNotFound.ShouldBe(false); + result.JsonWithReplacement.ShouldMatchApproved(c => c.SubFolder("Approved")); + } + + [Test] + public void ShouldWorkAndPreserveFormattingInWeirdlyFormattedJson() + { + var json = +@"{ + ""dependencies"" + : +{ + ""Microsoft.NETCore.App"": { + ""version"": ""1.0.0"" + } + }, + ""version"" + : +""1.1.0"" + +, + + +""runtimes"": { + ""win8-x64"": {} + } +} +"; + var result = ProjectJsonVersionReplacer.Replace(json, GetVariables()); + result.HasError.ShouldBe(false); + result.VersionElementNotFound.ShouldBe(false); + result.JsonWithReplacement.ShouldMatchApproved(c => c.SubFolder("Approved")); + } + + [Test] + public void ShouldNotBombOutOnMalformedJson() + { + var json = +@"{ + ""dependencies: { + ""Microsoft.NETCore.App"": { + ""version"": ""1.0.0"" + } + }, + ""version"": ""1.1.0"", + ""runtimes"": { + ""win8-x64"": {} + } +} +"; + var result = ProjectJsonVersionReplacer.Replace(json, GetVariables()); + result.HasError.ShouldBe(true); + result.Error.ShouldBe("Invalid character after parsing property name. Expected ':' but got: M. Path '', line 3, position 5."); + result.VersionElementNotFound.ShouldBe(false); + } + + [Test] + public void ShouldIndicateWhenNoVersionElementWasFound() + { + var json = +@"{ + ""dependencies"": { + ""Microsoft.NETCore.App"": { + ""version"": ""1.0.0"" + } + }, + ""runtimes"": { + ""win8-x64"": {} + } +} +"; + var result = ProjectJsonVersionReplacer.Replace(json, GetVariables()); + result.HasError.ShouldBe(false); + result.VersionElementNotFound.ShouldBe(true); + } + + private static VersionVariables GetVariables() + { + SemanticVersion semVer = new SemanticVersion(1, 2, 3) + { + BuildMetaData = new SemanticVersionBuildMetaData(2, "alpha", "ADF", new DateTimeOffset(2011, 2, 3, 4, 5, 6, 7, TimeSpan.FromHours(2)), "bar"), + PreReleaseTag = new SemanticVersionPreReleaseTag("foo", 4) + }; + return VariableProvider.GetVariablesFor(semVer, new TestEffectiveConfiguration(), true); + } + } +} \ No newline at end of file diff --git a/src/GitVersionExe/ArgumentParser.cs b/src/GitVersionExe/ArgumentParser.cs index 133f03009d..6f5e4ce521 100644 --- a/src/GitVersionExe/ArgumentParser.cs +++ b/src/GitVersionExe/ArgumentParser.cs @@ -268,6 +268,12 @@ public static Arguments ParseArguments(List commandLineArguments) continue; } + if (name.IsSwitch("updateprojectjson")) + { + arguments.UpdateProjectJson = !value.IsFalse(); + continue; + } + if (name.IsSwitch("overrideconfig")) { var keyValueOptions = (value ?? "").Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/GitVersionExe/Arguments.cs b/src/GitVersionExe/Arguments.cs index 97ba34ece9..fd52dd6534 100644 --- a/src/GitVersionExe/Arguments.cs +++ b/src/GitVersionExe/Arguments.cs @@ -40,6 +40,7 @@ public Arguments() public bool UpdateAssemblyInfo; public ISet UpdateAssemblyInfoFileName; public bool EnsureAssemblyInfo; + public bool UpdateProjectJson; public bool ShowConfig; public bool NoFetch; diff --git a/src/GitVersionExe/AssemblyInfoFileUpdate.cs b/src/GitVersionExe/AssemblyInfoFileUpdate.cs index c8ed0460c6..d8aac89587 100644 --- a/src/GitVersionExe/AssemblyInfoFileUpdate.cs +++ b/src/GitVersionExe/AssemblyInfoFileUpdate.cs @@ -9,11 +9,8 @@ namespace GitVersion using GitVersion.VersionAssemblyInfoResources; // TODO: Consolidate this with GitVersionTask.UpdateAssemblyInfo. @asbjornu - class AssemblyInfoFileUpdate : IDisposable + class AssemblyInfoFileUpdate : FileUpdateBase { - List restoreBackupTasks = new List(); - List cleanupBackupTasks = new List(); - public AssemblyInfoFileUpdate(Arguments args, string workingDirectory, VersionVariables variables, IFileSystem fileSystem) { if (!args.UpdateAssemblyInfo) return; @@ -129,26 +126,5 @@ static bool EnsureVersionAssemblyInfoFile(Arguments arguments, IFileSystem fileS Logger.WriteWarning(string.Format("No version assembly info template available to create source file '{0}'", arguments.UpdateAssemblyInfoFileName)); return false; } - - public void Dispose() - { - foreach (var restoreBackup in restoreBackupTasks) - { - restoreBackup(); - } - - cleanupBackupTasks.Clear(); - restoreBackupTasks.Clear(); - } - - public void DoNotRestoreAssemblyInfo() - { - foreach (var cleanupBackupTask in cleanupBackupTasks) - { - cleanupBackupTask(); - } - cleanupBackupTasks.Clear(); - restoreBackupTasks.Clear(); - } } } \ No newline at end of file diff --git a/src/GitVersionExe/Extensions.cs b/src/GitVersionExe/Extensions.cs index e5ea80e9ca..60530c0eb3 100644 --- a/src/GitVersionExe/Extensions.cs +++ b/src/GitVersionExe/Extensions.cs @@ -104,7 +104,8 @@ public static bool ArgumentRequiresValue(this string argument, int argumentIndex "init", "updateassemblyinfo", "ensureassemblyinfo", - "nofetch" + "nofetch", + "updateprojectjson" }; var argumentMightRequireValue = !booleanArguments.Contains(argument.Substring(1), StringComparer.OrdinalIgnoreCase); diff --git a/src/GitVersionExe/FileUpdateBase.cs b/src/GitVersionExe/FileUpdateBase.cs new file mode 100644 index 0000000000..a62527c1e9 --- /dev/null +++ b/src/GitVersionExe/FileUpdateBase.cs @@ -0,0 +1,32 @@ +namespace GitVersion +{ + using System; + using System.Collections.Generic; + + public abstract class FileUpdateBase : IDisposable + { + protected readonly List restoreBackupTasks = new List(); + protected readonly List cleanupBackupTasks = new List(); + + public void Dispose() + { + foreach (var restoreBackup in restoreBackupTasks) + { + restoreBackup(); + } + + cleanupBackupTasks.Clear(); + restoreBackupTasks.Clear(); + } + + public void DoNotRestoreFiles() + { + foreach (var cleanupBackupTask in cleanupBackupTasks) + { + cleanupBackupTask(); + } + cleanupBackupTasks.Clear(); + restoreBackupTasks.Clear(); + } + } +} \ No newline at end of file diff --git a/src/GitVersionExe/GitVersionExe.csproj b/src/GitVersionExe/GitVersionExe.csproj index 6c92543b0e..38be020492 100644 --- a/src/GitVersionExe/GitVersionExe.csproj +++ b/src/GitVersionExe/GitVersionExe.csproj @@ -53,6 +53,10 @@ ..\packages\LibGit2Sharp.0.23.0-pre20150419160303\lib\net40\LibGit2Sharp.dll True + + ..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll + True + @@ -67,10 +71,13 @@ + + + @@ -147,23 +154,21 @@ - + - - + - - + @@ -174,7 +179,6 @@ - @@ -190,7 +194,6 @@ - @@ -214,7 +217,6 @@ - diff --git a/src/GitVersionExe/HelpWriter.cs b/src/GitVersionExe/HelpWriter.cs index 53a0d3ef1b..efb63d4cd5 100644 --- a/src/GitVersionExe/HelpWriter.cs +++ b/src/GitVersionExe/HelpWriter.cs @@ -38,6 +38,12 @@ Specify name of AssemblyInfo file. Can also /updateAssemblyInfo GlobalAssemblyIn it be created with these attributes: AssemblyFileVersion, AssemblyVersion and AssemblyInformationalVersion --- Supports writing version info for: C#, F#, VB + + # .NET Core + /updateprojectjson + Will recursively seearch for all 'project.json' files that also have a '.xproj' file in the same directory + and update the value of the 'version' attribute in the root of the JSON document + # 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. diff --git a/src/GitVersionExe/ProjectJsonFileUpdate.cs b/src/GitVersionExe/ProjectJsonFileUpdate.cs new file mode 100644 index 0000000000..62237911d4 --- /dev/null +++ b/src/GitVersionExe/ProjectJsonFileUpdate.cs @@ -0,0 +1,61 @@ +using GitVersion.Helpers; + +namespace GitVersion +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + + public class ProjectJsonFileUpdate : FileUpdateBase + { + public ProjectJsonFileUpdate(Arguments arguments, string targetPath, VersionVariables variables, IFileSystem fileSystem) + { + if (!arguments.UpdateProjectJson) + return; + + if (arguments.Output != OutputType.Json) + Logger.WriteInfo("Updating project.json files"); + + foreach (var file in GetFiles(targetPath, fileSystem).ToArray()) + { + ReplaceInFile(variables, fileSystem, file); + } + } + + private void ReplaceInFile(VersionVariables variables, IFileSystem fileSystem, string file) + { + var backupFile = file + ".bak"; + fileSystem.Copy(file, backupFile, true); + cleanupBackupTasks.Add(() => fileSystem.Delete(backupFile)); + restoreBackupTasks.Add(() => + { + if (!fileSystem.Exists(backupFile)) + return; + fileSystem.Copy(backupFile, file, true); + fileSystem.Delete(backupFile); + }); + + var json = fileSystem.ReadAllText(file); + var result = ProjectJsonVersionReplacer.Replace(json, variables); + if (result.HasError) + { + Logger.WriteError(string.Format("An error occured replacing version in {0}: {1}", file, result.Error)); + } + else if (result.VersionElementNotFound) + { + Logger.WriteWarning(string.Format("The version element was not found in {0}", file)); + } + else + { + Logger.WriteInfo(string.Format("Replacing version in {0}", file)); + fileSystem.WriteAllText(file, result.JsonWithReplacement); + } + } + + static IEnumerable GetFiles(string targetPath, IFileSystem fileSystem) + { + return fileSystem.DirectoryGetFiles(targetPath, "project.json", SearchOption.AllDirectories) + .Where(f => fileSystem.DirectoryGetFiles(Path.GetDirectoryName(f), "*.xproj", SearchOption.TopDirectoryOnly).Any()); + } + } +} \ No newline at end of file diff --git a/src/GitVersionExe/ProjectJsonVersionReplacer.cs b/src/GitVersionExe/ProjectJsonVersionReplacer.cs new file mode 100644 index 0000000000..367d475f0f --- /dev/null +++ b/src/GitVersionExe/ProjectJsonVersionReplacer.cs @@ -0,0 +1,96 @@ +namespace GitVersion +{ + using System; + using System.IO; + using System.Text; + using Newtonsoft.Json; + + internal static class ProjectJsonVersionReplacer + { + + public static ReplacementResult Replace(string json, VersionVariables variables) + { + try + { + var pos = GetVersionPosition(json); + if (pos == null) + return new ReplacementResult + { + VersionElementNotFound = true + }; + + return new ReplacementResult() + { + JsonWithReplacement = ReplaceVersion(json, pos, variables.InformationalVersion) + }; + } + catch (Exception ex) + { + return new ReplacementResult() + { + Error = ex.Message, + HasError = true + }; + } + } + + private static string ReplaceVersion(string contents, VersionPosition pos, string version) + { + var sb = new StringBuilder(); + using (var reader = new StringReader(contents)) + using (var writer = new StringWriter(sb)) + { + for (var x = 1; x < pos.LineNumber; x++) + writer.WriteLine(reader.ReadLine()); + + var str = reader.ReadLine(); + if (str != null) + { + writer.Write(str.Substring(0, pos.LinePosition - pos.Length - 1)); + writer.Write(version); + writer.WriteLine(str.Substring(pos.LinePosition - 1)); + } + writer.Write(reader.ReadToEnd()); + } + return sb.ToString(); + } + + private static VersionPosition GetVersionPosition(string contents) + { + using (var r = new JsonTextReader(new StringReader(contents))) + while (r.Read()) + { + if (r.Depth == 1 && r.TokenType == JsonToken.PropertyName && (string)r.Value == "version") + { + r.Read(); + if (r.TokenType == JsonToken.String) + return new VersionPosition(r.LineNumber, r.LinePosition, ((string)r.Value).Length); + } + } + + return null; + } + + class VersionPosition + { + public VersionPosition(int lineNumber, int linePosition, int length) + { + LineNumber = lineNumber; + LinePosition = linePosition; + Length = length; + } + + public int LineNumber { get; set; } + public int LinePosition { get; set; } + public int Length { get; set; } + } + + public class ReplacementResult + { + public bool VersionElementNotFound { get; set; } + public string JsonWithReplacement { get; set; } + public string Error { get; set; } + public bool HasError { get; set; } + } + } +} \ No newline at end of file diff --git a/src/GitVersionExe/SpecifiedArgumentRunner.cs b/src/GitVersionExe/SpecifiedArgumentRunner.cs index 55f3909679..9b3893af0f 100644 --- a/src/GitVersionExe/SpecifiedArgumentRunner.cs +++ b/src/GitVersionExe/SpecifiedArgumentRunner.cs @@ -56,12 +56,14 @@ public static void Run(Arguments arguments, IFileSystem fileSystem) } using (var assemblyInfoUpdate = new AssemblyInfoFileUpdate(arguments, targetPath, variables, fileSystem)) + using (var projectJsonUpdate = new ProjectJsonFileUpdate(arguments, targetPath, variables, fileSystem)) { var execRun = RunExecCommandIfNeeded(arguments, targetPath, variables); var msbuildRun = RunMsBuildIfNeeded(arguments, targetPath, variables); if (!execRun && !msbuildRun) { - assemblyInfoUpdate.DoNotRestoreAssemblyInfo(); + assemblyInfoUpdate.DoNotRestoreFiles(); + projectJsonUpdate.DoNotRestoreFiles(); //TODO Put warning back //if (!context.CurrentBuildServer.IsRunningInBuildAgent()) //{ @@ -71,6 +73,7 @@ public static void Run(Arguments arguments, IFileSystem fileSystem) //} } } + } static bool RunMsBuildIfNeeded(Arguments args, string workingDirectory, VersionVariables variables) diff --git a/src/GitVersionExe/packages.config b/src/GitVersionExe/packages.config index 3574c719e9..ac224ec71f 100644 --- a/src/GitVersionExe/packages.config +++ b/src/GitVersionExe/packages.config @@ -7,6 +7,7 @@ + \ No newline at end of file From 5b1f227fbadd055cac99c05984923cb9d6fb292d Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Thu, 28 Jul 2016 12:36:55 +1000 Subject: [PATCH 2/6] Documentation for `/updateprojectjson` switch --- docs/usage/command-line.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/usage/command-line.md b/docs/usage/command-line.md index d29b18ec18..41726c351d 100644 --- a/docs/usage/command-line.md +++ b/docs/usage/command-line.md @@ -56,6 +56,17 @@ Will result in command line argument error Will iterate through each file and update known attributes (`AssemblyVersion`, `AssemblyFileVersion`, `AssemblyInformationalVersion`). +## Replace version in project.json +`GitVersion.exe /updateprojectjson` will recursively search for all `project.json` files that are +in the same directory as a `.xproj` file. For each found file it will update the value of the `version` element in the +root of the document with the `InformationalVersion`. + +The `dotnet build` command uses the `major.minor.patch` version from the `project.json` as the file and assembly version if the corresponding +attributes do not exist. It also automatically adds the `AssemblyInformationalVersion` attribute with the full version excluding metadata. + +### Example: +`GitVersion.exe /updateprojectjson` + ## Override config `/overrideconfig [key=value]` will override appropriate key from 'GitVersion.yml'. From 758bfd7f5b8bc627db59d8c0dae4eb64f10b5a8b Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Thu, 28 Jul 2016 13:58:28 +1000 Subject: [PATCH 3/6] Handled Linux paths in the tests --- src/GitVersionExe.Tests/GitVersionExe.Tests.csproj | 1 - src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs | 3 ++- .../ProjectJsonVersionReplacerTests.cs | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj index 0b32a99099..e1ac4e0b23 100644 --- a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj +++ b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj @@ -128,7 +128,6 @@ - diff --git a/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs index 0a5e425975..0a9fab28cf 100644 --- a/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs +++ b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs @@ -9,7 +9,8 @@ public class ProjectJsonFileUpdateTests { - private const string _testPath = @"x:\TestPath"; + + private static readonly string _testPath = Environment.OSVersion.Platform == PlatformID.Unix ? "/usr/TestPath" : @"x:\TestPath"; private const string _projectJson = @"{ ""version"": """" }"; diff --git a/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs b/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs index f3ac58632b..063603e666 100644 --- a/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs +++ b/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs @@ -1,6 +1,7 @@ namespace GitVersionExe.Tests { using System; + using System.Runtime.CompilerServices; using GitVersion; using GitVersionCore.Tests; using NUnit.Framework; @@ -8,7 +9,15 @@ public class ProjectJsonVersionReplacerTests { + + [SetUp] + public void SetUp() + { + ShouldlyConfiguration.ShouldMatchApprovedDefaults.LocateTestMethodUsingAttribute(); + } + [Test] + [MethodImpl(MethodImplOptions.NoInlining)] public void ShouldOnlyReplaceRootVersionValue() { var json = @@ -31,6 +40,7 @@ public void ShouldOnlyReplaceRootVersionValue() } [Test] + [MethodImpl(MethodImplOptions.NoInlining)] public void ShouldWorkAndPreserveFormattingInWeirdlyFormattedJson() { var json = From 20e14794472f3a43783584b9b11610fab2cc4962 Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Thu, 28 Jul 2016 14:25:07 +1000 Subject: [PATCH 4/6] Handled Linux line endings in the tests. Added support for null version in the original file. --- ...ceSuccessfullyWhenValueIsNull.approved.txt | 11 +++++++ .../GitVersionExe.Tests.csproj | 1 + .../ProjectJsonFileUpdateTests.cs | 8 ++--- .../ProjectJsonVersionReplacerTests.cs | 31 +++++++++++++++++++ .../ProjectJsonVersionReplacer.cs | 27 +++++++++++++--- 5 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldReplaceSuccessfullyWhenValueIsNull.approved.txt diff --git a/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldReplaceSuccessfullyWhenValueIsNull.approved.txt b/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldReplaceSuccessfullyWhenValueIsNull.approved.txt new file mode 100644 index 0000000000..f1992dce0c --- /dev/null +++ b/src/GitVersionExe.Tests/Approved/ProjectJsonVersionReplacerTests.ShouldReplaceSuccessfullyWhenValueIsNull.approved.txt @@ -0,0 +1,11 @@ +{ + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.0" + } + }, + "version": "1.2.3-foo.4+2.Branch.alpha.Sha.ADF.bar", + "runtimes": { + "win8-x64": {} + } +} diff --git a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj index e1ac4e0b23..ab8aeba53d 100644 --- a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj +++ b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj @@ -129,6 +129,7 @@ + diff --git a/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs index 0a9fab28cf..538a450bf8 100644 --- a/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs +++ b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs @@ -11,12 +11,8 @@ public class ProjectJsonFileUpdateTests { private static readonly string _testPath = Environment.OSVersion.Platform == PlatformID.Unix ? "/usr/TestPath" : @"x:\TestPath"; - private const string _projectJson = @"{ -""version"": """" -}"; - private const string _replacedJson = @"{ -""version"": ""1.2.3-foo.4+2.Branch.alpha.Sha.ADF.bar"" -}"; + private static string _projectJson = "{\n\"version\": \"\"\n}".Replace("\n", Environment.NewLine); + private static string _replacedJson = "{\n\"version\": \"1.2.3-foo.4+2.Branch.alpha.Sha.ADF.bar\"\n}".Replace("\n", Environment.NewLine); [Test] public void ShouldCreateBackupsOfTheOriginalFilesAndRemoveThem() diff --git a/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs b/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs index 063603e666..412393a9c6 100644 --- a/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs +++ b/src/GitVersionExe.Tests/ProjectJsonVersionReplacerTests.cs @@ -18,6 +18,8 @@ public void SetUp() [Test] [MethodImpl(MethodImplOptions.NoInlining)] + [Category("NoMono")] + [Description("Won't run on Mono due to source information not being available for ShouldMatchApproved.")] public void ShouldOnlyReplaceRootVersionValue() { var json = @@ -41,6 +43,33 @@ public void ShouldOnlyReplaceRootVersionValue() [Test] [MethodImpl(MethodImplOptions.NoInlining)] + [Category("NoMono")] + [Description("Won't run on Mono due to source information not being available for ShouldMatchApproved.")] + public void ShouldReplaceSuccessfullyWhenValueIsNull() + { + var json = +@"{ + ""dependencies"": { + ""Microsoft.NETCore.App"": { + ""version"": ""1.0.0"" + } + }, + ""version"": null, + ""runtimes"": { + ""win8-x64"": {} + } +} +"; + var result = ProjectJsonVersionReplacer.Replace(json, GetVariables()); + result.HasError.ShouldBe(false); + result.VersionElementNotFound.ShouldBe(false); + result.JsonWithReplacement.ShouldMatchApproved(c => c.SubFolder("Approved")); + } + + [Test] + [MethodImpl(MethodImplOptions.NoInlining)] + [Category("NoMono")] + [Description("Won't run on Mono due to source information not being available for ShouldMatchApproved.")] public void ShouldWorkAndPreserveFormattingInWeirdlyFormattedJson() { var json = @@ -112,6 +141,8 @@ public void ShouldIndicateWhenNoVersionElementWasFound() result.VersionElementNotFound.ShouldBe(true); } + + private static VersionVariables GetVariables() { SemanticVersion semVer = new SemanticVersion(1, 2, 3) diff --git a/src/GitVersionExe/ProjectJsonVersionReplacer.cs b/src/GitVersionExe/ProjectJsonVersionReplacer.cs index 367d475f0f..a44019de1f 100644 --- a/src/GitVersionExe/ProjectJsonVersionReplacer.cs +++ b/src/GitVersionExe/ProjectJsonVersionReplacer.cs @@ -46,9 +46,21 @@ private static string ReplaceVersion(string contents, VersionPosition pos, strin var str = reader.ReadLine(); if (str != null) { - writer.Write(str.Substring(0, pos.LinePosition - pos.Length - 1)); - writer.Write(version); - writer.WriteLine(str.Substring(pos.LinePosition - 1)); + if (pos.IsNull) + { + writer.Write(str.Substring(0, pos.LinePosition - 4)); + writer.Write("\""); + writer.Write(version); + writer.Write("\""); + writer.WriteLine(str.Substring(pos.LinePosition)); + } + else + { + writer.Write(str.Substring(0, pos.LinePosition - pos.Length - 1)); + writer.Write(version); + writer.WriteLine(str.Substring(pos.LinePosition - 1)); + } + } writer.Write(reader.ReadToEnd()); } @@ -63,8 +75,11 @@ private static VersionPosition GetVersionPosition(string contents) if (r.Depth == 1 && r.TokenType == JsonToken.PropertyName && (string)r.Value == "version") { r.Read(); + if(r.TokenType == JsonToken.Null) + return new VersionPosition(r.LineNumber, r.LinePosition, 4, true); + if (r.TokenType == JsonToken.String) - return new VersionPosition(r.LineNumber, r.LinePosition, ((string)r.Value).Length); + return new VersionPosition(r.LineNumber, r.LinePosition, ((string)r.Value).Length, false); } } @@ -73,16 +88,18 @@ private static VersionPosition GetVersionPosition(string contents) class VersionPosition { - public VersionPosition(int lineNumber, int linePosition, int length) + public VersionPosition(int lineNumber, int linePosition, int length, bool isNull) { LineNumber = lineNumber; LinePosition = linePosition; Length = length; + IsNull = isNull; } public int LineNumber { get; set; } public int LinePosition { get; set; } public int Length { get; set; } + public bool IsNull { get; set; } } public class ReplacementResult From 44d775cae05e32d3b759ca4f07dd287403c8a8e1 Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Thu, 28 Jul 2016 15:14:29 +1000 Subject: [PATCH 5/6] Removed the requirement for a .xproj file in the same directory as that is a VisualStudioism. --- docs/usage/command-line.md | 7 +++--- .../ProjectJsonFileUpdateTests.cs | 25 +++++-------------- src/GitVersionExe/HelpWriter.cs | 4 +-- src/GitVersionExe/ProjectJsonFileUpdate.cs | 12 +++------ 4 files changed, 14 insertions(+), 34 deletions(-) diff --git a/docs/usage/command-line.md b/docs/usage/command-line.md index 41726c351d..1fa7c0ddea 100644 --- a/docs/usage/command-line.md +++ b/docs/usage/command-line.md @@ -57,12 +57,11 @@ Will result in command line argument error Will iterate through each file and update known attributes (`AssemblyVersion`, `AssemblyFileVersion`, `AssemblyInformationalVersion`). ## Replace version in project.json -`GitVersion.exe /updateprojectjson` will recursively search for all `project.json` files that are -in the same directory as a `.xproj` file. For each found file it will update the value of the `version` element in the -root of the document with the `InformationalVersion`. +`GitVersion.exe /updateprojectjson` will recursively search for all `project.json` files and set the `InformationalVersion` as +the value of the `version` element in the root of the JSON document. The `dotnet build` command uses the `major.minor.patch` version from the `project.json` as the file and assembly version if the corresponding -attributes do not exist. It also automatically adds the `AssemblyInformationalVersion` attribute with the full version excluding metadata. +attributes do not exist and also automatically adds the `AssemblyInformationalVersion` attribute with the full version excluding metadata. ### Example: `GitVersion.exe /updateprojectjson` diff --git a/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs index 538a450bf8..737eb89f96 100644 --- a/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs +++ b/src/GitVersionExe.Tests/ProjectJsonFileUpdateTests.cs @@ -18,7 +18,7 @@ public class ProjectJsonFileUpdateTests public void ShouldCreateBackupsOfTheOriginalFilesAndRemoveThem() { var fs = new TestFileSystem(); - var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + var filename = CreateProjectJson(fs, "MyProj"); using (CreateTestProjectJsonFileUpdate(fs)) { fs.ReadAllText(filename + ".bak").ShouldBe(_projectJson); @@ -30,7 +30,7 @@ public void ShouldCreateBackupsOfTheOriginalFilesAndRemoveThem() public void ShouldReplaceJsonAndThenRestore() { var fs = new TestFileSystem(); - var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + var filename = CreateProjectJson(fs, "MyProj"); using (CreateTestProjectJsonFileUpdate(fs)) { fs.ReadAllText(filename).ShouldBe(_replacedJson); @@ -43,7 +43,7 @@ public void ShouldReplaceJsonAndThenRestore() public void ShouldReplaceJsonAndNotRestoreIfDoNotRestoreFilesCalled() { var fs = new TestFileSystem(); - var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + var filename = CreateProjectJson(fs, "MyProj"); using (var update = CreateTestProjectJsonFileUpdate(fs)) { fs.ReadAllText(filename).ShouldBe(_replacedJson); @@ -56,7 +56,7 @@ public void ShouldReplaceJsonAndNotRestoreIfDoNotRestoreFilesCalled() public void ShouldRemoveBackupsIfDoNotRestoreFilesCalled() { var fs = new TestFileSystem(); - var filename = CreateProjectJsonAndXProj(fs, "MyProj"); + var filename = CreateProjectJson(fs, "MyProj"); using (var update = CreateTestProjectJsonFileUpdate(fs)) { fs.Exists(filename + ".bak").ShouldBe(true); @@ -69,19 +69,7 @@ public void ShouldRemoveBackupsIfDoNotRestoreFilesCalled() public void ShouldNotReplaceJsonOutsideTestPath() { var fs = new TestFileSystem(); - var filename = CreateProjectJsonAndXProj(fs, ".."); - using (CreateTestProjectJsonFileUpdate(fs)) - { - fs.ReadAllText(filename).ShouldBe(_projectJson); - } - } - - [Test] - public void ShouldNotReplaceJsonIfNoCorrespondingXProjFile() - { - var fs = new TestFileSystem(); - var filename = Path.GetFullPath(Path.Combine(_testPath, "foo", "project.json")); - fs.WriteAllText(filename, _projectJson); + var filename = CreateProjectJson(fs, ".."); using (CreateTestProjectJsonFileUpdate(fs)) { fs.ReadAllText(filename).ShouldBe(_projectJson); @@ -89,11 +77,10 @@ public void ShouldNotReplaceJsonIfNoCorrespondingXProjFile() } - private string CreateProjectJsonAndXProj(TestFileSystem fs, string subdir) + private string CreateProjectJson(TestFileSystem fs, string subdir) { var projectJsonFileName = Path.GetFullPath(Path.Combine(_testPath, subdir, "project.json")); fs.WriteAllText(projectJsonFileName, _projectJson); - fs.WriteAllText(Path.Combine(_testPath, subdir, "foo.xproj"), _projectJson); return projectJsonFileName; } diff --git a/src/GitVersionExe/HelpWriter.cs b/src/GitVersionExe/HelpWriter.cs index efb63d4cd5..67e13d6ebf 100644 --- a/src/GitVersionExe/HelpWriter.cs +++ b/src/GitVersionExe/HelpWriter.cs @@ -41,8 +41,8 @@ Specify name of AssemblyInfo file. Can also /updateAssemblyInfo GlobalAssemblyIn # .NET Core /updateprojectjson - Will recursively seearch for all 'project.json' files that also have a '.xproj' file in the same directory - and update the value of the 'version' attribute in the root of the JSON document + Will recursively search for all 'project.json' files and update the value of the 'version' attribute in + the root of the JSON document # Remote repository args /url Url to remote git repository. diff --git a/src/GitVersionExe/ProjectJsonFileUpdate.cs b/src/GitVersionExe/ProjectJsonFileUpdate.cs index 62237911d4..2939710183 100644 --- a/src/GitVersionExe/ProjectJsonFileUpdate.cs +++ b/src/GitVersionExe/ProjectJsonFileUpdate.cs @@ -2,7 +2,6 @@ namespace GitVersion { - using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,8 +14,9 @@ public ProjectJsonFileUpdate(Arguments arguments, string targetPath, VersionVari if (arguments.Output != OutputType.Json) Logger.WriteInfo("Updating project.json files"); - - foreach (var file in GetFiles(targetPath, fileSystem).ToArray()) + + var files = fileSystem.DirectoryGetFiles(targetPath, "project.json", SearchOption.AllDirectories).ToArray(); + foreach (var file in files) { ReplaceInFile(variables, fileSystem, file); } @@ -51,11 +51,5 @@ private void ReplaceInFile(VersionVariables variables, IFileSystem fileSystem, s fileSystem.WriteAllText(file, result.JsonWithReplacement); } } - - static IEnumerable GetFiles(string targetPath, IFileSystem fileSystem) - { - return fileSystem.DirectoryGetFiles(targetPath, "project.json", SearchOption.AllDirectories) - .Where(f => fileSystem.DirectoryGetFiles(Path.GetDirectoryName(f), "*.xproj", SearchOption.TopDirectoryOnly).Any()); - } } } \ No newline at end of file From 0f023ce674c4d9c040c15656042fbda9ade985d6 Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Thu, 28 Jul 2016 16:08:42 +1000 Subject: [PATCH 6/6] Empty change to see if GitHub recognises commit pushed during outage --- docs/usage/command-line.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/command-line.md b/docs/usage/command-line.md index 1fa7c0ddea..3480f97359 100644 --- a/docs/usage/command-line.md +++ b/docs/usage/command-line.md @@ -61,7 +61,7 @@ Will iterate through each file and update known attributes (`AssemblyVersion`, ` the value of the `version` element in the root of the JSON document. The `dotnet build` command uses the `major.minor.patch` version from the `project.json` as the file and assembly version if the corresponding -attributes do not exist and also automatically adds the `AssemblyInformationalVersion` attribute with the full version excluding metadata. +attributes do not exist and also automatically adds the `AssemblyInformationalVersion` attribute with the full version excluding metadata. ### Example: `GitVersion.exe /updateprojectjson` @@ -74,4 +74,4 @@ At the moment only `tag-prefix` option is supported. Read more about [Configurat It will not change config file 'GitVersion.yml'. ### Example: How to override configuration option 'tag-prefix' to use prefix 'custom' -`GitVersion.exe /output json /overrideconfig tag-prefix=custom` \ No newline at end of file +`GitVersion.exe /output json /overrideconfig tag-prefix=custom`