dependabot-nuget 0.239.0 → 0.241.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/cache_manager.rb +2 -0
- data/lib/dependabot/nuget/file_fetcher.rb +51 -40
- data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +0 -6
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +7 -22
- data/lib/dependabot/nuget/file_parser.rb +1 -1
- data/lib/dependabot/nuget/file_updater.rb +6 -2
- data/lib/dependabot/nuget/metadata_finder.rb +4 -4
- data/lib/dependabot/nuget/native_helpers.rb +7 -4
- data/lib/dependabot/nuget/nuget_client.rb +99 -0
- data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +71 -0
- data/lib/dependabot/nuget/requirement.rb +1 -1
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +2 -2
- data/lib/dependabot/nuget/update_checker/dependency_finder.rb +2 -2
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +1 -29
- data/lib/dependabot/nuget/update_checker/property_updater.rb +2 -2
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +39 -8
- data/lib/dependabot/nuget/update_checker/requirements_updater.rb +2 -2
- data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +2 -2
- data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
- data/lib/dependabot/nuget/update_checker/version_finder.rb +4 -42
- metadata +107 -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
|
+
}
|