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.
- checksums.yaml +4 -4
- data/helpers/build +45 -0
- data/helpers/lib/NuGetUpdater/.editorconfig +364 -0
- data/helpers/lib/NuGetUpdater/.gitignore +5 -0
- data/helpers/lib/NuGetUpdater/Directory.Build.props +10 -0
- data/helpers/lib/NuGetUpdater/Directory.Common.props +16 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Build.props +14 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Build.targets +7 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Packages.props +29 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Build.Tasks/NuGet.Build.Tasks.csproj +27 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/AssemblyMetadataExtractor.cs +203 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/NuGet.CommandLine.csproj +33 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Commands/NuGet.Commands.csproj +26 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Common/NuGet.Common.csproj +21 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Config +6 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Configuration/NuGet.Configuration.csproj +24 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Credentials/NuGet.Credentials.csproj +20 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.DependencyResolver.Core/NuGet.DependencyResolver.Core.csproj +22 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Frameworks/NuGet.Frameworks.csproj +17 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.LibraryModel/NuGet.LibraryModel.csproj +17 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.PackageManagement/NuGet.PackageManagement.csproj +27 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Packaging/NuGet.Packaging.csproj +28 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.ProjectModel/NuGet.ProjectModel.csproj +20 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Protocol/NuGet.Protocol.csproj +21 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Resolver/NuGet.Resolver.csproj +20 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Versioning/NuGet.Versioning.csproj +17 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/README.md +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/FrameworkCheckCommand.cs +35 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +43 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/NuGetUpdater.Cli.csproj +20 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +31 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.FrameworkCheck.cs +42 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +323 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/NuGetUpdater.Cli.Test.csproj +24 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Dependency.cs +3 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyType.cs +12 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/BuildFile.cs +97 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/DotNetToolsJsonBuildFile.cs +24 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/GlobalJsonBuildFile.cs +25 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +32 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +31 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +94 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/XmlBuildFile.cs +14 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +39 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +73 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +146 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +27 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +316 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectResolver.cs +87 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/ConfigurationFile.cs +3 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +66 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +48 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +172 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +498 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateResult.cs +7 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +105 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +222 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/Logger.cs +24 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +443 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +15 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +69 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +66 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +124 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/DotNetToolsJsonBuildFileTests.cs +52 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/GlobalJsonBuildFileTests.cs +63 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/PackagesConfigBuildFileTests.cs +63 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/ProjectBuildFileTests.cs +154 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/CompatibilityCheckerFacts.cs +64 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/FrameworkCompatibilityServiceFacts.cs +122 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/SupportedFrameworkFacts.cs +68 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj +23 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +36 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestExtensions.cs +15 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +79 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorker.DirsProj.cs +201 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +147 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +225 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +217 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +94 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +938 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +2177 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/JsonHelperTests.cs +239 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +394 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +179 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterTests.cs +238 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.sln +152 -0
- data/helpers/lib/NuGetUpdater/xunit.runner.json +4 -0
- data/lib/dependabot/nuget/metadata_finder.rb +4 -4
- 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,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
|
+
}
|