dependabot-nuget 0.302.0 → 0.303.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/lib/NuGetUpdater/Directory.Packages.props +5 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscoveryTargetingPacks.props +10 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +96 -97
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +2 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +8 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +7 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +2 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +257 -37
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +12 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationBase.cs +209 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationResult.cs +3 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +79 -24
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +11 -11
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +19 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +24 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +177 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateOperationBaseTests.cs +130 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +5 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +71 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +87 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +23 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +145 -147
- metadata +11 -7
@@ -1,9 +1,12 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
+
using System.Text.Json;
|
2
3
|
|
3
4
|
using Microsoft.Language.Xml;
|
4
5
|
|
6
|
+
using NuGet.Frameworks;
|
5
7
|
using NuGet.Versioning;
|
6
8
|
|
9
|
+
using NuGetUpdater.Core.Updater;
|
7
10
|
using NuGetUpdater.Core.Utilities;
|
8
11
|
|
9
12
|
namespace NuGetUpdater.Core;
|
@@ -21,7 +24,7 @@ namespace NuGetUpdater.Core;
|
|
21
24
|
/// </remarks>
|
22
25
|
internal static class PackageReferenceUpdater
|
23
26
|
{
|
24
|
-
public static async Task UpdateDependencyAsync(
|
27
|
+
public static async Task<IEnumerable<UpdateOperationBase>> UpdateDependencyAsync(
|
25
28
|
string repoRootPath,
|
26
29
|
string projectPath,
|
27
30
|
string dependencyName,
|
@@ -60,45 +63,67 @@ internal static class PackageReferenceUpdater
|
|
60
63
|
|
61
64
|
if (!await DoesDependencyRequireUpdateAsync(repoRootPath, projectPath, tfms, topLevelDependencies, dependencyName, newDependencyVersion, experimentsManager, logger))
|
62
65
|
{
|
63
|
-
return;
|
66
|
+
return [];
|
64
67
|
}
|
65
68
|
|
69
|
+
var updateOperations = new List<UpdateOperationBase>();
|
66
70
|
var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, experimentsManager, logger);
|
67
71
|
if (experimentsManager.UseLegacyDependencySolver)
|
68
72
|
{
|
69
73
|
if (isTransitive)
|
70
74
|
{
|
71
|
-
await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
|
75
|
+
var updatedFiles = await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
|
76
|
+
updateOperations.Add(new PinnedUpdate()
|
77
|
+
{
|
78
|
+
DependencyName = dependencyName,
|
79
|
+
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
80
|
+
UpdatedFiles = [.. updatedFiles],
|
81
|
+
});
|
72
82
|
}
|
73
83
|
else
|
74
84
|
{
|
75
85
|
if (peerDependencies is null)
|
76
86
|
{
|
77
|
-
return;
|
87
|
+
return updateOperations;
|
78
88
|
}
|
79
89
|
|
80
|
-
await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, experimentsManager, logger);
|
90
|
+
var topLevelUpdateOperations = await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, experimentsManager, logger);
|
91
|
+
updateOperations.AddRange(topLevelUpdateOperations);
|
81
92
|
}
|
82
93
|
}
|
83
94
|
else
|
84
95
|
{
|
85
96
|
if (peerDependencies is null)
|
86
97
|
{
|
87
|
-
return;
|
98
|
+
return updateOperations;
|
88
99
|
}
|
89
100
|
|
90
|
-
await UpdateDependencyWithConflictResolution(
|
101
|
+
var conflictResolutionUpdateOperations = await UpdateDependencyWithConflictResolution(
|
102
|
+
repoRootPath,
|
103
|
+
buildFiles,
|
104
|
+
tfms,
|
105
|
+
projectPath,
|
106
|
+
dependencyName,
|
107
|
+
previousDependencyVersion,
|
108
|
+
newDependencyVersion,
|
109
|
+
isTransitive,
|
110
|
+
peerDependencies,
|
111
|
+
experimentsManager,
|
112
|
+
logger);
|
113
|
+
updateOperations.AddRange(conflictResolutionUpdateOperations);
|
91
114
|
}
|
92
115
|
|
93
116
|
if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, buildFiles, tfms, experimentsManager, logger))
|
94
117
|
{
|
95
|
-
return
|
118
|
+
// should we return an empty set because we failed?
|
119
|
+
return updateOperations;
|
96
120
|
}
|
97
121
|
|
98
122
|
await SaveBuildFilesAsync(buildFiles, logger);
|
123
|
+
return updateOperations;
|
99
124
|
}
|
100
125
|
|
101
|
-
public static async Task UpdateDependencyWithConflictResolution(
|
126
|
+
public static async Task<IEnumerable<UpdateOperationBase>> UpdateDependencyWithConflictResolution(
|
102
127
|
string repoRootPath,
|
103
128
|
ImmutableArray<ProjectBuildFile> buildFiles,
|
104
129
|
string[] targetFrameworks,
|
@@ -111,17 +136,20 @@ internal static class PackageReferenceUpdater
|
|
111
136
|
ExperimentsManager experimentsManager,
|
112
137
|
ILogger logger)
|
113
138
|
{
|
114
|
-
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).
|
139
|
+
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToImmutableArray();
|
115
140
|
var isDependencyTopLevel = topLevelDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
|
116
|
-
var dependenciesToUpdate = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference) };
|
141
|
+
var dependenciesToUpdate = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference) }.ToImmutableArray();
|
142
|
+
var updateOperations = new List<UpdateOperationBase>();
|
117
143
|
|
118
144
|
// update the initial dependency...
|
119
|
-
TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
|
145
|
+
var (_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
|
146
|
+
updateOperations.AddRange(updateOperationsPerformed);
|
120
147
|
|
121
148
|
// ...and the peer dependencies...
|
122
149
|
foreach (var (packageName, packageVersion) in peerDependencies.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0))
|
123
150
|
{
|
124
|
-
TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
151
|
+
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
152
|
+
updateOperations.AddRange(updateOperationsPerformed);
|
125
153
|
}
|
126
154
|
|
127
155
|
// ...and everything else
|
@@ -136,21 +164,164 @@ internal static class PackageReferenceUpdater
|
|
136
164
|
continue;
|
137
165
|
}
|
138
166
|
|
139
|
-
var isDependencyInResolutionSet = resolvedDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
|
167
|
+
var isDependencyInResolutionSet = resolvedDependencies.Value.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
|
140
168
|
if (isTransitive && !isDependencyTopLevel && isDependencyInResolutionSet)
|
141
169
|
{
|
142
170
|
// a transitive dependency had to be pinned; add it here
|
143
|
-
await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
|
171
|
+
var updatedFiles = await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
|
144
172
|
}
|
145
173
|
|
146
174
|
// update all resolved dependencies that aren't the initial dependency
|
147
|
-
foreach (var resolvedDependency in resolvedDependencies
|
175
|
+
foreach (var resolvedDependency in resolvedDependencies.Value
|
148
176
|
.Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
149
177
|
.Where(d => d.Version is not null))
|
150
178
|
{
|
151
|
-
TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
|
179
|
+
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
|
180
|
+
updateOperations.AddRange(updateOperationsPerformed);
|
181
|
+
}
|
182
|
+
|
183
|
+
updateOperationsPerformed = await ComputeUpdateOperations(repoRootPath, projectPath, tfm, topLevelDependencies, dependenciesToUpdate, resolvedDependencies.Value, experimentsManager, logger);
|
184
|
+
updateOperations.AddRange(updateOperationsPerformed.Select(u => u with { UpdatedFiles = [projectFile.Path] }));
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
return updateOperations;
|
189
|
+
}
|
190
|
+
|
191
|
+
internal static async Task<IEnumerable<UpdateOperationBase>> ComputeUpdateOperations(
|
192
|
+
string repoRoot,
|
193
|
+
string projectPath,
|
194
|
+
string targetFramework,
|
195
|
+
ImmutableArray<Dependency> topLevelDependencies,
|
196
|
+
ImmutableArray<Dependency> requestedUpdates,
|
197
|
+
ImmutableArray<Dependency> resolvedDependencies,
|
198
|
+
ExperimentsManager experimentsManager,
|
199
|
+
ILogger logger
|
200
|
+
)
|
201
|
+
{
|
202
|
+
var topLevelNames = topLevelDependencies.Select(d => d.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
203
|
+
var requestedVersions = requestedUpdates.ToDictionary(d => d.Name, d => NuGetVersion.Parse(d.Version!), StringComparer.OrdinalIgnoreCase);
|
204
|
+
var resolvedVersions = resolvedDependencies
|
205
|
+
.Select(d => (d.Name, NuGetVersion.TryParse(d.Version, out var version), version))
|
206
|
+
.Where(d => d.Item2)
|
207
|
+
.ToDictionary(d => d.Item1, d => d.Item3!, StringComparer.OrdinalIgnoreCase);
|
208
|
+
|
209
|
+
var (packageParents, packageVersions) = await GetPackageGraphForDependencies(repoRoot, projectPath, targetFramework, resolvedDependencies, experimentsManager, logger);
|
210
|
+
var updateOperations = new List<UpdateOperationBase>();
|
211
|
+
foreach (var (requestedDependencyName, requestedDependencyVersion) in requestedVersions)
|
212
|
+
{
|
213
|
+
var isDependencyTopLevel = topLevelNames.Contains(requestedDependencyName);
|
214
|
+
var isDependencyInResolvedSet = resolvedVersions.ContainsKey(requestedDependencyName);
|
215
|
+
switch ((isDependencyTopLevel, isDependencyInResolvedSet))
|
216
|
+
{
|
217
|
+
case (true, true):
|
218
|
+
// direct update performed
|
219
|
+
var resolvedVer = resolvedVersions[requestedDependencyName];
|
220
|
+
updateOperations.Add(new DirectUpdate()
|
221
|
+
{
|
222
|
+
DependencyName = requestedDependencyName,
|
223
|
+
NewVersion = resolvedVer,
|
224
|
+
UpdatedFiles = [],
|
225
|
+
});
|
226
|
+
break;
|
227
|
+
case (false, true):
|
228
|
+
// pinned transitive update
|
229
|
+
updateOperations.Add(new PinnedUpdate()
|
230
|
+
{
|
231
|
+
DependencyName = requestedDependencyName,
|
232
|
+
NewVersion = resolvedVersions[requestedDependencyName],
|
233
|
+
UpdatedFiles = [],
|
234
|
+
});
|
235
|
+
break;
|
236
|
+
case (false, false):
|
237
|
+
// walk the first parent all the way up to find a top-level dependency that resulted in the desired change
|
238
|
+
string? rootPackageName = null;
|
239
|
+
var currentPackageName = requestedDependencyName;
|
240
|
+
while (packageParents.TryGetValue(currentPackageName, out var parentSet))
|
241
|
+
{
|
242
|
+
currentPackageName = parentSet.First();
|
243
|
+
if (topLevelNames.Contains(currentPackageName))
|
244
|
+
{
|
245
|
+
rootPackageName = currentPackageName;
|
246
|
+
break;
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
if (rootPackageName is not null)
|
251
|
+
{
|
252
|
+
updateOperations.Add(new ParentUpdate()
|
253
|
+
{
|
254
|
+
DependencyName = requestedDependencyName,
|
255
|
+
NewVersion = requestedVersions[requestedDependencyName],
|
256
|
+
UpdatedFiles = [],
|
257
|
+
ParentDependencyName = rootPackageName,
|
258
|
+
ParentNewVersion = packageVersions[rootPackageName],
|
259
|
+
});
|
260
|
+
}
|
261
|
+
break;
|
262
|
+
case (true, false):
|
263
|
+
// dependency is top-level, but not in the resolved versions; this can happen if an unrelated package has a wildcard
|
264
|
+
break;
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
return [.. updateOperations];
|
269
|
+
}
|
270
|
+
|
271
|
+
private static async Task<(Dictionary<string, HashSet<string>> PackageParents, Dictionary<string, NuGetVersion> PackageVersions)> GetPackageGraphForDependencies(string repoRoot, string projectPath, string targetFramework, ImmutableArray<Dependency> topLevelDependencies, ExperimentsManager experimentsManager, ILogger logger)
|
272
|
+
{
|
273
|
+
var packageParents = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
|
274
|
+
var packageVersions = new Dictionary<string, NuGetVersion>(StringComparer.OrdinalIgnoreCase);
|
275
|
+
var tempDir = Directory.CreateTempSubdirectory("_package_graph_for_dependencies_");
|
276
|
+
try
|
277
|
+
{
|
278
|
+
// generate project.assets.json
|
279
|
+
var parsedTargetFramework = NuGetFramework.Parse(targetFramework);
|
280
|
+
var tempProject = await MSBuildHelper.CreateTempProjectAsync(tempDir, repoRoot, projectPath, targetFramework, topLevelDependencies, experimentsManager, logger, importDependencyTargets: !experimentsManager.UseDirectDiscovery);
|
281
|
+
var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["build", tempProject, "/t:_ReportDependencies"], tempDir.FullName, experimentsManager);
|
282
|
+
var assetsJsonPath = Path.Join(tempDir.FullName, "obj", "project.assets.json");
|
283
|
+
var assetsJsonContent = await File.ReadAllTextAsync(assetsJsonPath);
|
284
|
+
|
285
|
+
// build reverse dependency graph
|
286
|
+
var assets = JsonDocument.Parse(assetsJsonContent).RootElement;
|
287
|
+
foreach (var tfmObject in assets.GetProperty("targets").EnumerateObject())
|
288
|
+
{
|
289
|
+
var reportedTargetFramework = NuGetFramework.Parse(tfmObject.Name);
|
290
|
+
if (reportedTargetFramework != parsedTargetFramework)
|
291
|
+
{
|
292
|
+
// not interested in this target framework
|
293
|
+
continue;
|
294
|
+
}
|
295
|
+
|
296
|
+
foreach (var parentObject in tfmObject.Value.EnumerateObject())
|
297
|
+
{
|
298
|
+
var parts = parentObject.Name.Split('/');
|
299
|
+
var parentName = parts[0];
|
300
|
+
var parentVersion = parts[1];
|
301
|
+
packageVersions[parentName] = NuGetVersion.Parse(parentVersion);
|
302
|
+
|
303
|
+
if (parentObject.Value.TryGetProperty("dependencies", out var dependencies))
|
304
|
+
{
|
305
|
+
foreach (var childObject in dependencies.EnumerateObject())
|
306
|
+
{
|
307
|
+
var childName = childObject.Name;
|
308
|
+
var parentSet = packageParents.GetOrAdd(childName, () => new(StringComparer.OrdinalIgnoreCase));
|
309
|
+
parentSet.Add(parentName);
|
310
|
+
}
|
311
|
+
}
|
152
312
|
}
|
153
313
|
}
|
314
|
+
|
315
|
+
return (packageParents, packageVersions);
|
316
|
+
}
|
317
|
+
catch (Exception ex)
|
318
|
+
{
|
319
|
+
logger.Error($"Error while generating package graph: {ex.Message}");
|
320
|
+
throw;
|
321
|
+
}
|
322
|
+
finally
|
323
|
+
{
|
324
|
+
tempDir.Delete(recursive: true);
|
154
325
|
}
|
155
326
|
}
|
156
327
|
|
@@ -227,7 +398,8 @@ internal static class PackageReferenceUpdater
|
|
227
398
|
return true;
|
228
399
|
}
|
229
400
|
|
230
|
-
|
401
|
+
/// <returns>The updated files.</returns>
|
402
|
+
private static async Task<IEnumerable<string>> UpdateTransitiveDependencyAsync(
|
231
403
|
string repoRootPath,
|
232
404
|
string projectPath,
|
233
405
|
string dependencyName,
|
@@ -237,16 +409,19 @@ internal static class PackageReferenceUpdater
|
|
237
409
|
ILogger logger
|
238
410
|
)
|
239
411
|
{
|
412
|
+
IEnumerable<string> updatedFiles;
|
240
413
|
var directoryPackagesWithPinning = buildFiles.OfType<ProjectBuildFile>()
|
241
414
|
.FirstOrDefault(bf => IsCpmTransitivePinningEnabled(bf));
|
242
415
|
if (directoryPackagesWithPinning is not null)
|
243
416
|
{
|
244
|
-
PinTransitiveDependency(directoryPackagesWithPinning, dependencyName, newDependencyVersion, logger);
|
417
|
+
updatedFiles = PinTransitiveDependency(directoryPackagesWithPinning, dependencyName, newDependencyVersion, logger);
|
245
418
|
}
|
246
419
|
else
|
247
420
|
{
|
248
|
-
await AddTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, experimentsManager, logger);
|
421
|
+
updatedFiles = await AddTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, experimentsManager, logger);
|
249
422
|
}
|
423
|
+
|
424
|
+
return updatedFiles;
|
250
425
|
}
|
251
426
|
|
252
427
|
private static bool IsCpmTransitivePinningEnabled(ProjectBuildFile buildFile)
|
@@ -271,7 +446,8 @@ internal static class PackageReferenceUpdater
|
|
271
446
|
return isTransitivePinningEnabled is not null && string.Equals(isTransitivePinningEnabled, "true", StringComparison.OrdinalIgnoreCase);
|
272
447
|
}
|
273
448
|
|
274
|
-
|
449
|
+
/// <returns>The updated files.</returns>
|
450
|
+
private static IEnumerable<string> PinTransitiveDependency(ProjectBuildFile directoryPackages, string dependencyName, string newDependencyVersion, ILogger logger)
|
275
451
|
{
|
276
452
|
var existingPackageVersionElement = directoryPackages.ItemNodes
|
277
453
|
.Where(e => e.Name.Equals("PackageVersion", StringComparison.OrdinalIgnoreCase) &&
|
@@ -288,7 +464,7 @@ internal static class PackageReferenceUpdater
|
|
288
464
|
if (lastPackageVersion is null)
|
289
465
|
{
|
290
466
|
logger.Info($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not pinned.");
|
291
|
-
return;
|
467
|
+
return [];
|
292
468
|
}
|
293
469
|
|
294
470
|
var lastItemGroup = lastPackageVersion.Parent;
|
@@ -324,7 +500,7 @@ internal static class PackageReferenceUpdater
|
|
324
500
|
else
|
325
501
|
{
|
326
502
|
logger.Info(" Existing PackageVersion element version was already correct.");
|
327
|
-
return;
|
503
|
+
return [];
|
328
504
|
}
|
329
505
|
|
330
506
|
updatedItemGroup = lastItemGroup.ReplaceChildElement(existingPackageVersionElement, updatedPackageVersionElement);
|
@@ -332,10 +508,14 @@ internal static class PackageReferenceUpdater
|
|
332
508
|
|
333
509
|
var updatedXml = directoryPackages.Contents.ReplaceNode(lastItemGroup.AsNode, updatedItemGroup.AsNode);
|
334
510
|
directoryPackages.Update(updatedXml);
|
511
|
+
|
512
|
+
return [directoryPackages.Path];
|
335
513
|
}
|
336
514
|
|
337
|
-
|
515
|
+
/// <returns>The updated files.</returns>
|
516
|
+
private static async Task<IEnumerable<string>> AddTransitiveDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string newDependencyVersion, ExperimentsManager experimentsManager, ILogger logger)
|
338
517
|
{
|
518
|
+
var updatedFiles = new[] { projectPath }; // assume this worked unless...
|
339
519
|
var projectDirectory = Path.GetDirectoryName(projectPath)!;
|
340
520
|
await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
|
341
521
|
{
|
@@ -351,10 +531,13 @@ internal static class PackageReferenceUpdater
|
|
351
531
|
if (exitCode != 0)
|
352
532
|
{
|
353
533
|
logger.Warn($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
|
534
|
+
updatedFiles = [];
|
354
535
|
}
|
355
536
|
|
356
537
|
return exitCode;
|
357
538
|
}, logger, retainMSBuildSdks: true);
|
539
|
+
|
540
|
+
return updatedFiles;
|
358
541
|
}
|
359
542
|
|
360
543
|
/// <summary>
|
@@ -371,7 +554,7 @@ internal static class PackageReferenceUpdater
|
|
371
554
|
ILogger logger)
|
372
555
|
{
|
373
556
|
var newDependency = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.Unknown) };
|
374
|
-
var tfmsAndDependencies = new Dictionary<string, Dependency
|
557
|
+
var tfmsAndDependencies = new Dictionary<string, ImmutableArray<Dependency>>();
|
375
558
|
foreach (var tfm in tfms)
|
376
559
|
{
|
377
560
|
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, newDependency, experimentsManager, logger);
|
@@ -415,7 +598,7 @@ internal static class PackageReferenceUpdater
|
|
415
598
|
return packagesAndVersions;
|
416
599
|
}
|
417
600
|
|
418
|
-
private static async Task UpdateTopLevelDepdendency(
|
601
|
+
private static async Task<IEnumerable<UpdateOperationBase>> UpdateTopLevelDepdendency(
|
419
602
|
string repoRootPath,
|
420
603
|
ImmutableArray<ProjectBuildFile> buildFiles,
|
421
604
|
string[] targetFrameworks,
|
@@ -427,25 +610,29 @@ internal static class PackageReferenceUpdater
|
|
427
610
|
ILogger logger)
|
428
611
|
{
|
429
612
|
// update dependencies...
|
430
|
-
var
|
431
|
-
|
613
|
+
var updateOperations = new List<UpdateOperationBase>();
|
614
|
+
var (updateResult, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
|
615
|
+
if (updateResult == UpdateResult.NotFound)
|
432
616
|
{
|
433
617
|
logger.Info($" Root package [{dependencyName}/{previousDependencyVersion}] was not updated; skipping dependencies.");
|
434
|
-
return;
|
618
|
+
return [];
|
435
619
|
}
|
436
620
|
|
621
|
+
updateOperations.AddRange(updateOperationsPerformed);
|
622
|
+
|
437
623
|
foreach (var (packageName, packageVersion) in peerDependencies.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0))
|
438
624
|
{
|
439
|
-
TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
625
|
+
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
626
|
+
updateOperations.AddRange(updateOperationsPerformed);
|
440
627
|
}
|
441
628
|
|
442
629
|
// ...and make them all coherent
|
443
|
-
|
630
|
+
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToImmutableArray();
|
444
631
|
foreach (ProjectBuildFile projectFile in buildFiles)
|
445
632
|
{
|
446
633
|
foreach (string tfm in targetFrameworks)
|
447
634
|
{
|
448
|
-
var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRootPath, projectFile.Path, tfm,
|
635
|
+
var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRootPath, projectFile.Path, tfm, topLevelDependencies, experimentsManager, logger);
|
449
636
|
if (resolvedDependencies is null)
|
450
637
|
{
|
451
638
|
logger.Info($" Unable to resolve dependency conflicts for {projectFile.Path}.");
|
@@ -453,7 +640,7 @@ internal static class PackageReferenceUpdater
|
|
453
640
|
}
|
454
641
|
|
455
642
|
// ensure the originally requested dependency was resolved to the correct version
|
456
|
-
var specificResolvedDependency = resolvedDependencies.Where(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
643
|
+
var specificResolvedDependency = resolvedDependencies.Value.Where(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
457
644
|
if (specificResolvedDependency is null)
|
458
645
|
{
|
459
646
|
logger.Info($" Unable to resolve requested dependency for {dependencyName} in {projectFile.Path}.");
|
@@ -467,17 +654,24 @@ internal static class PackageReferenceUpdater
|
|
467
654
|
}
|
468
655
|
|
469
656
|
// update all versions
|
470
|
-
foreach (Dependency resolvedDependency in resolvedDependencies
|
657
|
+
foreach (Dependency resolvedDependency in resolvedDependencies.Value
|
471
658
|
.Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
472
659
|
.Where(d => d.Version is not null))
|
473
660
|
{
|
474
|
-
TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
|
661
|
+
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
|
662
|
+
updateOperations.AddRange(updateOperationsPerformed);
|
475
663
|
}
|
664
|
+
|
665
|
+
updateOperationsPerformed = await ComputeUpdateOperations(repoRootPath, projectFile.Path, tfm, topLevelDependencies, [new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference)], resolvedDependencies.Value, experimentsManager, logger);
|
666
|
+
updateOperations.AddRange(updateOperationsPerformed.Select(u => u with { UpdatedFiles = [projectFile.Path] }));
|
476
667
|
}
|
477
668
|
}
|
669
|
+
|
670
|
+
return updateOperations;
|
478
671
|
}
|
479
672
|
|
480
|
-
|
673
|
+
/// <returns>The updated files.</returns>
|
674
|
+
private static (UpdateResult, IEnumerable<UpdateOperationBase>) TryUpdateDependencyVersion(
|
481
675
|
ImmutableArray<ProjectBuildFile> buildFiles,
|
482
676
|
string dependencyName,
|
483
677
|
string? previousDependencyVersion,
|
@@ -488,6 +682,7 @@ internal static class PackageReferenceUpdater
|
|
488
682
|
var foundUnsupported = false;
|
489
683
|
var updateWasPerformed = false;
|
490
684
|
var propertyNames = new List<string>();
|
685
|
+
var updateOperations = new List<UpdateOperationBase>();
|
491
686
|
|
492
687
|
// First we locate all the PackageReference, GlobalPackageReference, or PackageVersion which set the Version
|
493
688
|
// or VersionOverride attribute. In the simplest case we can update the version attribute directly then move
|
@@ -526,6 +721,12 @@ internal static class PackageReferenceUpdater
|
|
526
721
|
{
|
527
722
|
logger.Info($" Found incorrect [{packageNode.Name}] version attribute in [{buildFile.RelativePath}].");
|
528
723
|
updateNodes.Add(versionAttribute);
|
724
|
+
updateOperations.Add(new DirectUpdate()
|
725
|
+
{
|
726
|
+
DependencyName = dependencyName,
|
727
|
+
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
728
|
+
UpdatedFiles = [buildFile.Path],
|
729
|
+
});
|
529
730
|
}
|
530
731
|
else if (previousDependencyVersion == null && NuGetVersion.TryParse(currentVersion, out var previousVersion))
|
531
732
|
{
|
@@ -536,6 +737,12 @@ internal static class PackageReferenceUpdater
|
|
536
737
|
|
537
738
|
logger.Info($" Found incorrect peer [{packageNode.Name}] version attribute in [{buildFile.RelativePath}].");
|
538
739
|
updateNodes.Add(versionAttribute);
|
740
|
+
updateOperations.Add(new DirectUpdate()
|
741
|
+
{
|
742
|
+
DependencyName = dependencyName,
|
743
|
+
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
744
|
+
UpdatedFiles = [buildFile.Path],
|
745
|
+
});
|
539
746
|
}
|
540
747
|
}
|
541
748
|
else if (string.Equals(currentVersion, newDependencyVersion, StringComparison.Ordinal))
|
@@ -566,6 +773,12 @@ internal static class PackageReferenceUpdater
|
|
566
773
|
if (versionElement is XmlElementSyntax elementSyntax)
|
567
774
|
{
|
568
775
|
updateNodes.Add(elementSyntax);
|
776
|
+
updateOperations.Add(new DirectUpdate()
|
777
|
+
{
|
778
|
+
DependencyName = dependencyName,
|
779
|
+
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
780
|
+
UpdatedFiles = [buildFile.Path],
|
781
|
+
});
|
569
782
|
}
|
570
783
|
else
|
571
784
|
{
|
@@ -583,6 +796,12 @@ internal static class PackageReferenceUpdater
|
|
583
796
|
if (versionElement is XmlElementSyntax elementSyntax)
|
584
797
|
{
|
585
798
|
updateNodes.Add(elementSyntax);
|
799
|
+
updateOperations.Add(new DirectUpdate()
|
800
|
+
{
|
801
|
+
DependencyName = dependencyName,
|
802
|
+
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
803
|
+
UpdatedFiles = [buildFile.Path],
|
804
|
+
});
|
586
805
|
}
|
587
806
|
else
|
588
807
|
{
|
@@ -706,13 +925,14 @@ internal static class PackageReferenceUpdater
|
|
706
925
|
}
|
707
926
|
}
|
708
927
|
|
709
|
-
|
928
|
+
var updateResult = updateWasPerformed
|
710
929
|
? UpdateResult.Updated
|
711
930
|
: foundCorrect
|
712
931
|
? UpdateResult.Correct
|
713
932
|
: foundUnsupported
|
714
933
|
? UpdateResult.NotSupported
|
715
934
|
: UpdateResult.NotFound;
|
935
|
+
return (updateResult, updateOperations);
|
716
936
|
}
|
717
937
|
|
718
938
|
private static IEnumerable<IXmlElementSyntax> FindPackageNodes(
|
@@ -7,6 +7,7 @@ using System.Xml.XPath;
|
|
7
7
|
using Microsoft.Language.Xml;
|
8
8
|
|
9
9
|
using NuGet.CommandLine;
|
10
|
+
using NuGet.Versioning;
|
10
11
|
|
11
12
|
using NuGetUpdater.Core.Updater;
|
12
13
|
using NuGetUpdater.Core.Utilities;
|
@@ -25,7 +26,7 @@ namespace NuGetUpdater.Core;
|
|
25
26
|
/// <remarks>
|
26
27
|
internal static partial class PackagesConfigUpdater
|
27
28
|
{
|
28
|
-
public static async Task UpdateDependencyAsync(
|
29
|
+
public static async Task<IEnumerable<UpdateOperationBase>> UpdateDependencyAsync(
|
29
30
|
string repoRootPath,
|
30
31
|
string projectPath,
|
31
32
|
string dependencyName,
|
@@ -44,7 +45,7 @@ internal static partial class PackagesConfigUpdater
|
|
44
45
|
if (packagesSubDirectory is null)
|
45
46
|
{
|
46
47
|
logger.Info($" Project [{projectPath}] does not reference this dependency.");
|
47
|
-
return;
|
48
|
+
return [];
|
48
49
|
}
|
49
50
|
|
50
51
|
logger.Info($" Using packages directory [{packagesSubDirectory}] for project [{projectPath}].");
|
@@ -95,10 +96,18 @@ internal static partial class PackagesConfigUpdater
|
|
95
96
|
projectBuildFile.NormalizeDirectorySeparatorsInProject();
|
96
97
|
|
97
98
|
// Update binding redirects
|
98
|
-
await BindingRedirectManager.UpdateBindingRedirectsAsync(projectBuildFile, dependencyName, newDependencyVersion);
|
99
|
+
var updatedConfigFiles = await BindingRedirectManager.UpdateBindingRedirectsAsync(projectBuildFile, dependencyName, newDependencyVersion);
|
99
100
|
|
100
101
|
logger.Info(" Writing project file back to disk");
|
101
102
|
await projectBuildFile.SaveAsync();
|
103
|
+
|
104
|
+
var updateResult = new DirectUpdate()
|
105
|
+
{
|
106
|
+
DependencyName = dependencyName,
|
107
|
+
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
108
|
+
UpdatedFiles = [projectPath, packagesConfigPath, .. updatedConfigFiles],
|
109
|
+
};
|
110
|
+
return [updateResult];
|
102
111
|
}
|
103
112
|
|
104
113
|
private static void RunNugetUpdate(List<string> updateArgs, List<string> restoreArgs, string projectDirectory, ILogger logger)
|