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,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
|
+
}
|