dependabot-nuget 0.240.0 → 0.241.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/build +45 -0
  3. data/helpers/lib/NuGetUpdater/.editorconfig +364 -0
  4. data/helpers/lib/NuGetUpdater/.gitignore +5 -0
  5. data/helpers/lib/NuGetUpdater/Directory.Build.props +10 -0
  6. data/helpers/lib/NuGetUpdater/Directory.Common.props +16 -0
  7. data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Build.props +14 -0
  8. data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Build.targets +7 -0
  9. data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Packages.props +29 -0
  10. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Build.Tasks/NuGet.Build.Tasks.csproj +27 -0
  11. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/AssemblyMetadataExtractor.cs +203 -0
  12. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/NuGet.CommandLine.csproj +33 -0
  13. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Commands/NuGet.Commands.csproj +26 -0
  14. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Common/NuGet.Common.csproj +21 -0
  15. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Config +6 -0
  16. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Configuration/NuGet.Configuration.csproj +24 -0
  17. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Credentials/NuGet.Credentials.csproj +20 -0
  18. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.DependencyResolver.Core/NuGet.DependencyResolver.Core.csproj +22 -0
  19. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Frameworks/NuGet.Frameworks.csproj +17 -0
  20. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.LibraryModel/NuGet.LibraryModel.csproj +17 -0
  21. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.PackageManagement/NuGet.PackageManagement.csproj +27 -0
  22. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Packaging/NuGet.Packaging.csproj +28 -0
  23. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.ProjectModel/NuGet.ProjectModel.csproj +20 -0
  24. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Protocol/NuGet.Protocol.csproj +21 -0
  25. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Resolver/NuGet.Resolver.csproj +20 -0
  26. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Versioning/NuGet.Versioning.csproj +17 -0
  27. data/helpers/lib/NuGetUpdater/NuGetProjects/README.md +1 -0
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/FrameworkCheckCommand.cs +35 -0
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +43 -0
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/NuGetUpdater.Cli.csproj +20 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +31 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.FrameworkCheck.cs +42 -0
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +323 -0
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/NuGetUpdater.Cli.Test.csproj +24 -0
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Dependency.cs +3 -0
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyType.cs +12 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/BuildFile.cs +97 -0
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/DotNetToolsJsonBuildFile.cs +24 -0
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/GlobalJsonBuildFile.cs +25 -0
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +32 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +31 -0
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +94 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/XmlBuildFile.cs +14 -0
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +39 -0
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +73 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +146 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +27 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +316 -0
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectResolver.cs +87 -0
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/ConfigurationFile.cs +3 -0
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +66 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +48 -0
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +172 -0
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +498 -0
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateResult.cs +7 -0
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +105 -0
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +222 -0
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/Logger.cs +24 -0
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +443 -0
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +15 -0
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +69 -0
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +66 -0
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +124 -0
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/DotNetToolsJsonBuildFileTests.cs +52 -0
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/GlobalJsonBuildFileTests.cs +63 -0
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/PackagesConfigBuildFileTests.cs +63 -0
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/ProjectBuildFileTests.cs +154 -0
  68. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/CompatibilityCheckerFacts.cs +64 -0
  69. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/FrameworkCompatibilityServiceFacts.cs +122 -0
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/SupportedFrameworkFacts.cs +68 -0
  71. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj +23 -0
  72. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +36 -0
  73. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestExtensions.cs +15 -0
  74. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +79 -0
  75. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorker.DirsProj.cs +201 -0
  76. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +147 -0
  77. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +225 -0
  78. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +217 -0
  79. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +94 -0
  80. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +938 -0
  81. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +2177 -0
  82. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/JsonHelperTests.cs +239 -0
  83. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +394 -0
  84. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +179 -0
  85. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterTests.cs +238 -0
  86. data/helpers/lib/NuGetUpdater/NuGetUpdater.sln +152 -0
  87. data/helpers/lib/NuGetUpdater/xunit.runner.json +4 -0
  88. data/lib/dependabot/nuget/metadata_finder.rb +4 -4
  89. metadata +91 -5
@@ -0,0 +1,316 @@
1
+ extern alias CoreV2;
2
+
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.IO;
6
+ using System.Linq;
7
+ using System.Threading.Tasks;
8
+ using System.Xml.Linq;
9
+
10
+ using Microsoft.Language.Xml;
11
+
12
+ using NuGet.ProjectManagement;
13
+
14
+ using AssemblyBinding = CoreV2::NuGet.Runtime.AssemblyBinding;
15
+
16
+ namespace NuGetUpdater.Core;
17
+
18
+ internal static class BindingRedirectManager
19
+ {
20
+ private static readonly XName AssemblyBindingName = AssemblyBinding.GetQualifiedName("assemblyBinding");
21
+ private static readonly XName DependentAssemblyName = AssemblyBinding.GetQualifiedName("dependentAssembly");
22
+ private static readonly XName BindingRedirectName = AssemblyBinding.GetQualifiedName("bindingRedirect");
23
+
24
+ public static async ValueTask UpdateBindingRedirectsAsync(ProjectBuildFile projectBuildFile)
25
+ {
26
+ var configFile = await TryGetRuntimeConfigurationFile(projectBuildFile);
27
+ if (configFile is null)
28
+ {
29
+ // no runtime config file so no need to add binding redirects
30
+ return;
31
+ }
32
+
33
+ var references = ExtractReferenceElements(projectBuildFile);
34
+ references = ToAbsolutePaths(references, projectBuildFile.Path);
35
+
36
+ var bindings = BindingRedirectResolver.GetBindingRedirects(projectBuildFile.Path, references.Select(static x => x.Include));
37
+ if (!bindings.Any())
38
+ {
39
+ // no bindings to update
40
+ return;
41
+ }
42
+
43
+ var fileContent = AddBindingRedirects(configFile, bindings);
44
+ configFile = configFile with { Content = fileContent };
45
+
46
+ await File.WriteAllTextAsync(configFile.Path, configFile.Content);
47
+
48
+ if (configFile.ShouldAddToProject)
49
+ {
50
+ AddConfigFileToProject(projectBuildFile, configFile);
51
+ }
52
+
53
+ return;
54
+
55
+ static List<(string Include, string HintPath)> ExtractReferenceElements(ProjectBuildFile projectBuildFile)
56
+ {
57
+ var document = projectBuildFile.Contents;
58
+ var hintPaths = new List<(string Include, string HintPath)>();
59
+
60
+ foreach (var element in document.Descendants().Where(static x => x.Name == "Reference"))
61
+ {
62
+ // Extract Include attribute
63
+ var includeAttribute = element.GetAttribute("Include");
64
+ if (includeAttribute == null) continue;
65
+
66
+ // Check for HintPath as a child element
67
+ var hintPathElement = element.Elements.FirstOrDefault(static x => x.Name == "HintPath");
68
+ if (hintPathElement != null)
69
+ {
70
+ hintPaths.Add((includeAttribute.Value, hintPathElement.GetContentValue()));
71
+ }
72
+
73
+ // Check for HintPath as an attribute
74
+ var hintPathAttribute = element.GetAttribute("HintPath");
75
+ if (hintPathAttribute != null)
76
+ {
77
+ hintPaths.Add((includeAttribute.Value, hintPathAttribute.Value));
78
+ }
79
+ }
80
+
81
+ return hintPaths;
82
+ }
83
+
84
+ static void AddConfigFileToProject(ProjectBuildFile projectBuildFile, ConfigurationFile configFile)
85
+ {
86
+ var projectNode = projectBuildFile.Contents.RootSyntax;
87
+ var itemGroup = XmlExtensions.CreateOpenCloseXmlElementSyntax("ItemGroup")
88
+ .AddChild(
89
+ XmlExtensions.CreateSingleLineXmlElementSyntax("None")
90
+ .WithAttribute("Include", Path.GetRelativePath(Path.GetDirectoryName(projectBuildFile.Path)!, configFile.Path)));
91
+
92
+ var updatedProjectNode = projectNode.AddChild(itemGroup);
93
+ var updatedXml = projectBuildFile.Contents.ReplaceNode(projectNode.AsNode, updatedProjectNode.AsNode);
94
+ projectBuildFile.Update(updatedXml);
95
+ }
96
+
97
+ static List<(string Include, string HintPath)> ToAbsolutePaths(List<(string Include, string HintPath)> references, string projectPath)
98
+ {
99
+ var directoryPath = Path.GetDirectoryName(projectPath);
100
+ ArgumentNullException.ThrowIfNull(directoryPath, nameof(directoryPath));
101
+ return references.Select(t => (t.Include, Path.GetFullPath(Path.Combine(directoryPath, t.HintPath)))).ToList();
102
+ }
103
+ }
104
+
105
+ private static async ValueTask<ConfigurationFile?> TryGetRuntimeConfigurationFile(ProjectBuildFile projectBuildFile)
106
+ {
107
+ var directoryPath = Path.GetDirectoryName(projectBuildFile.Path);
108
+ if (directoryPath is null)
109
+ {
110
+ return null;
111
+ }
112
+
113
+ var configFile = projectBuildFile.ItemNodes
114
+ .Where(IsConfigFile)
115
+ .FirstOrDefault();
116
+
117
+ if (configFile is null)
118
+ {
119
+ return null;
120
+ }
121
+
122
+ var configFilePath = Path.GetFullPath(Path.Combine(directoryPath, GetContent(configFile)));
123
+ var configFileContents = await File.ReadAllTextAsync(configFilePath);
124
+ return new ConfigurationFile(configFilePath, configFileContents, false);
125
+
126
+ static string GetContent(IXmlElementSyntax element)
127
+ {
128
+ var content = element.GetContentValue();
129
+ if (!string.IsNullOrEmpty(content))
130
+ {
131
+ return content;
132
+ }
133
+
134
+ content = element.GetAttributeValue("Include");
135
+ if (!string.IsNullOrEmpty(content))
136
+ {
137
+ return content;
138
+ }
139
+
140
+ return string.Empty;
141
+ }
142
+
143
+ static bool IsConfigFile(IXmlElementSyntax element)
144
+ {
145
+ var content = GetContent(element);
146
+ if (content is null)
147
+ {
148
+ return false;
149
+ }
150
+
151
+ var path = Path.GetFileName(content);
152
+ return (element.Name == "None" && string.Equals(path, "app.config", StringComparison.OrdinalIgnoreCase))
153
+ || (element.Name == "Content" && string.Equals(path, "web.config", StringComparison.OrdinalIgnoreCase));
154
+ }
155
+
156
+ static string GetConfigFileName(XmlDocumentSyntax document)
157
+ {
158
+ var guidValue = document.Descendants()
159
+ .Where(static x => x.Name == "PropertyGroup")
160
+ .SelectMany(static x => x.Elements.Where(static x => x.Name == "ProjectGuid"))
161
+ .FirstOrDefault()
162
+ ?.GetContentValue();
163
+ return guidValue switch
164
+ {
165
+ "{E24C65DC-7377-472B-9ABA-BC803B73C61A}" or "{349C5851-65DF-11DA-9384-00065B846F21}" => "Web.config",
166
+ _ => "App.config"
167
+ };
168
+ }
169
+
170
+ static string GenerateDefaultAppConfig(XmlDocumentSyntax document)
171
+ {
172
+ var frameworkVersion = GetFrameworkVersion(document);
173
+ return $"""
174
+ <?xml version="1.0" encoding="utf-8" ?>
175
+ <configuration>
176
+ <startup>
177
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version={frameworkVersion}" />
178
+ </startup>
179
+ </configuration>
180
+ """;
181
+ }
182
+
183
+ static string? GetFrameworkVersion(XmlDocumentSyntax document)
184
+ {
185
+ return document.Descendants()
186
+ .Where(static x => x.Name == "PropertyGroup")
187
+ .SelectMany(static x => x.Elements.Where(static x => x.Name == "TargetFrameworkVersion"))
188
+ .FirstOrDefault()
189
+ ?.GetContentValue();
190
+ }
191
+ }
192
+
193
+ private static string AddBindingRedirects(ConfigurationFile configFile, IEnumerable<AssemblyBinding> bindingRedirects)
194
+ {
195
+ // Do nothing if there are no binding redirects to add, bail out
196
+ if (!bindingRedirects.Any())
197
+ {
198
+ return configFile.Content;
199
+ }
200
+
201
+ // Get the configuration file
202
+ var document = GetConfiguration(configFile.Content);
203
+
204
+ // Get the runtime element
205
+ var runtime = document.Root?.Element("runtime");
206
+
207
+ if (runtime == null)
208
+ {
209
+ // Add the runtime element to the configuration document
210
+ runtime = new XElement("runtime");
211
+ document.Root.AddIndented(runtime);
212
+ }
213
+
214
+ // Get all of the current bindings in config
215
+ var currentBindings = GetAssemblyBindings(runtime);
216
+
217
+ foreach (var bindingRedirect in bindingRedirects)
218
+ {
219
+ // Look to see if we already have this in the list of bindings already in config.
220
+ if (currentBindings.TryGetValue((bindingRedirect.Name, bindingRedirect.PublicKeyToken), out var existingBinding))
221
+ {
222
+ UpdateBindingRedirectElement(existingBinding, bindingRedirect);
223
+ }
224
+ else
225
+ {
226
+ // Get an assembly binding element to use
227
+ var assemblyBindingElement = GetAssemblyBindingElement(runtime);
228
+
229
+ // Add the binding to that element
230
+ assemblyBindingElement.AddIndented(bindingRedirect.ToXElement());
231
+ }
232
+ }
233
+
234
+ return document.ToString();
235
+
236
+ static XDocument GetConfiguration(string configFileContent)
237
+ {
238
+ try
239
+ {
240
+ return XDocument.Parse(configFileContent, LoadOptions.PreserveWhitespace);
241
+ }
242
+ catch (Exception ex)
243
+ {
244
+ throw new InvalidOperationException("Error loading binging redirect configuration", ex);
245
+ }
246
+ }
247
+
248
+ static void RemoveElement(XElement element)
249
+ {
250
+ // Hold onto the parent element before removing the element
251
+ var parentElement = element.Parent;
252
+
253
+ // Remove the element from the document if we find a match
254
+ element.RemoveIndented();
255
+
256
+ if (parentElement?.HasElements != true)
257
+ {
258
+ parentElement.RemoveIndented();
259
+ }
260
+ }
261
+
262
+ static void UpdateBindingRedirectElement(
263
+ XElement existingDependentAssemblyElement,
264
+ AssemblyBinding newBindingRedirect)
265
+ {
266
+ var existingBindingRedirectElement = existingDependentAssemblyElement.Element(BindingRedirectName);
267
+ // Since we've successfully parsed this node, it has to be valid and this child must exist.
268
+ if (existingBindingRedirectElement != null)
269
+ {
270
+ existingBindingRedirectElement.SetAttributeValue(XName.Get("oldVersion"), newBindingRedirect.OldVersion);
271
+ existingBindingRedirectElement.SetAttributeValue(XName.Get("newVersion"), newBindingRedirect.NewVersion);
272
+ }
273
+ else
274
+ {
275
+ // At this point, <dependentAssemblyElement> already exists, but <bindingRedirectElement> does not.
276
+ // So, extract the <bindingRedirectElement> from the newDependencyAssemblyElement, and add it
277
+ // to the existingDependentAssemblyElement
278
+ var newDependentAssemblyElement = newBindingRedirect.ToXElement();
279
+ var newBindingRedirectElement = newDependentAssemblyElement.Element(BindingRedirectName);
280
+ existingDependentAssemblyElement.AddIndented(newBindingRedirectElement);
281
+ }
282
+ }
283
+
284
+ static Dictionary<(string Name, string PublicKeyToken), XElement> GetAssemblyBindings(XElement runtime)
285
+ {
286
+ var dependencyAssemblyElements = runtime.Elements(AssemblyBindingName)
287
+ .Elements(DependentAssemblyName);
288
+
289
+ // We're going to need to know which element is associated with what binding for removal
290
+ var assemblyElementPairs = from dependentAssemblyElement in dependencyAssemblyElements
291
+ select new
292
+ {
293
+ Binding = AssemblyBinding.Parse(dependentAssemblyElement),
294
+ Element = dependentAssemblyElement
295
+ };
296
+
297
+ // Return a mapping from binding to element
298
+ return assemblyElementPairs.ToDictionary(p => (p.Binding.Name, p.Binding.PublicKeyToken), p => p.Element);
299
+ }
300
+
301
+ static XElement GetAssemblyBindingElement(XElement runtime)
302
+ {
303
+ // Pick the first assembly binding element or create one if there aren't any
304
+ var assemblyBinding = runtime.Elements(AssemblyBindingName).FirstOrDefault();
305
+ if (assemblyBinding is not null)
306
+ {
307
+ return assemblyBinding;
308
+ }
309
+
310
+ assemblyBinding = new XElement(AssemblyBindingName);
311
+ runtime.AddIndented(assemblyBinding);
312
+
313
+ return assemblyBinding;
314
+ }
315
+ }
316
+ }
@@ -0,0 +1,87 @@
1
+ extern alias CoreV2;
2
+
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.Diagnostics.CodeAnalysis;
6
+ using System.IO;
7
+ using System.Linq;
8
+ using System.Reflection;
9
+ using System.Text.RegularExpressions;
10
+
11
+ using AssemblyBinding = CoreV2::NuGet.Runtime.AssemblyBinding;
12
+ using IAssembly = CoreV2::NuGet.Runtime.IAssembly;
13
+
14
+ namespace NuGetUpdater.Core;
15
+
16
+ public static partial class BindingRedirectResolver
17
+ {
18
+ public static IEnumerable<AssemblyBinding> GetBindingRedirects(string projectPath, IEnumerable<string> includes)
19
+ {
20
+ var directoryPath = Path.GetDirectoryName(projectPath);
21
+ if (directoryPath is null)
22
+ {
23
+ yield break;
24
+ }
25
+
26
+ foreach (var include in includes)
27
+ {
28
+ if (TryParseIncludesString(include, out var assemblyInfo))
29
+ {
30
+ yield return new AssemblyBinding(assemblyInfo);
31
+ }
32
+ }
33
+
34
+ yield break;
35
+
36
+ static bool TryParseIncludesString(string include, [NotNullWhen(true)] out AssemblyWrapper? assemblyInfo)
37
+ {
38
+ assemblyInfo = null;
39
+ var name = include.Split(',').FirstOrDefault();
40
+ if (name is null)
41
+ {
42
+ return false;
43
+ }
44
+
45
+ var dict = IncludesRegex
46
+ .Matches(include)
47
+ .ToDictionary(static x => x.Groups["key"].Value, static x => x.Groups["value"].Value);
48
+
49
+ if (!dict.TryGetValue("Version", out var versionString) ||
50
+ !Version.TryParse(versionString, out var version))
51
+ {
52
+ return false;
53
+ }
54
+
55
+ dict.TryGetValue("PublicKeyToken", out var publicKeyToken);
56
+ dict.TryGetValue("Culture", out var culture);
57
+
58
+ assemblyInfo = new AssemblyWrapper(name, version, publicKeyToken, culture);
59
+ return true;
60
+ }
61
+ }
62
+
63
+ private static readonly Regex IncludesRegex = IncludesPattern();
64
+
65
+ /// <summary>
66
+ /// Wraps systme<see cref="Assembly"/> type in the nuget interface <see cref="IAssembly"/> to interop with nuget apis
67
+ /// </summary>
68
+ private class AssemblyWrapper : IAssembly
69
+ {
70
+ public AssemblyWrapper(string name, Version version, string? publicKeyToken = null, string? culture = null)
71
+ {
72
+ Name = name;
73
+ Version = version;
74
+ PublicKeyToken = publicKeyToken;
75
+ Culture = culture;
76
+ }
77
+
78
+ public string Name { get; }
79
+ public Version Version { get; }
80
+ public string? PublicKeyToken { get; }
81
+ public string? Culture { get; }
82
+ public IEnumerable<IAssembly> ReferencedAssemblies { get; } = Enumerable.Empty<AssemblyWrapper>();
83
+ }
84
+
85
+ [GeneratedRegex("(?<key>\\w+)=(?<value>[^,]+)")]
86
+ private static partial Regex IncludesPattern();
87
+ }
@@ -0,0 +1,3 @@
1
+ namespace NuGetUpdater.Core;
2
+
3
+ public record ConfigurationFile(string Path, string Content, bool ShouldAddToProject);
@@ -0,0 +1,66 @@
1
+ using System;
2
+ using System.Collections.Immutable;
3
+ using System.IO;
4
+ using System.Linq;
5
+ using System.Threading.Tasks;
6
+
7
+ namespace NuGetUpdater.Core;
8
+
9
+ internal static partial class DotNetToolsJsonUpdater
10
+ {
11
+ public static async Task UpdateDependencyAsync(string repoRootPath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, Logger logger)
12
+ {
13
+ var buildFiles = LoadBuildFiles(repoRootPath);
14
+ if (buildFiles.Length == 0)
15
+ {
16
+ logger.Log($" No dotnet-tools.json files found.");
17
+ return;
18
+ }
19
+
20
+ logger.Log($" Updating dotnet-tools.json files.");
21
+
22
+
23
+ var filesToUpdate = buildFiles.Where(f =>
24
+ f.GetDependencies().Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)))
25
+ .ToImmutableArray();
26
+ if (filesToUpdate.Length == 0)
27
+ {
28
+ logger.Log($" Dependency [{dependencyName}] not found in any dotnet-tools.json files.");
29
+ return;
30
+ }
31
+
32
+ foreach (var buildFile in filesToUpdate)
33
+ {
34
+ var tool = buildFile.Tools
35
+ .Single(kvp => kvp.Key.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
36
+
37
+ var toolObject = tool.Value?.AsObject();
38
+
39
+ if (toolObject is not null &&
40
+ toolObject["version"]?.GetValue<string>() == previousDependencyVersion)
41
+ {
42
+ buildFile.UpdateProperty(new[] { "tools", dependencyName, "version" }, newDependencyVersion);
43
+
44
+ if (await buildFile.SaveAsync())
45
+ {
46
+ logger.Log($" Saved [{buildFile.RepoRelativePath}].");
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ private static ImmutableArray<DotNetToolsJsonBuildFile> LoadBuildFiles(string repoRootPath)
53
+ {
54
+ var options = new EnumerationOptions()
55
+ {
56
+ RecurseSubdirectories = true,
57
+ MatchType = MatchType.Win32,
58
+ AttributesToSkip = 0,
59
+ IgnoreInaccessible = false,
60
+ MatchCasing = MatchCasing.CaseInsensitive,
61
+ };
62
+ return Directory.EnumerateFiles(repoRootPath, "dotnet-tools.json", options)
63
+ .Select(path => DotNetToolsJsonBuildFile.Open(repoRootPath, path))
64
+ .ToImmutableArray();
65
+ }
66
+ }
@@ -0,0 +1,48 @@
1
+ using System;
2
+ using System.IO;
3
+ using System.Linq;
4
+ using System.Threading.Tasks;
5
+
6
+ namespace NuGetUpdater.Core;
7
+
8
+ internal static partial class GlobalJsonUpdater
9
+ {
10
+ public static async Task UpdateDependencyAsync(string repoRootPath, string globalJsonPath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, Logger logger)
11
+ {
12
+ if (!File.Exists(globalJsonPath))
13
+ {
14
+ logger.Log($" No global.json file found at [{globalJsonPath}].");
15
+ return;
16
+ }
17
+
18
+ var globalJsonFile = GlobalJsonBuildFile.Open(repoRootPath, globalJsonPath);
19
+
20
+ logger.Log($" Updating [{globalJsonFile.RepoRelativePath}] file.");
21
+
22
+ var containsDependency = globalJsonFile.GetDependencies().Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
23
+ if (!containsDependency)
24
+ {
25
+ logger.Log($" Dependency [{dependencyName}] not found.");
26
+ return;
27
+ }
28
+
29
+ if (globalJsonFile.MSBuildSdks?.TryGetPropertyValue(dependencyName, out var version) != true
30
+ || version?.GetValue<string>() is not string versionString)
31
+ {
32
+ logger.Log($" Unable to determine dependency version.");
33
+ return;
34
+ }
35
+
36
+ if (versionString != previousDependencyVersion)
37
+ {
38
+ return;
39
+ }
40
+
41
+ globalJsonFile.UpdateProperty(["msbuild-sdks", dependencyName], newDependencyVersion);
42
+
43
+ if (await globalJsonFile.SaveAsync())
44
+ {
45
+ logger.Log($" Saved [{globalJsonFile.RepoRelativePath}].");
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,172 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.IO;
4
+ using System.Linq;
5
+ using System.Text;
6
+ using System.Threading.Tasks;
7
+ using System.Xml.Linq;
8
+
9
+ using Microsoft.Language.Xml;
10
+
11
+ namespace NuGetUpdater.Core;
12
+
13
+ internal static class PackagesConfigUpdater
14
+ {
15
+ public static async Task UpdateDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive, Logger logger)
16
+ {
17
+ logger.Log($" Found {NuGetHelper.PackagesConfigFileName}; running with NuGet.exe");
18
+
19
+ // use NuGet.exe to perform update
20
+
21
+ // ensure local packages directory exists
22
+ var projectBuildFile = ProjectBuildFile.Open(repoRootPath, projectPath);
23
+ var packagesSubDirectory = GetPathToPackagesDirectory(projectBuildFile, dependencyName, previousDependencyVersion);
24
+ if (packagesSubDirectory is null)
25
+ {
26
+ logger.Log($" Project [{projectPath}] does not reference this dependency.");
27
+ return;
28
+ }
29
+
30
+ logger.Log($" Using packages directory [{packagesSubDirectory}] for project [{projectPath}].");
31
+
32
+ var projectDirectory = Path.GetDirectoryName(projectPath);
33
+ var packagesConfigPath = PathHelper.JoinPath(projectDirectory, NuGetHelper.PackagesConfigFileName);
34
+
35
+ var packagesDirectory = PathHelper.JoinPath(projectDirectory, packagesSubDirectory);
36
+ Directory.CreateDirectory(packagesDirectory);
37
+
38
+ var args = new List<string>()
39
+ {
40
+ "update",
41
+ packagesConfigPath,
42
+ "-Id",
43
+ dependencyName,
44
+ "-Version",
45
+ newDependencyVersion,
46
+ "-RepositoryPath",
47
+ packagesDirectory,
48
+ "-NonInteractive",
49
+ };
50
+
51
+ logger.Log(" Finding MSBuild...");
52
+ var msbuildDirectory = MSBuildHelper.MSBuildPath;
53
+ if (msbuildDirectory is not null)
54
+ {
55
+ args.Add("-MSBuildPath");
56
+ args.Add(msbuildDirectory); // e.g., /usr/share/dotnet/sdk/7.0.203
57
+ }
58
+
59
+ RunNuget(args, packagesDirectory, logger);
60
+
61
+ projectBuildFile = ProjectBuildFile.Open(repoRootPath, projectPath);
62
+ projectBuildFile.NormalizeDirectorySeparatorsInProject();
63
+
64
+ // Update binding redirects
65
+ await BindingRedirectManager.UpdateBindingRedirectsAsync(projectBuildFile);
66
+
67
+ logger.Log(" Writing project file back to disk");
68
+ await projectBuildFile.SaveAsync();
69
+ }
70
+
71
+ private static void RunNuget(List<string> args, string packagesDirectory, Logger logger)
72
+ {
73
+ var outputBuilder = new StringBuilder();
74
+ var writer = new StringWriter(outputBuilder);
75
+
76
+ var originalOut = Console.Out;
77
+ var originalError = Console.Error;
78
+ Console.SetOut(writer);
79
+ Console.SetError(writer);
80
+
81
+ var currentDir = Environment.CurrentDirectory;
82
+ try
83
+ {
84
+ logger.Log($" Running NuGet.exe with args: {string.Join(" ", args)}");
85
+
86
+ Environment.CurrentDirectory = packagesDirectory;
87
+ var result = NuGet.CommandLine.Program.Main(args.ToArray());
88
+ var fullOutput = outputBuilder.ToString();
89
+ logger.Log($" Result: {result}");
90
+ logger.Log($" Output:\n{fullOutput}");
91
+ if (result != 0)
92
+ {
93
+ throw new Exception(fullOutput);
94
+ }
95
+ }
96
+ catch (Exception e)
97
+ {
98
+ logger.Log($"Error: {e}");
99
+ throw;
100
+ }
101
+ finally
102
+ {
103
+ Environment.CurrentDirectory = currentDir;
104
+ Console.SetOut(originalOut);
105
+ Console.SetError(originalError);
106
+ }
107
+ }
108
+
109
+ internal static string? GetPathToPackagesDirectory(ProjectBuildFile projectBuildFile, string dependencyName, string dependencyVersion)
110
+ {
111
+ // the packages directory can be found from the hint path of the matching dependency, e.g., when given "Newtonsoft.Json", "7.0.1", and a project like this:
112
+ // <Project>
113
+ // <ItemGroup>
114
+ // <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
115
+ // <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
116
+ // </Reference>
117
+ // <ItemGroup>
118
+ // </Project>
119
+ //
120
+ // the result should be "..\packages"
121
+ //
122
+ // first try to do an exact match with the provided version number, but optionally fall back to just matching the package name and _any_ version
123
+ var hintPathSubString = $"{dependencyName}.{dependencyVersion}";
124
+
125
+ string? partialPathMatch = null;
126
+ var hintPathNodes = projectBuildFile.Contents.Descendants()
127
+ .Where(e =>
128
+ e.Name.Equals("HintPath", StringComparison.OrdinalIgnoreCase) &&
129
+ e.Parent.Name.Equals("Reference", StringComparison.OrdinalIgnoreCase) &&
130
+ e.Parent.GetAttributeValue("Include", StringComparison.OrdinalIgnoreCase)?.StartsWith($"{dependencyName},", StringComparison.OrdinalIgnoreCase) == true);
131
+ foreach (var hintPathNode in hintPathNodes)
132
+ {
133
+ var hintPath = hintPathNode.GetContentValue();
134
+ var hintPathSubStringLocation = hintPath.IndexOf(hintPathSubString, StringComparison.OrdinalIgnoreCase);
135
+ if (hintPathSubStringLocation >= 0)
136
+ {
137
+ // exact match was found, use it
138
+ var subpath = GetUpToIndexWithoutTrailingDirectorySeparator(hintPath, hintPathSubStringLocation);
139
+ return subpath;
140
+ }
141
+
142
+ if (partialPathMatch is null)
143
+ {
144
+ var partialHintPathSubStringLocation = hintPath.IndexOf($"{dependencyName}.", StringComparison.OrdinalIgnoreCase);
145
+ if (partialHintPathSubStringLocation >= 0)
146
+ {
147
+ // look instead for, e.g., "Newtonsoft.Json.<digit>"
148
+ var candidateVersionLocation = partialHintPathSubStringLocation + dependencyName.Length + 1; // 1 is the dot
149
+ if (hintPath.Length > candidateVersionLocation && char.IsDigit(hintPath[candidateVersionLocation]))
150
+ {
151
+ // partial match was found, save it in case we don't find anything better
152
+ var subpath = GetUpToIndexWithoutTrailingDirectorySeparator(hintPath, partialHintPathSubStringLocation);
153
+ partialPathMatch = subpath;
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ return partialPathMatch;
160
+ }
161
+
162
+ private static string GetUpToIndexWithoutTrailingDirectorySeparator(string path, int index)
163
+ {
164
+ var subpath = path[..index];
165
+ if (subpath.EndsWith('/') || subpath.EndsWith('\\'))
166
+ {
167
+ subpath = subpath[..^1];
168
+ }
169
+
170
+ return subpath;
171
+ }
172
+ }