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,498 @@
|
|
1
|
+
using System;
|
2
|
+
using System.Collections.Generic;
|
3
|
+
using System.Collections.Immutable;
|
4
|
+
using System.IO;
|
5
|
+
using System.Linq;
|
6
|
+
using System.Threading.Tasks;
|
7
|
+
|
8
|
+
using Microsoft.Build.Evaluation;
|
9
|
+
using Microsoft.Language.Xml;
|
10
|
+
|
11
|
+
using NuGet.Versioning;
|
12
|
+
|
13
|
+
namespace NuGetUpdater.Core;
|
14
|
+
|
15
|
+
internal static partial class SdkPackageUpdater
|
16
|
+
{
|
17
|
+
public static async Task UpdateDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive, Logger logger)
|
18
|
+
{
|
19
|
+
// SDK-style project, modify the XML directly
|
20
|
+
logger.Log(" Running for SDK-style project");
|
21
|
+
var buildFiles = await MSBuildHelper.LoadBuildFiles(repoRootPath, projectPath);
|
22
|
+
|
23
|
+
var newDependencyNuGetVersion = NuGetVersion.Parse(newDependencyVersion);
|
24
|
+
|
25
|
+
// update all dependencies, including transitive
|
26
|
+
var tfms = MSBuildHelper.GetTargetFrameworkMonikers(buildFiles);
|
27
|
+
|
28
|
+
// Get the set of all top-level dependencies in the current project
|
29
|
+
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependenyInfos(buildFiles).ToArray();
|
30
|
+
|
31
|
+
var packageFoundInDependencies = false;
|
32
|
+
var packageNeedsUpdating = false;
|
33
|
+
|
34
|
+
foreach (var tfm in tfms)
|
35
|
+
{
|
36
|
+
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, topLevelDependencies, logger);
|
37
|
+
foreach (var (packageName, packageVersion, _, _, _) in dependencies)
|
38
|
+
{
|
39
|
+
if (packageName.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
40
|
+
{
|
41
|
+
packageFoundInDependencies = true;
|
42
|
+
|
43
|
+
var nugetVersion = NuGetVersion.Parse(packageVersion);
|
44
|
+
if (nugetVersion < newDependencyNuGetVersion)
|
45
|
+
{
|
46
|
+
packageNeedsUpdating = true;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
// Skip updating the project if the dependency does not exist in the graph
|
53
|
+
if (!packageFoundInDependencies)
|
54
|
+
{
|
55
|
+
logger.Log($" Package [{dependencyName}] Does not exist as a dependency in [{projectPath}].");
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
|
59
|
+
// Skip updating the project if the dependency version meets or exceeds the newDependencyVersion
|
60
|
+
if (!packageNeedsUpdating)
|
61
|
+
{
|
62
|
+
logger.Log($" Package [{dependencyName}] already meets the requested dependency version in [{projectPath}].");
|
63
|
+
return;
|
64
|
+
}
|
65
|
+
|
66
|
+
var newDependency = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.Unknown) };
|
67
|
+
var tfmsAndDependencies = new Dictionary<string, Dependency[]>();
|
68
|
+
foreach (var tfm in tfms)
|
69
|
+
{
|
70
|
+
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, newDependency, logger);
|
71
|
+
tfmsAndDependencies[tfm] = dependencies;
|
72
|
+
}
|
73
|
+
|
74
|
+
// stop update process if we find conflicting package versions
|
75
|
+
var conflictingPackageVersionsFound = false;
|
76
|
+
var packagesAndVersions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
77
|
+
foreach (var (tfm, dependencies) in tfmsAndDependencies)
|
78
|
+
{
|
79
|
+
foreach (var (packageName, packageVersion, _, _, _) in dependencies)
|
80
|
+
{
|
81
|
+
if (packagesAndVersions.TryGetValue(packageName, out var existingVersion) &&
|
82
|
+
existingVersion != packageVersion)
|
83
|
+
{
|
84
|
+
logger.Log($" Package [{packageName}] tried to update to version [{packageVersion}], but found conflicting package version of [{existingVersion}].");
|
85
|
+
conflictingPackageVersionsFound = true;
|
86
|
+
}
|
87
|
+
else
|
88
|
+
{
|
89
|
+
packagesAndVersions[packageName] = packageVersion!;
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
if (conflictingPackageVersionsFound)
|
95
|
+
{
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
|
99
|
+
var unupgradableTfms = tfmsAndDependencies.Where(kvp => !kvp.Value.Any()).Select(kvp => kvp.Key);
|
100
|
+
if (unupgradableTfms.Any())
|
101
|
+
{
|
102
|
+
logger.Log($" The following target frameworks could not find packages to upgrade: {string.Join(", ", unupgradableTfms)}");
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
|
106
|
+
if (isTransitive)
|
107
|
+
{
|
108
|
+
var directoryPackagesWithPinning = buildFiles.OfType<ProjectBuildFile>()
|
109
|
+
.FirstOrDefault(bf => IsCpmTransitivePinningEnabled(bf));
|
110
|
+
if (directoryPackagesWithPinning is not null)
|
111
|
+
{
|
112
|
+
PinTransitiveDependency(directoryPackagesWithPinning, dependencyName, newDependencyVersion, logger);
|
113
|
+
}
|
114
|
+
else
|
115
|
+
{
|
116
|
+
await AddTransitiveDependencyAsync(projectPath, dependencyName, newDependencyVersion, logger);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
else
|
120
|
+
{
|
121
|
+
await UpdateTopLevelDepdendencyAsync(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, packagesAndVersions, logger);
|
122
|
+
}
|
123
|
+
|
124
|
+
var updatedTopLevelDependencies = MSBuildHelper.GetTopLevelPackageDependenyInfos(buildFiles);
|
125
|
+
foreach (var tfm in tfms)
|
126
|
+
{
|
127
|
+
var updatedPackages = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, updatedTopLevelDependencies.ToArray(), logger);
|
128
|
+
var dependenciesAreCoherent = await MSBuildHelper.DependenciesAreCoherentAsync(repoRootPath, projectPath, tfm, updatedPackages, logger);
|
129
|
+
if (!dependenciesAreCoherent)
|
130
|
+
{
|
131
|
+
logger.Log($" Package [{dependencyName}] could not be updated in [{projectPath}] because it would cause a dependency conflict.");
|
132
|
+
return;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
foreach (var buildFile in buildFiles)
|
137
|
+
{
|
138
|
+
if (await buildFile.SaveAsync())
|
139
|
+
{
|
140
|
+
logger.Log($" Saved [{buildFile.RepoRelativePath}].");
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
private static bool IsCpmTransitivePinningEnabled(ProjectBuildFile buildFile)
|
146
|
+
{
|
147
|
+
var buildFileName = Path.GetFileName(buildFile.Path);
|
148
|
+
if (!buildFileName.Equals("Directory.Packages.props", StringComparison.OrdinalIgnoreCase))
|
149
|
+
{
|
150
|
+
return false;
|
151
|
+
}
|
152
|
+
|
153
|
+
var propertyElements = buildFile.PropertyNodes;
|
154
|
+
|
155
|
+
var isCpmEnabledValue = propertyElements.FirstOrDefault(e =>
|
156
|
+
e.Name.Equals("ManagePackageVersionsCentrally", StringComparison.OrdinalIgnoreCase))?.GetContentValue();
|
157
|
+
if (isCpmEnabledValue is null || !string.Equals(isCpmEnabledValue, "true", StringComparison.OrdinalIgnoreCase))
|
158
|
+
{
|
159
|
+
return false;
|
160
|
+
}
|
161
|
+
|
162
|
+
var isTransitivePinningEnabled = propertyElements.FirstOrDefault(e =>
|
163
|
+
e.Name.Equals("CentralPackageTransitivePinningEnabled", StringComparison.OrdinalIgnoreCase))?.GetContentValue();
|
164
|
+
return isTransitivePinningEnabled is not null && string.Equals(isTransitivePinningEnabled, "true", StringComparison.OrdinalIgnoreCase);
|
165
|
+
}
|
166
|
+
|
167
|
+
private static void PinTransitiveDependency(ProjectBuildFile directoryPackages, string dependencyName, string newDependencyVersion, Logger logger)
|
168
|
+
{
|
169
|
+
var existingPackageVersionElement = directoryPackages.ItemNodes
|
170
|
+
.Where(e => e.Name.Equals("PackageVersion", StringComparison.OrdinalIgnoreCase) &&
|
171
|
+
e.Attributes.Any(a => a.Name.Equals("Include", StringComparison.OrdinalIgnoreCase) &&
|
172
|
+
a.Value.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)))
|
173
|
+
.FirstOrDefault();
|
174
|
+
|
175
|
+
logger.Log($" Pinning [{dependencyName}/{newDependencyVersion}] as a package version.");
|
176
|
+
|
177
|
+
var lastPackageVersion = directoryPackages.ItemNodes
|
178
|
+
.Where(e => e.Name.Equals("PackageVersion", StringComparison.OrdinalIgnoreCase))
|
179
|
+
.LastOrDefault();
|
180
|
+
|
181
|
+
if (lastPackageVersion is null)
|
182
|
+
{
|
183
|
+
logger.Log($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not pinned.");
|
184
|
+
return;
|
185
|
+
}
|
186
|
+
|
187
|
+
var lastItemGroup = lastPackageVersion.Parent;
|
188
|
+
|
189
|
+
IXmlElementSyntax updatedItemGroup;
|
190
|
+
if (existingPackageVersionElement is null)
|
191
|
+
{
|
192
|
+
// need to add a new entry
|
193
|
+
logger.Log(" New PackageVersion element added.");
|
194
|
+
var leadingTrivia = lastPackageVersion.AsNode.GetLeadingTrivia();
|
195
|
+
var packageVersionElement = XmlExtensions.CreateSingleLineXmlElementSyntax("PackageVersion", new SyntaxList<SyntaxNode>(leadingTrivia))
|
196
|
+
.WithAttribute("Include", dependencyName)
|
197
|
+
.WithAttribute("Version", newDependencyVersion);
|
198
|
+
updatedItemGroup = lastItemGroup.AddChild(packageVersionElement);
|
199
|
+
}
|
200
|
+
else
|
201
|
+
{
|
202
|
+
IXmlElementSyntax updatedPackageVersionElement;
|
203
|
+
var versionAttribute = existingPackageVersionElement.Attributes.FirstOrDefault(a => a.Name.Equals("Version", StringComparison.OrdinalIgnoreCase));
|
204
|
+
if (versionAttribute is null)
|
205
|
+
{
|
206
|
+
// need to add the version
|
207
|
+
logger.Log(" Adding version attribute to element.");
|
208
|
+
updatedPackageVersionElement = existingPackageVersionElement.WithAttribute("Version", newDependencyVersion);
|
209
|
+
}
|
210
|
+
else if (!versionAttribute.Value.Equals(newDependencyVersion, StringComparison.OrdinalIgnoreCase))
|
211
|
+
{
|
212
|
+
// need to update the version
|
213
|
+
logger.Log($" Updating version attribute of [{versionAttribute.Value}].");
|
214
|
+
var updatedVersionAttribute = versionAttribute.WithValue(newDependencyVersion);
|
215
|
+
updatedPackageVersionElement = existingPackageVersionElement.ReplaceAttribute(versionAttribute, updatedVersionAttribute);
|
216
|
+
}
|
217
|
+
else
|
218
|
+
{
|
219
|
+
logger.Log(" Existing PackageVersion element version was already correct.");
|
220
|
+
return;
|
221
|
+
}
|
222
|
+
|
223
|
+
updatedItemGroup = lastItemGroup.ReplaceChildElement(existingPackageVersionElement, updatedPackageVersionElement);
|
224
|
+
}
|
225
|
+
|
226
|
+
var updatedXml = directoryPackages.Contents.ReplaceNode(lastItemGroup.AsNode, updatedItemGroup.AsNode);
|
227
|
+
directoryPackages.Update(updatedXml);
|
228
|
+
}
|
229
|
+
|
230
|
+
private static async Task AddTransitiveDependencyAsync(string projectPath, string dependencyName, string newDependencyVersion, Logger logger)
|
231
|
+
{
|
232
|
+
logger.Log($" Adding [{dependencyName}/{newDependencyVersion}] as a top-level package reference.");
|
233
|
+
|
234
|
+
// see https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli
|
235
|
+
var (exitCode, _, _) = await ProcessEx.RunAsync("dotnet", $"add {projectPath} package {dependencyName} --version {newDependencyVersion}");
|
236
|
+
if (exitCode != 0)
|
237
|
+
{
|
238
|
+
logger.Log($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.");
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
private static async Task UpdateTopLevelDepdendencyAsync(ImmutableArray<ProjectBuildFile> buildFiles, string dependencyName, string previousDependencyVersion, string newDependencyVersion, Dictionary<string, string> packagesAndVersions, Logger logger)
|
243
|
+
{
|
244
|
+
var result = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
|
245
|
+
if (result == UpdateResult.NotFound)
|
246
|
+
{
|
247
|
+
logger.Log($" Root package [{dependencyName}/{previousDependencyVersion}] was not updated; skipping dependencies.");
|
248
|
+
return;
|
249
|
+
}
|
250
|
+
|
251
|
+
foreach (var (packageName, packageVersion) in packagesAndVersions.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0))
|
252
|
+
{
|
253
|
+
TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
private static UpdateResult TryUpdateDependencyVersion(ImmutableArray<ProjectBuildFile> buildFiles, string dependencyName, string? previousDependencyVersion, string newDependencyVersion, Logger logger)
|
258
|
+
{
|
259
|
+
var foundCorrect = false;
|
260
|
+
var foundUnsupported = false;
|
261
|
+
var updateWasPerformed = false;
|
262
|
+
var propertyNames = new List<string>();
|
263
|
+
|
264
|
+
// First we locate all the PackageReference, GlobalPackageReference, or PackageVersion which set the Version
|
265
|
+
// or VersionOverride attribute. In the simplest case we can update the version attribute directly then move
|
266
|
+
// on. When property substitution is used we have to additionally search for the property containing the version.
|
267
|
+
|
268
|
+
foreach (var buildFile in buildFiles)
|
269
|
+
{
|
270
|
+
var updateNodes = new List<XmlNodeSyntax>();
|
271
|
+
var packageNodes = FindPackageNodes(buildFile, dependencyName);
|
272
|
+
|
273
|
+
var previousPackageVersion = previousDependencyVersion;
|
274
|
+
|
275
|
+
foreach (var packageNode in packageNodes)
|
276
|
+
{
|
277
|
+
var versionAttribute = packageNode.GetAttribute("Version", StringComparison.OrdinalIgnoreCase)
|
278
|
+
?? packageNode.GetAttribute("VersionOverride", StringComparison.OrdinalIgnoreCase);
|
279
|
+
var versionElement = packageNode.Elements.FirstOrDefault(e => e.Name.Equals("Version", StringComparison.OrdinalIgnoreCase))
|
280
|
+
?? packageNode.Elements.FirstOrDefault(e => e.Name.Equals("VersionOverride", StringComparison.OrdinalIgnoreCase));
|
281
|
+
if (versionAttribute is not null)
|
282
|
+
{
|
283
|
+
// Is this the case where version is specified with property substitution?
|
284
|
+
if (MSBuildHelper.TryGetPropertyName(versionAttribute.Value, out var propertyName))
|
285
|
+
{
|
286
|
+
propertyNames.Add(propertyName);
|
287
|
+
}
|
288
|
+
// Is this the case that the version is specified directly in the package node?
|
289
|
+
else
|
290
|
+
{
|
291
|
+
var currentVersion = versionAttribute.Value.TrimStart('[', '(').TrimEnd(']', ')');
|
292
|
+
if (currentVersion.Contains(',') || currentVersion.Contains('*'))
|
293
|
+
{
|
294
|
+
logger.Log($" Found unsupported [{packageNode.Name}] version attribute value [{versionAttribute.Value}] in [{buildFile.RepoRelativePath}].");
|
295
|
+
foundUnsupported = true;
|
296
|
+
}
|
297
|
+
else if (string.Equals(currentVersion, previousDependencyVersion, StringComparison.Ordinal))
|
298
|
+
{
|
299
|
+
logger.Log($" Found incorrect [{packageNode.Name}] version attribute in [{buildFile.RepoRelativePath}].");
|
300
|
+
updateNodes.Add(versionAttribute);
|
301
|
+
}
|
302
|
+
else if (previousDependencyVersion == null && NuGetVersion.TryParse(currentVersion, out var previousVersion))
|
303
|
+
{
|
304
|
+
var newVersion = NuGetVersion.Parse(newDependencyVersion);
|
305
|
+
if (previousVersion < newVersion)
|
306
|
+
{
|
307
|
+
previousPackageVersion = currentVersion;
|
308
|
+
|
309
|
+
logger.Log($" Found incorrect peer [{packageNode.Name}] version attribute in [{buildFile.RepoRelativePath}].");
|
310
|
+
updateNodes.Add(versionAttribute);
|
311
|
+
}
|
312
|
+
}
|
313
|
+
else if (string.Equals(currentVersion, newDependencyVersion, StringComparison.Ordinal))
|
314
|
+
{
|
315
|
+
logger.Log($" Found correct [{packageNode.Name}] version attribute in [{buildFile.RepoRelativePath}].");
|
316
|
+
foundCorrect = true;
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|
320
|
+
else if (versionElement is not null)
|
321
|
+
{
|
322
|
+
var versionValue = versionElement.GetContentValue();
|
323
|
+
if (MSBuildHelper.TryGetPropertyName(versionValue, out var propertyName))
|
324
|
+
{
|
325
|
+
propertyNames.Add(propertyName);
|
326
|
+
}
|
327
|
+
else
|
328
|
+
{
|
329
|
+
var currentVersion = versionValue.TrimStart('[', '(').TrimEnd(']', ')');
|
330
|
+
if (currentVersion.Contains(',') || currentVersion.Contains('*'))
|
331
|
+
{
|
332
|
+
logger.Log($" Found unsupported [{packageNode.Name}] version node value [{versionValue}] in [{buildFile.RepoRelativePath}].");
|
333
|
+
foundUnsupported = true;
|
334
|
+
}
|
335
|
+
else if (currentVersion == previousDependencyVersion)
|
336
|
+
{
|
337
|
+
logger.Log($" Found incorrect [{packageNode.Name}] version node in [{buildFile.RepoRelativePath}].");
|
338
|
+
if (versionElement is XmlElementSyntax elementSyntax)
|
339
|
+
{
|
340
|
+
updateNodes.Add(elementSyntax);
|
341
|
+
}
|
342
|
+
else
|
343
|
+
{
|
344
|
+
throw new InvalidDataException("A concrete type was required for updateNodes. This should not happen.");
|
345
|
+
}
|
346
|
+
}
|
347
|
+
else if (previousDependencyVersion == null && NuGetVersion.TryParse(currentVersion, out var previousVersion))
|
348
|
+
{
|
349
|
+
var newVersion = NuGetVersion.Parse(newDependencyVersion);
|
350
|
+
if (previousVersion < newVersion)
|
351
|
+
{
|
352
|
+
previousPackageVersion = currentVersion;
|
353
|
+
|
354
|
+
logger.Log($" Found incorrect peer [{packageNode.Name}] version node in [{buildFile.RepoRelativePath}].");
|
355
|
+
if (versionElement is XmlElementSyntax elementSyntax)
|
356
|
+
{
|
357
|
+
updateNodes.Add(elementSyntax);
|
358
|
+
}
|
359
|
+
else
|
360
|
+
{
|
361
|
+
// This only exists for completeness in case we ever add a new type of node we don't want to silently ignore them.
|
362
|
+
throw new InvalidDataException("A concrete type was required for updateNodes. This should not happen.");
|
363
|
+
}
|
364
|
+
}
|
365
|
+
}
|
366
|
+
else if (currentVersion == newDependencyVersion)
|
367
|
+
{
|
368
|
+
logger.Log($" Found correct [{packageNode.Name}] version node in [{buildFile.RepoRelativePath}].");
|
369
|
+
foundCorrect = true;
|
370
|
+
}
|
371
|
+
}
|
372
|
+
}
|
373
|
+
else
|
374
|
+
{
|
375
|
+
// We weren't able to find the version node. Central package management?
|
376
|
+
logger.Log($" Found package reference but was unable to locate version information.");
|
377
|
+
continue;
|
378
|
+
}
|
379
|
+
}
|
380
|
+
|
381
|
+
if (updateNodes.Count > 0)
|
382
|
+
{
|
383
|
+
var updatedXml = buildFile.Contents
|
384
|
+
.ReplaceNodes(updateNodes, (o, n) =>
|
385
|
+
{
|
386
|
+
if (n is XmlAttributeSyntax attributeSyntax)
|
387
|
+
{
|
388
|
+
return attributeSyntax.WithValue(attributeSyntax.Value.Replace(previousPackageVersion!, newDependencyVersion));
|
389
|
+
}
|
390
|
+
else if (n is XmlElementSyntax elementsSyntax)
|
391
|
+
{
|
392
|
+
var modifiedContent = elementsSyntax.GetContentValue().Replace(previousPackageVersion!, newDependencyVersion);
|
393
|
+
|
394
|
+
var textSyntax = SyntaxFactory.XmlText(SyntaxFactory.Token(null, SyntaxKind.XmlTextLiteralToken, null, modifiedContent));
|
395
|
+
return elementsSyntax.WithContent(SyntaxFactory.SingletonList(textSyntax));
|
396
|
+
}
|
397
|
+
else
|
398
|
+
{
|
399
|
+
throw new InvalidDataException($"Unsupported SyntaxType {n.GetType().Name} marked for update");
|
400
|
+
}
|
401
|
+
});
|
402
|
+
buildFile.Update(updatedXml);
|
403
|
+
updateWasPerformed = true;
|
404
|
+
}
|
405
|
+
}
|
406
|
+
|
407
|
+
// If property substitution was used to set the Version, we must search for the property containing
|
408
|
+
// the version string. Since it could also be populated by property substitution this search repeats
|
409
|
+
// with the each new property name until the version string is located.
|
410
|
+
|
411
|
+
var processedPropertyNames = new HashSet<string>();
|
412
|
+
|
413
|
+
for (int propertyNameIndex = 0; propertyNameIndex < propertyNames.Count; propertyNameIndex++)
|
414
|
+
{
|
415
|
+
var propertyName = propertyNames[propertyNameIndex];
|
416
|
+
if (processedPropertyNames.Contains(propertyName))
|
417
|
+
{
|
418
|
+
continue;
|
419
|
+
}
|
420
|
+
|
421
|
+
processedPropertyNames.Add(propertyName);
|
422
|
+
|
423
|
+
foreach (var buildFile in buildFiles)
|
424
|
+
{
|
425
|
+
var updateProperties = new List<XmlElementSyntax>();
|
426
|
+
var propertyElements = buildFile.PropertyNodes
|
427
|
+
.Where(e => e.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
|
428
|
+
|
429
|
+
var previousPackageVersion = previousDependencyVersion;
|
430
|
+
|
431
|
+
foreach (var propertyElement in propertyElements)
|
432
|
+
{
|
433
|
+
var propertyContents = propertyElement.GetContentValue();
|
434
|
+
|
435
|
+
// Is this the case where this property contains another property substitution?
|
436
|
+
if (MSBuildHelper.TryGetPropertyName(propertyContents, out var propName))
|
437
|
+
{
|
438
|
+
propertyNames.Add(propName);
|
439
|
+
}
|
440
|
+
// Is this the case that the property contains the version?
|
441
|
+
else
|
442
|
+
{
|
443
|
+
var currentVersion = propertyContents.TrimStart('[', '(').TrimEnd(']', ')');
|
444
|
+
if (currentVersion.Contains(',') || currentVersion.Contains('*'))
|
445
|
+
{
|
446
|
+
logger.Log($" Found unsupported version property [{propertyElement.Name}] value [{propertyContents}] in [{buildFile.RepoRelativePath}].");
|
447
|
+
foundUnsupported = true;
|
448
|
+
}
|
449
|
+
else if (currentVersion == previousDependencyVersion)
|
450
|
+
{
|
451
|
+
logger.Log($" Found incorrect version property [{propertyElement.Name}] in [{buildFile.RepoRelativePath}].");
|
452
|
+
updateProperties.Add((XmlElementSyntax)propertyElement.AsNode);
|
453
|
+
}
|
454
|
+
else if (previousDependencyVersion is null && NuGetVersion.TryParse(currentVersion, out var previousVersion))
|
455
|
+
{
|
456
|
+
var newVersion = NuGetVersion.Parse(newDependencyVersion);
|
457
|
+
if (previousVersion < newVersion)
|
458
|
+
{
|
459
|
+
previousPackageVersion = currentVersion;
|
460
|
+
|
461
|
+
logger.Log($" Found incorrect peer version property [{propertyElement.Name}] in [{buildFile.RepoRelativePath}].");
|
462
|
+
updateProperties.Add((XmlElementSyntax)propertyElement.AsNode);
|
463
|
+
}
|
464
|
+
}
|
465
|
+
else if (currentVersion == newDependencyVersion)
|
466
|
+
{
|
467
|
+
logger.Log($" Found correct version property [{propertyElement.Name}] in [{buildFile.RepoRelativePath}].");
|
468
|
+
foundCorrect = true;
|
469
|
+
}
|
470
|
+
}
|
471
|
+
}
|
472
|
+
|
473
|
+
if (updateProperties.Count > 0)
|
474
|
+
{
|
475
|
+
var updatedXml = buildFile.Contents
|
476
|
+
.ReplaceNodes(updateProperties, (o, n) => n.WithContent(o.GetContentValue().Replace(previousPackageVersion!, newDependencyVersion)).AsNode);
|
477
|
+
buildFile.Update(updatedXml);
|
478
|
+
updateWasPerformed = true;
|
479
|
+
}
|
480
|
+
}
|
481
|
+
}
|
482
|
+
|
483
|
+
return updateWasPerformed
|
484
|
+
? UpdateResult.Updated
|
485
|
+
: foundCorrect
|
486
|
+
? UpdateResult.Correct
|
487
|
+
: foundUnsupported
|
488
|
+
? UpdateResult.NotSupported
|
489
|
+
: UpdateResult.NotFound;
|
490
|
+
}
|
491
|
+
|
492
|
+
private static IEnumerable<IXmlElementSyntax> FindPackageNodes(ProjectBuildFile buildFile, string packageName)
|
493
|
+
{
|
494
|
+
return buildFile.PackageItemNodes.Where(e =>
|
495
|
+
string.Equals(e.GetAttributeOrSubElementValue("Include", StringComparison.OrdinalIgnoreCase) ?? e.GetAttributeOrSubElementValue("Update", StringComparison.OrdinalIgnoreCase), packageName, StringComparison.OrdinalIgnoreCase) &&
|
496
|
+
(e.GetAttributeOrSubElementValue("Version", StringComparison.OrdinalIgnoreCase) ?? e.GetAttributeOrSubElementValue("VersionOverride", StringComparison.OrdinalIgnoreCase)) is not null);
|
497
|
+
}
|
498
|
+
}
|
@@ -0,0 +1,105 @@
|
|
1
|
+
using System;
|
2
|
+
using System.Collections.Generic;
|
3
|
+
using System.IO;
|
4
|
+
using System.Threading.Tasks;
|
5
|
+
|
6
|
+
namespace NuGetUpdater.Core;
|
7
|
+
|
8
|
+
public partial class UpdaterWorker
|
9
|
+
{
|
10
|
+
private readonly Logger _logger;
|
11
|
+
private readonly HashSet<string> _processedGlobalJsonPaths = new(StringComparer.OrdinalIgnoreCase);
|
12
|
+
|
13
|
+
public UpdaterWorker(Logger logger)
|
14
|
+
{
|
15
|
+
_logger = logger;
|
16
|
+
}
|
17
|
+
|
18
|
+
public async Task RunAsync(string repoRootPath, string filePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
19
|
+
{
|
20
|
+
MSBuildHelper.RegisterMSBuild();
|
21
|
+
|
22
|
+
if (!Path.IsPathRooted(filePath) || !File.Exists(filePath))
|
23
|
+
{
|
24
|
+
filePath = Path.GetFullPath(Path.Join(repoRootPath, filePath));
|
25
|
+
}
|
26
|
+
|
27
|
+
if (!isTransitive)
|
28
|
+
{
|
29
|
+
await DotNetToolsJsonUpdater.UpdateDependencyAsync(repoRootPath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
|
30
|
+
}
|
31
|
+
|
32
|
+
var extension = Path.GetExtension(filePath).ToLowerInvariant();
|
33
|
+
switch (extension)
|
34
|
+
{
|
35
|
+
case ".sln":
|
36
|
+
await RunForSolutionAsync(repoRootPath, filePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
37
|
+
break;
|
38
|
+
case ".proj":
|
39
|
+
await RunForProjFileAsync(repoRootPath, filePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
40
|
+
break;
|
41
|
+
case ".csproj":
|
42
|
+
case ".fsproj":
|
43
|
+
case ".vbproj":
|
44
|
+
await RunForProjectAsync(repoRootPath, filePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
45
|
+
break;
|
46
|
+
default:
|
47
|
+
_logger.Log($"File extension [{extension}] is not supported.");
|
48
|
+
break;
|
49
|
+
}
|
50
|
+
|
51
|
+
_processedGlobalJsonPaths.Clear();
|
52
|
+
}
|
53
|
+
|
54
|
+
private async Task RunForSolutionAsync(string repoRootPath, string solutionPath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
55
|
+
{
|
56
|
+
_logger.Log($"Running for solution [{Path.GetRelativePath(repoRootPath, solutionPath)}]");
|
57
|
+
var projectPaths = MSBuildHelper.GetProjectPathsFromSolution(solutionPath);
|
58
|
+
foreach (var projectPath in projectPaths)
|
59
|
+
{
|
60
|
+
await RunForProjectAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
private async Task RunForProjFileAsync(string repoRootPath, string projFilePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
65
|
+
{
|
66
|
+
_logger.Log($"Running for proj file [{Path.GetRelativePath(repoRootPath, projFilePath)}]");
|
67
|
+
if (!File.Exists(projFilePath))
|
68
|
+
{
|
69
|
+
_logger.Log($"File [{projFilePath}] does not exist.");
|
70
|
+
return;
|
71
|
+
}
|
72
|
+
var projectFilePaths = MSBuildHelper.GetProjectPathsFromProject(projFilePath);
|
73
|
+
foreach (var projectFullPath in projectFilePaths)
|
74
|
+
{
|
75
|
+
// If there is some MSBuild logic that needs to run to fully resolve the path skip the project
|
76
|
+
if (File.Exists(projectFullPath))
|
77
|
+
{
|
78
|
+
await RunForProjectAsync(repoRootPath, projectFullPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
private async Task RunForProjectAsync(string repoRootPath, string projectPath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
84
|
+
{
|
85
|
+
_logger.Log($"Running for project [{projectPath}]");
|
86
|
+
|
87
|
+
if (!isTransitive
|
88
|
+
&& MSBuildHelper.GetGlobalJsonPath(repoRootPath, projectPath) is string globalJsonPath
|
89
|
+
&& !_processedGlobalJsonPaths.Contains(globalJsonPath))
|
90
|
+
{
|
91
|
+
_processedGlobalJsonPaths.Add(globalJsonPath);
|
92
|
+
await GlobalJsonUpdater.UpdateDependencyAsync(repoRootPath, globalJsonPath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
|
93
|
+
}
|
94
|
+
|
95
|
+
if (NuGetHelper.HasProjectConfigFile(projectPath))
|
96
|
+
{
|
97
|
+
await PackagesConfigUpdater.UpdateDependencyAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, _logger);
|
98
|
+
}
|
99
|
+
|
100
|
+
// Some repos use a mix of packages.config and PackageReference
|
101
|
+
await SdkPackageUpdater.UpdateDependencyAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, _logger);
|
102
|
+
|
103
|
+
_logger.Log("Update complete.");
|
104
|
+
}
|
105
|
+
}
|