From 10a7f1ef8cf7503155859ba674afc58cf9742574 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Tue, 2 Apr 2024 23:27:05 -0400 Subject: [PATCH 1/4] feat: Add support for global.json updates --- src/NvGet/Contracts/FileType.cs | 7 ++- src/NvGet/Extensions/DocumentReference.cs | 30 ++++++++++ src/NvGet/Extensions/FileTypeExtensions.cs | 23 +++----- src/NvGet/Extensions/JsonDocumentReference.cs | 40 ++++++++++++++ src/NvGet/Extensions/XmlDocumentExtensions.cs | 15 +++-- src/NvGet/Extensions/XmlDocumentReference.cs | 40 ++++++++++++++ src/NvGet/Helpers/SolutionHelper.cs | 39 +++++++++++++ .../Extensions/XmlDocumentExtensions.cs | 55 ++++++++++++++----- src/NvGet/Tools/Updater/NuGetUpdater.cs | 17 ++++-- 9 files changed, 228 insertions(+), 38 deletions(-) create mode 100644 src/NvGet/Extensions/DocumentReference.cs create mode 100644 src/NvGet/Extensions/JsonDocumentReference.cs create mode 100644 src/NvGet/Extensions/XmlDocumentReference.cs diff --git a/src/NvGet/Contracts/FileType.cs b/src/NvGet/Contracts/FileType.cs index 3baa575..9cd1db2 100644 --- a/src/NvGet/Contracts/FileType.cs +++ b/src/NvGet/Contracts/FileType.cs @@ -38,9 +38,14 @@ public enum FileType /// CentralPackageManagement = 32, + /// + /// global.json files. + /// + GlobalJson = 64, + /// /// All the supported file types. /// - All = Nuspec | Csproj | DirectoryProps | DirectoryTargets | CentralPackageManagement, + All = Nuspec | Csproj | DirectoryProps | DirectoryTargets | CentralPackageManagement | GlobalJson, } } diff --git a/src/NvGet/Extensions/DocumentReference.cs b/src/NvGet/Extensions/DocumentReference.cs new file mode 100644 index 0000000..e276d34 --- /dev/null +++ b/src/NvGet/Extensions/DocumentReference.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NvGet.Entities; +using NuGet.Packaging.Core; +using NuGet.Versioning; +using Uno.Extensions; + +#if WINDOWS_UWP +using System.Text.RegularExpressions; +using Windows.Data.Xml.Dom; +using Windows.Storage; +using XmlDocument = Windows.Data.Xml.Dom.XmlDocument; +using XmlElement = Windows.Data.Xml.Dom.XmlElement; +using XmlNode = Windows.Data.Xml.Dom.IXmlNode; +#else +using XmlDocument = System.Xml.XmlDocument; +using XmlElement = System.Xml.XmlElement; +using XmlNode = System.Xml.XmlNode; +#endif + +namespace NvGet.Extensions +{ + public abstract class DocumentReference + { + public abstract Task Save(CancellationToken ct, string path); + } +} diff --git a/src/NvGet/Extensions/FileTypeExtensions.cs b/src/NvGet/Extensions/FileTypeExtensions.cs index 3265022..95c39dc 100644 --- a/src/NvGet/Extensions/FileTypeExtensions.cs +++ b/src/NvGet/Extensions/FileTypeExtensions.cs @@ -8,21 +8,16 @@ public static class FileTypeExtensions { public static string GetDescription(this FileType target) { - switch(target) + return target switch { - case FileType.Nuspec: - return ".nuspec"; - case FileType.Csproj: - return ".csproj"; - case FileType.DirectoryProps: - return "Directory.Build.targets"; - case FileType.DirectoryTargets: - return "Directory.Build.props"; - case FileType.CentralPackageManagement: - return "Directory.Packages.props"; - default: - return default; - } + FileType.Nuspec => ".nuspec", + FileType.Csproj => ".csproj", + FileType.DirectoryProps => "Directory.Build.targets", + FileType.DirectoryTargets => "Directory.Build.props", + FileType.CentralPackageManagement => "Directory.Packages.props", + FileType.GlobalJson => "global.json", + _ => default, + }; } public static bool HasAnyFlag(this FileType target, params FileType[] others) diff --git a/src/NvGet/Extensions/JsonDocumentReference.cs b/src/NvGet/Extensions/JsonDocumentReference.cs new file mode 100644 index 0000000..4b3c6c8 --- /dev/null +++ b/src/NvGet/Extensions/JsonDocumentReference.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NvGet.Entities; +using NuGet.Packaging.Core; +using NuGet.Versioning; +using Uno.Extensions; + +#if WINDOWS_UWP +using System.Text.RegularExpressions; +using Windows.Data.Xml.Dom; +using Windows.Storage; +using XmlDocument = Windows.Data.Xml.Dom.XmlDocument; +using XmlElement = Windows.Data.Xml.Dom.XmlElement; +using XmlNode = Windows.Data.Xml.Dom.IXmlNode; +#else +using XmlDocument = System.Xml.XmlDocument; +using XmlElement = System.Xml.XmlElement; +using XmlNode = System.Xml.XmlNode; +#endif + +namespace NvGet.Extensions +{ + public class JsonDocumentReference : DocumentReference + { + public JsonDocumentReference(string contents) + { + Contents = contents; + } + + public string Contents { get; set; } + + public override async Task Save(CancellationToken ct, string path) + { + System.IO.File.WriteAllText(path, Contents); + } + } +} diff --git a/src/NvGet/Extensions/XmlDocumentExtensions.cs b/src/NvGet/Extensions/XmlDocumentExtensions.cs index 98e323d..494df76 100644 --- a/src/NvGet/Extensions/XmlDocumentExtensions.cs +++ b/src/NvGet/Extensions/XmlDocumentExtensions.cs @@ -121,21 +121,28 @@ private static PackageIdentity CreatePackageIdentity(string id, string version) /// /// /// - public static async Task> OpenFiles( + public static async Task> OpenFiles( this IEnumerable references, CancellationToken ct ) { var files = references .SelectMany(r => r.Files) - .SelectMany(g => g.Value) + .SelectMany(g => g.Value.Select(file => (fileType: g.Key, file: file))) .Distinct(); - var documents = new Dictionary(); + var documents = new Dictionary(); foreach(var file in files) { - documents.Add(file, await file.LoadDocument(ct)); + if(file.fileType == Contracts.FileType.GlobalJson) + { + documents.Add(file.file, new JsonDocumentReference(System.IO.File.ReadAllText(file.file))); + } + else + { + documents.Add(file.file, new XmlDocumentReference(await file.file.LoadDocument(ct))); + } } return documents; diff --git a/src/NvGet/Extensions/XmlDocumentReference.cs b/src/NvGet/Extensions/XmlDocumentReference.cs new file mode 100644 index 0000000..12425c6 --- /dev/null +++ b/src/NvGet/Extensions/XmlDocumentReference.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NvGet.Entities; +using NuGet.Packaging.Core; +using NuGet.Versioning; +using Uno.Extensions; + +#if WINDOWS_UWP +using System.Text.RegularExpressions; +using Windows.Data.Xml.Dom; +using Windows.Storage; +using XmlDocument = Windows.Data.Xml.Dom.XmlDocument; +using XmlElement = Windows.Data.Xml.Dom.XmlElement; +using XmlNode = Windows.Data.Xml.Dom.IXmlNode; +#else +using XmlDocument = System.Xml.XmlDocument; +using XmlElement = System.Xml.XmlElement; +using XmlNode = System.Xml.XmlNode; +#endif + +namespace NvGet.Extensions +{ + public class XmlDocumentReference : DocumentReference + { + public XmlDocumentReference(XmlDocument document) + { + Document = document; + } + + public XmlDocument Document { get; } + + public override async Task Save(CancellationToken ct, string path) + { + Document.Save(ct, path); + } + } +} diff --git a/src/NvGet/Helpers/SolutionHelper.cs b/src/NvGet/Helpers/SolutionHelper.cs index 23054db..085ab7a 100644 --- a/src/NvGet/Helpers/SolutionHelper.cs +++ b/src/NvGet/Helpers/SolutionHelper.cs @@ -12,6 +12,7 @@ using NvGet.Extensions; using NuGet.Versioning; using Uno.Extensions; +using Newtonsoft.Json; namespace NvGet.Helpers { @@ -72,6 +73,16 @@ public static async Task GetPackageReferences( } } + if(fileType.HasFlag(FileType.GlobalJson)) + { + const FileType currentTarget = FileType.GlobalJson; + + foreach(var file in await GetDirectoryFiles(ct, solutionPath, currentTarget, log)) + { + packages.AddRange(await GetGlobalJsonFileReferences(ct, file, currentTarget, updateProperties)); + } + } + if(fileType.HasFlag(FileType.Nuspec)) { foreach(var f in await GetNuspecFiles(ct, solutionPath, log)) @@ -188,5 +199,33 @@ private static async Task GetFileReferences(CancellationToke .Select(g => new PackageReference(g.Key, new NuGetVersion(g.FirstOrDefault().Version), file, target)) .ToArray(); } + + private static async Task GetGlobalJsonFileReferences(CancellationToken ct, string file, FileType target, ICollection<(string PropertyName, string PackageId)> updateProperties) + { + if(file.IsNullOrEmpty()) + { + return Array.Empty(); + } + + // Parse the global.json file to find all the sdks + var json = await FileHelper.ReadFileContent(ct, file); + var globalJson = JsonConvert.DeserializeObject(json); + + var references = globalJson + ?.MSBuildSdks + ?.Select(s => new PackageIdentity(s.Key, new NuGetVersion(s.Value))) + .ToArray() ?? Array.Empty(); + + return references + .GroupBy(r => r.Id) + .Select(g => new PackageReference(g.Key, new NuGetVersion(g.FirstOrDefault().Version), file, target)) + .ToArray(); + } + } + + public class GlobalJson + { + [JsonProperty("msbuild-sdks")] + public Dictionary MSBuildSdks { get; set; } } } diff --git a/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs b/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs index 67e5d05..127a201 100644 --- a/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs +++ b/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Xml; +using Newtonsoft.Json; using NvGet.Extensions; +using NvGet.Helpers; using NvGet.Tools.Updater.Log; using Uno.Extensions; @@ -139,31 +142,57 @@ private static void SetAttributeOrChild(this XmlElement element, string name, st /// /// public static IEnumerable UpdateDependencies( - this XmlDocument document, + this DocumentReference document, UpdateOperation operation ) { - var operations = new List(); - - foreach(var node in document.SelectElements("dependency", $"[@id='{operation.PackageId}']")) + if(document is XmlDocumentReference xmlDocReference) { - var versionNodeValue = node.GetAttribute("version"); + var operations = new List(); - // only nodes with explicit version, skip expansion. - if(!versionNodeValue.Contains("{", System.StringComparison.OrdinalIgnoreCase)) + foreach(var node in xmlDocReference.Document.SelectElements("dependency", $"[@id='{operation.PackageId}']")) { - var currentOperation = operation.WithPreviousVersion(versionNodeValue); + var versionNodeValue = node.GetAttribute("version"); - if(currentOperation.ShouldProceed()) + // only nodes with explicit version, skip expansion. + if(!versionNodeValue.Contains("{", System.StringComparison.OrdinalIgnoreCase)) { - node.SetAttribute("version", currentOperation.UpdatedVersion.ToString()); + var currentOperation = operation.WithPreviousVersion(versionNodeValue); + + if(currentOperation.ShouldProceed()) + { + node.SetAttribute("version", currentOperation.UpdatedVersion.ToString()); + } + + operations.Add(currentOperation); } + } + + return operations; + } + else if(document is JsonDocumentReference jsonDocReference) + { + var operations = new List(); + + var globalJson = JsonConvert.DeserializeObject(jsonDocReference.Contents); + + if(globalJson.MSBuildSdks.TryGetValue(operation.PackageId, out var value)) + { + globalJson.MSBuildSdks[operation.PackageId] = operation.UpdatedVersion.ToString(); + + var currentOperation = operation.WithPreviousVersion(value); operations.Add(currentOperation); } - } - return operations; + jsonDocReference.Contents = JsonConvert.SerializeObject(globalJson); + + return operations; + } + else + { + throw new NotSupportedException(); + } } } } diff --git a/src/NvGet/Tools/Updater/NuGetUpdater.cs b/src/NvGet/Tools/Updater/NuGetUpdater.cs index 0cc21e1..0d1e71b 100644 --- a/src/NvGet/Tools/Updater/NuGetUpdater.cs +++ b/src/NvGet/Tools/Updater/NuGetUpdater.cs @@ -144,7 +144,7 @@ private async Task UpdateFiles( CancellationToken ct, UpdateOperation operation, Dictionary targetFiles, - Dictionary documents + Dictionary documents ) { var operations = new List(); @@ -166,14 +166,19 @@ Dictionary documents var currentOperation = operation.WithFilePath(path); - if(fileType.HasFlag(FileType.Nuspec)) + if(fileType.HasFlag(FileType.Nuspec) && document is XmlDocumentReference xmlDocReference) { - updates = document.UpdateDependencies(currentOperation); + updates = xmlDocReference.UpdateDependencies(currentOperation); } - else if(fileType.HasAnyFlag(FileType.DirectoryProps, FileType.DirectoryTargets, FileType.Csproj, FileType.CentralPackageManagement)) + else if(fileType.HasFlag(FileType.GlobalJson) && document is JsonDocumentReference jsonDocReference) { - updates = document.UpdatePackageReferences(currentOperation); - var propertyUpdates = document.UpdateUpdateProperties(currentOperation, _parameters.UpdateProperties); + updates = jsonDocReference.UpdateDependencies(currentOperation); + } + else if(fileType.HasAnyFlag(FileType.DirectoryProps, FileType.DirectoryTargets, FileType.Csproj, FileType.CentralPackageManagement) + && document is XmlDocumentReference xmlDocReference2) + { + updates = xmlDocReference2.Document.UpdatePackageReferences(currentOperation); + var propertyUpdates = xmlDocReference2.Document.UpdateUpdateProperties(currentOperation, _parameters.UpdateProperties); updates = updates.Concat(propertyUpdates); } From 964b56dd13bfea4ad8ccd81e68709b40235279ec Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Wed, 3 Apr 2024 13:35:02 -0400 Subject: [PATCH 2/4] feat: Support for `*Version` properties update --- src/NvGet.Tools.Updater/Readme.md | 12 ++++++++ src/NvGet/Extensions/XmlDocumentExtensions.cs | 29 +++++++++++++++++++ src/NvGet/Helpers/SolutionHelper.cs | 11 ++++++- .../Extensions/XmlDocumentExtensions.cs | 18 ++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/NvGet.Tools.Updater/Readme.md b/src/NvGet.Tools.Updater/Readme.md index 6fdbd4f..8ea9ae5 100644 --- a/src/NvGet.Tools.Updater/Readme.md +++ b/src/NvGet.Tools.Updater/Readme.md @@ -94,3 +94,15 @@ properties.json example: ] ``` In this case the `UnoVersion` property will be updated to the latest version of `Uno.UI` found in the solution. + +## Supported types of updates + +The nuget updater supports updating: +- `.csproj`, `Directory.Build.props`, `Directory.Build.targets` and `Directory.Packages.props` + For this type of files, the tool will update: + - `PackageReference` and `PackageVersion` items + - MSBuild properties using named this way `UnoWinUIVersion` or `UnoExtensionsNavigationVersion` +- `.nuspec` + For this type of files, the tool will update `reference` entries. +- `global.json` + For this type of files, the tool will update `msbuild-sdk` entries diff --git a/src/NvGet/Extensions/XmlDocumentExtensions.cs b/src/NvGet/Extensions/XmlDocumentExtensions.cs index 494df76..d122b02 100644 --- a/src/NvGet/Extensions/XmlDocumentExtensions.cs +++ b/src/NvGet/Extensions/XmlDocumentExtensions.cs @@ -88,6 +88,35 @@ public static PackageIdentity[] GetPackageReferences(this XmlDocument document) } } + // find all nodes inside a PropertyGroup node that for which the name is ending by Version + var propertyGroupVersionReferences = document + .SelectElements("PropertyGroup") + .SelectMany(pg => pg.SelectNodes("*").OfType()) + .Where(e => e.LocalName.EndsWith("Version", StringComparison.OrdinalIgnoreCase)); + + foreach(var versionProperty in propertyGroupVersionReferences) + { + var originalTrimmedName = versionProperty + .LocalName + .TrimEnd("Version"); + + var nameParts = + originalTrimmedName + .Select((c, i) => + i > 0 + && char.IsUpper(c) + && !char.IsUpper(originalTrimmedName[i - 1]) + ? "." + c + : c.ToString()); + + var packageName = string.Concat(nameParts).ToLowerInvariant(); + + if(NuGetVersion.TryParse(versionProperty.InnerText, out var nugetVersion)) + { + references.Add(CreatePackageIdentity(packageName, versionProperty.InnerText)); + } + } + return references .Trim() .ToArray(); diff --git a/src/NvGet/Helpers/SolutionHelper.cs b/src/NvGet/Helpers/SolutionHelper.cs index 085ab7a..2f82947 100644 --- a/src/NvGet/Helpers/SolutionHelper.cs +++ b/src/NvGet/Helpers/SolutionHelper.cs @@ -140,7 +140,16 @@ private static async Task GetDirectoryFiles(CancellationToken ct, stri else { var solutionFolder = Path.GetDirectoryName(solutionPath); - file = Path.Combine(solutionFolder, target.GetDescription()); + + if(target is FileType.DirectoryProps or FileType.DirectoryTargets or FileType.GlobalJson or FileType.CentralPackageManagement) + { + var matchingFiles = await FileHelper.GetFiles(ct, solutionFolder, nameFilter: target.GetDescription()); + return matchingFiles.ToArray(); + } + else + { + file = Path.Combine(solutionFolder, target.GetDescription()); + } } if(file.HasValue() && await FileHelper.Exists(file)) diff --git a/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs b/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs index 127a201..d2a0c65 100644 --- a/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs +++ b/src/NvGet/Tools/Updater/Extensions/XmlDocumentExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Xml; using Newtonsoft.Json; +using NuGet.Versioning; using NvGet.Extensions; using NvGet.Helpers; using NvGet.Tools.Updater.Log; @@ -95,6 +96,23 @@ UpdateOperation operation } } + var propertyGroupVersionReferences = document + .SelectElements("PropertyGroup") + .SelectMany(pg => pg.SelectNodes(packageId.Replace(".", "") + "Version").OfType()); + + foreach(var versionProperty in propertyGroupVersionReferences) + { + if(NuGetVersion.TryParse(versionProperty.InnerText, out var previousVersion)) + { + var currentOperation = operation.WithPreviousVersion(versionProperty.InnerText); + + if(currentOperation.ShouldProceed()) + { + versionProperty.InnerText = currentOperation.UpdatedVersion.ToString(); + } + } + } + return operations; } From 9b8e93a9ee4c6c29db18df5905abe94164785607 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Thu, 4 Apr 2024 16:21:53 -0400 Subject: [PATCH 3/4] chore: missing await --- src/NvGet/Extensions/XmlDocumentReference.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NvGet/Extensions/XmlDocumentReference.cs b/src/NvGet/Extensions/XmlDocumentReference.cs index 12425c6..937e477 100644 --- a/src/NvGet/Extensions/XmlDocumentReference.cs +++ b/src/NvGet/Extensions/XmlDocumentReference.cs @@ -34,7 +34,7 @@ public XmlDocumentReference(XmlDocument document) public override async Task Save(CancellationToken ct, string path) { - Document.Save(ct, path); + await Document.Save(ct, path); } } } From cddbb6b2c4e2675c0c381ccea8a83421c730b77f Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Thu, 4 Apr 2024 16:22:06 -0400 Subject: [PATCH 4/4] chore: Move globaljson in its own file --- src/NvGet/Helpers/GlobalJson.cs | 24 ++++++++++++++++++++++++ src/NvGet/Helpers/SolutionHelper.cs | 6 ------ 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/NvGet/Helpers/GlobalJson.cs diff --git a/src/NvGet/Helpers/GlobalJson.cs b/src/NvGet/Helpers/GlobalJson.cs new file mode 100644 index 0000000..81a0226 --- /dev/null +++ b/src/NvGet/Helpers/GlobalJson.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Common; +using NuGet.Packaging.Core; +using NvGet.Contracts; +using NvGet.Entities; +using NvGet.Extensions; +using NuGet.Versioning; +using Uno.Extensions; +using Newtonsoft.Json; + +namespace NvGet.Helpers +{ + public class GlobalJson + { + [JsonProperty("msbuild-sdks")] + public Dictionary MSBuildSdks { get; set; } + } +} diff --git a/src/NvGet/Helpers/SolutionHelper.cs b/src/NvGet/Helpers/SolutionHelper.cs index 2f82947..a3d3244 100644 --- a/src/NvGet/Helpers/SolutionHelper.cs +++ b/src/NvGet/Helpers/SolutionHelper.cs @@ -231,10 +231,4 @@ private static async Task GetGlobalJsonFileReferences(Cancel .ToArray(); } } - - public class GlobalJson - { - [JsonProperty("msbuild-sdks")] - public Dictionary MSBuildSdks { get; set; } - } }