dependabot-nuget 0.321.3 → 0.322.1
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 +22 -22
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/Program.cs +21 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +19 -11
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +19 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +21 -14
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/FrameworkCheckCommand.cs +8 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +29 -16
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +20 -19
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +10 -23
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +9 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +15 -232
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +1 -154
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -12
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/GlobalJsonBuildFile.cs +5 -13
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/PrivateSourceTimedOutException.cs +12 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +4 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceTimedOut.cs +10 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/AzurePackageDetailFinder.cs +30 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGenerator.cs +237 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/GitHubPackageDetailFinder.cs +101 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/GitLabPackageDetailFinder.cs +107 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/HttpFetcher.cs +32 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IHttpFetcher.cs +30 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IPackageDetailFinder.cs +47 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IPullRequestBodyGenerator.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/SimplePullRequestBodyGenerator.cs +15 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +7 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +3 -525
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/FileWriterWorker.cs +85 -35
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/XmlFileWriter.cs +27 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +1 -856
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationBase.cs +18 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +16 -200
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +21 -556
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +9 -73
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolver/MSBuildDependencySolverTests.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +1 -20
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +0 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +3 -62
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +13 -563
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +20 -269
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/GlobalJsonBuildFileTests.cs +0 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +131 -131
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/JobErrorBaseTests.cs +7 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +0 -203
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGeneratorTests.cs +871 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/IPackageDetailFinderTests.cs +28 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/TestHttpFetcher.cs +23 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +24 -24
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +14 -12
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandlerTests.cs +6 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/GroupUpdateAllVersionsHandlerTests.cs +18 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandlerTests.cs +15 -15
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandlerTests.cs +21 -21
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandlerTests.cs +15 -15
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/UpdateHandlersTestsBase.cs +1 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterWorkerTests.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterWorkerTests_MiscellaneousTests.cs +45 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests.cs +111 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +1 -159
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +26 -660
- data/helpers/lib/NuGetUpdater/global.json +1 -1
- metadata +18 -10
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunResult.cs +0 -13
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestMessageTests.cs +0 -296
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +0 -3592
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatePermittedAndMessageTests.cs +0 -457
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +0 -378
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +0 -175
@@ -1,8 +1,5 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
2
|
using System.Text.Json;
|
3
|
-
using System.Text.RegularExpressions;
|
4
|
-
|
5
|
-
using Microsoft.Language.Xml;
|
6
3
|
|
7
4
|
using NuGet.Frameworks;
|
8
5
|
using NuGet.Versioning;
|
@@ -25,170 +22,6 @@ namespace NuGetUpdater.Core;
|
|
25
22
|
/// </remarks>
|
26
23
|
internal static class PackageReferenceUpdater
|
27
24
|
{
|
28
|
-
public static async Task<IEnumerable<UpdateOperationBase>> UpdateDependencyAsync(
|
29
|
-
string repoRootPath,
|
30
|
-
string projectPath,
|
31
|
-
string dependencyName,
|
32
|
-
string previousDependencyVersion,
|
33
|
-
string newDependencyVersion,
|
34
|
-
bool isTransitive,
|
35
|
-
ExperimentsManager experimentsManager,
|
36
|
-
ILogger logger)
|
37
|
-
{
|
38
|
-
// PackageReference project; modify the XML directly
|
39
|
-
logger.Info(" Running 'PackageReference' project direct XML update");
|
40
|
-
|
41
|
-
(ImmutableArray<ProjectBuildFile> buildFiles, string[] tfms) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(repoRootPath, projectPath);
|
42
|
-
|
43
|
-
// Get the set of all top-level dependencies in the current project
|
44
|
-
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
|
45
|
-
var isDependencyTopLevel = topLevelDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
|
46
|
-
if (isDependencyTopLevel)
|
47
|
-
{
|
48
|
-
var packageMapper = DotNetPackageCorrelationManager.GetPackageMapper();
|
49
|
-
// TODO: this is slow
|
50
|
-
var isSdkReplacementPackage = packageMapper.RuntimePackages.Runtimes.Any(r =>
|
51
|
-
{
|
52
|
-
return r.Value.Packages.Any(p => dependencyName.Equals(p.Key, StringComparison.Ordinal));
|
53
|
-
});
|
54
|
-
if (isSdkReplacementPackage)
|
55
|
-
{
|
56
|
-
// If we're updating a top level SDK replacement package, the version listed in the project file won't
|
57
|
-
// necessarily match the resolved version that caused the update because the SDK might have replaced
|
58
|
-
// the package. To handle this scenario, we pretend the version we're searching for is the actual
|
59
|
-
// version in the file, not the resolved version. This allows us to keep a strict equality check when
|
60
|
-
// finding the file to update.
|
61
|
-
previousDependencyVersion = topLevelDependencies.First(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).Version!;
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
if (!await DoesDependencyRequireUpdateAsync(repoRootPath, projectPath, tfms, topLevelDependencies, dependencyName, newDependencyVersion, experimentsManager, logger))
|
66
|
-
{
|
67
|
-
return [];
|
68
|
-
}
|
69
|
-
|
70
|
-
var updateOperations = new List<UpdateOperationBase>();
|
71
|
-
var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, experimentsManager, logger);
|
72
|
-
if (experimentsManager.UseLegacyDependencySolver)
|
73
|
-
{
|
74
|
-
if (isTransitive)
|
75
|
-
{
|
76
|
-
var updatedFiles = await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
|
77
|
-
updateOperations.Add(new PinnedUpdate()
|
78
|
-
{
|
79
|
-
DependencyName = dependencyName,
|
80
|
-
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
81
|
-
UpdatedFiles = [.. updatedFiles],
|
82
|
-
});
|
83
|
-
}
|
84
|
-
else
|
85
|
-
{
|
86
|
-
if (peerDependencies is null)
|
87
|
-
{
|
88
|
-
return updateOperations;
|
89
|
-
}
|
90
|
-
|
91
|
-
var topLevelUpdateOperations = await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, experimentsManager, logger);
|
92
|
-
updateOperations.AddRange(topLevelUpdateOperations);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
else
|
96
|
-
{
|
97
|
-
if (peerDependencies is null)
|
98
|
-
{
|
99
|
-
return updateOperations;
|
100
|
-
}
|
101
|
-
|
102
|
-
var conflictResolutionUpdateOperations = await UpdateDependencyWithConflictResolution(
|
103
|
-
repoRootPath,
|
104
|
-
buildFiles,
|
105
|
-
tfms,
|
106
|
-
projectPath,
|
107
|
-
dependencyName,
|
108
|
-
previousDependencyVersion,
|
109
|
-
newDependencyVersion,
|
110
|
-
isTransitive,
|
111
|
-
peerDependencies,
|
112
|
-
experimentsManager,
|
113
|
-
logger);
|
114
|
-
updateOperations.AddRange(conflictResolutionUpdateOperations);
|
115
|
-
}
|
116
|
-
|
117
|
-
if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, buildFiles, tfms, experimentsManager, logger))
|
118
|
-
{
|
119
|
-
// should we return an empty set because we failed?
|
120
|
-
return updateOperations;
|
121
|
-
}
|
122
|
-
|
123
|
-
await SaveBuildFilesAsync(buildFiles, logger);
|
124
|
-
return updateOperations;
|
125
|
-
}
|
126
|
-
|
127
|
-
public static async Task<IEnumerable<UpdateOperationBase>> UpdateDependencyWithConflictResolution(
|
128
|
-
string repoRootPath,
|
129
|
-
ImmutableArray<ProjectBuildFile> buildFiles,
|
130
|
-
string[] targetFrameworks,
|
131
|
-
string projectPath,
|
132
|
-
string dependencyName,
|
133
|
-
string previousDependencyVersion,
|
134
|
-
string newDependencyVersion,
|
135
|
-
bool isTransitive,
|
136
|
-
IDictionary<string, string> peerDependencies,
|
137
|
-
ExperimentsManager experimentsManager,
|
138
|
-
ILogger logger)
|
139
|
-
{
|
140
|
-
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToImmutableArray();
|
141
|
-
var isDependencyTopLevel = topLevelDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
|
142
|
-
var dependenciesToUpdate = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference) }.ToImmutableArray();
|
143
|
-
var updateOperations = new List<UpdateOperationBase>();
|
144
|
-
|
145
|
-
// update the initial dependency...
|
146
|
-
var (_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
|
147
|
-
updateOperations.AddRange(updateOperationsPerformed);
|
148
|
-
|
149
|
-
// ...and the peer dependencies...
|
150
|
-
foreach (var (packageName, packageVersion) in peerDependencies.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0))
|
151
|
-
{
|
152
|
-
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
153
|
-
updateOperations.AddRange(updateOperationsPerformed);
|
154
|
-
}
|
155
|
-
|
156
|
-
// ...and everything else
|
157
|
-
foreach (var projectFile in buildFiles)
|
158
|
-
{
|
159
|
-
foreach (var tfm in targetFrameworks)
|
160
|
-
{
|
161
|
-
var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, topLevelDependencies, dependenciesToUpdate, experimentsManager, logger);
|
162
|
-
if (resolvedDependencies is null)
|
163
|
-
{
|
164
|
-
logger.Warn($" Unable to resolve dependency conflicts for {projectFile.Path}.");
|
165
|
-
continue;
|
166
|
-
}
|
167
|
-
|
168
|
-
var isDependencyInResolutionSet = resolvedDependencies.Value.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
|
169
|
-
if (isTransitive && !isDependencyTopLevel && isDependencyInResolutionSet)
|
170
|
-
{
|
171
|
-
// a transitive dependency had to be pinned; add it here
|
172
|
-
var updatedFiles = await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
|
173
|
-
}
|
174
|
-
|
175
|
-
// update all resolved dependencies that aren't the initial dependency
|
176
|
-
foreach (var resolvedDependency in resolvedDependencies.Value
|
177
|
-
.Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
178
|
-
.Where(d => d.Version is not null))
|
179
|
-
{
|
180
|
-
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
|
181
|
-
updateOperations.AddRange(updateOperationsPerformed);
|
182
|
-
}
|
183
|
-
|
184
|
-
updateOperationsPerformed = await ComputeUpdateOperations(repoRootPath, projectPath, tfm, topLevelDependencies, dependenciesToUpdate, resolvedDependencies.Value, experimentsManager, logger);
|
185
|
-
updateOperations.AddRange(updateOperationsPerformed.Select(u => u with { UpdatedFiles = [projectFile.Path] }));
|
186
|
-
}
|
187
|
-
}
|
188
|
-
|
189
|
-
return updateOperations;
|
190
|
-
}
|
191
|
-
|
192
25
|
internal static async Task<IEnumerable<UpdateOperationBase>> ComputeUpdateOperations(
|
193
26
|
string repoRoot,
|
194
27
|
string projectPath,
|
@@ -285,7 +118,7 @@ internal static class PackageReferenceUpdater
|
|
285
118
|
{
|
286
119
|
// generate project.assets.json
|
287
120
|
var parsedTargetFramework = NuGetFramework.Parse(targetFramework);
|
288
|
-
var tempProject = await MSBuildHelper.CreateTempProjectAsync(tempDir, repoRoot, projectPath, targetFramework, topLevelDependencies, experimentsManager, logger, importDependencyTargets:
|
121
|
+
var tempProject = await MSBuildHelper.CreateTempProjectAsync(tempDir, repoRoot, projectPath, targetFramework, topLevelDependencies, experimentsManager, logger, importDependencyTargets: false);
|
289
122
|
var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["build", tempProject, "/t:_ReportDependencies"], tempDir.FullName, experimentsManager);
|
290
123
|
var assetsJsonPath = Path.Join(tempDir.FullName, "obj", "project.assets.json");
|
291
124
|
var assetsJsonContent = await File.ReadAllTextAsync(assetsJsonPath);
|
@@ -332,692 +165,4 @@ internal static class PackageReferenceUpdater
|
|
332
165
|
tempDir.Delete(recursive: true);
|
333
166
|
}
|
334
167
|
}
|
335
|
-
|
336
|
-
/// <summary>
|
337
|
-
/// Verifies that the package does not already satisfy the requested dependency version.
|
338
|
-
/// </summary>
|
339
|
-
/// <returns>Returns false if the package is not found or does not need to be updated.</returns>
|
340
|
-
private static async Task<bool> DoesDependencyRequireUpdateAsync(
|
341
|
-
string repoRootPath,
|
342
|
-
string projectPath,
|
343
|
-
string[] tfms,
|
344
|
-
Dependency[] topLevelDependencies,
|
345
|
-
string dependencyName,
|
346
|
-
string newDependencyVersion,
|
347
|
-
ExperimentsManager experimentsManager,
|
348
|
-
ILogger logger)
|
349
|
-
{
|
350
|
-
var newDependencyNuGetVersion = NuGetVersion.Parse(newDependencyVersion);
|
351
|
-
|
352
|
-
bool packageFound = false;
|
353
|
-
bool needsUpdate = false;
|
354
|
-
|
355
|
-
foreach (var tfm in tfms)
|
356
|
-
{
|
357
|
-
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(
|
358
|
-
repoRootPath,
|
359
|
-
projectPath,
|
360
|
-
tfm,
|
361
|
-
topLevelDependencies,
|
362
|
-
experimentsManager,
|
363
|
-
logger);
|
364
|
-
foreach (var dependency in dependencies)
|
365
|
-
{
|
366
|
-
var packageName = dependency.Name;
|
367
|
-
var packageVersion = dependency.Version;
|
368
|
-
if (packageVersion is null)
|
369
|
-
{
|
370
|
-
continue;
|
371
|
-
}
|
372
|
-
|
373
|
-
if (packageName.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
374
|
-
{
|
375
|
-
packageFound = true;
|
376
|
-
|
377
|
-
var nugetVersion = NuGetVersion.Parse(packageVersion);
|
378
|
-
if (nugetVersion < newDependencyNuGetVersion)
|
379
|
-
{
|
380
|
-
needsUpdate = true;
|
381
|
-
break;
|
382
|
-
}
|
383
|
-
}
|
384
|
-
}
|
385
|
-
|
386
|
-
if (packageFound && needsUpdate)
|
387
|
-
{
|
388
|
-
break;
|
389
|
-
}
|
390
|
-
}
|
391
|
-
|
392
|
-
// Skip updating the project if the dependency does not exist in the graph
|
393
|
-
if (!packageFound)
|
394
|
-
{
|
395
|
-
logger.Info($" Package [{dependencyName}] Does not exist as a dependency in [{projectPath}].");
|
396
|
-
return false;
|
397
|
-
}
|
398
|
-
|
399
|
-
// Skip updating the project if the dependency version meets or exceeds the newDependencyVersion
|
400
|
-
if (!needsUpdate)
|
401
|
-
{
|
402
|
-
logger.Info($" Package [{dependencyName}] already meets the requested dependency version in [{projectPath}].");
|
403
|
-
return false;
|
404
|
-
}
|
405
|
-
|
406
|
-
return true;
|
407
|
-
}
|
408
|
-
|
409
|
-
/// <returns>The updated files.</returns>
|
410
|
-
internal static async Task<IEnumerable<string>> UpdateTransitiveDependencyAsync(
|
411
|
-
string repoRootPath,
|
412
|
-
string projectPath,
|
413
|
-
string dependencyName,
|
414
|
-
string newDependencyVersion,
|
415
|
-
ImmutableArray<ProjectBuildFile> buildFiles,
|
416
|
-
ExperimentsManager experimentsManager,
|
417
|
-
ILogger logger
|
418
|
-
)
|
419
|
-
{
|
420
|
-
IEnumerable<string> updatedFiles;
|
421
|
-
var directoryPackagesWithPinning = buildFiles.OfType<ProjectBuildFile>()
|
422
|
-
.FirstOrDefault(bf => IsCpmTransitivePinningEnabled(bf));
|
423
|
-
if (directoryPackagesWithPinning is not null)
|
424
|
-
{
|
425
|
-
updatedFiles = PinTransitiveDependency(directoryPackagesWithPinning, dependencyName, newDependencyVersion, logger);
|
426
|
-
}
|
427
|
-
else
|
428
|
-
{
|
429
|
-
updatedFiles = await AddTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, experimentsManager, logger);
|
430
|
-
|
431
|
-
// files directly modified on disk by an external tool need to be refreshed in-memory
|
432
|
-
foreach (var updatedFile in updatedFiles)
|
433
|
-
{
|
434
|
-
var matchingBuildFile = buildFiles.FirstOrDefault(bf => PathComparer.Instance.Compare(updatedFile, bf.Path) == 0);
|
435
|
-
if (matchingBuildFile is not null)
|
436
|
-
{
|
437
|
-
var updatedContents = await File.ReadAllTextAsync(updatedFile);
|
438
|
-
matchingBuildFile.Update(ProjectBuildFile.Parse(updatedContents));
|
439
|
-
}
|
440
|
-
}
|
441
|
-
}
|
442
|
-
|
443
|
-
return updatedFiles;
|
444
|
-
}
|
445
|
-
|
446
|
-
private static bool IsCpmTransitivePinningEnabled(ProjectBuildFile buildFile)
|
447
|
-
{
|
448
|
-
var buildFileName = Path.GetFileName(buildFile.Path);
|
449
|
-
if (!buildFileName.Equals("Directory.Packages.props", StringComparison.OrdinalIgnoreCase))
|
450
|
-
{
|
451
|
-
return false;
|
452
|
-
}
|
453
|
-
|
454
|
-
var propertyElements = buildFile.PropertyNodes;
|
455
|
-
|
456
|
-
var isCpmEnabledValue = propertyElements.FirstOrDefault(e =>
|
457
|
-
e.Name.Equals("ManagePackageVersionsCentrally", StringComparison.OrdinalIgnoreCase))?.GetContentValue();
|
458
|
-
if (isCpmEnabledValue is null || !string.Equals(isCpmEnabledValue, "true", StringComparison.OrdinalIgnoreCase))
|
459
|
-
{
|
460
|
-
return false;
|
461
|
-
}
|
462
|
-
|
463
|
-
var isTransitivePinningEnabled = propertyElements.FirstOrDefault(e =>
|
464
|
-
e.Name.Equals("CentralPackageTransitivePinningEnabled", StringComparison.OrdinalIgnoreCase))?.GetContentValue();
|
465
|
-
return isTransitivePinningEnabled is not null && string.Equals(isTransitivePinningEnabled, "true", StringComparison.OrdinalIgnoreCase);
|
466
|
-
}
|
467
|
-
|
468
|
-
/// <returns>The updated files.</returns>
|
469
|
-
private static IEnumerable<string> PinTransitiveDependency(ProjectBuildFile directoryPackages, string dependencyName, string newDependencyVersion, ILogger logger)
|
470
|
-
{
|
471
|
-
var existingPackageVersionElement = directoryPackages.ItemNodes
|
472
|
-
.Where(e => e.Name.Equals("PackageVersion", StringComparison.OrdinalIgnoreCase) &&
|
473
|
-
e.Attributes.Any(a => a.Name.Equals("Include", StringComparison.OrdinalIgnoreCase) &&
|
474
|
-
a.Value.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)))
|
475
|
-
.FirstOrDefault();
|
476
|
-
|
477
|
-
logger.Info($" Pinning [{dependencyName}/{newDependencyVersion}] as a package version.");
|
478
|
-
|
479
|
-
var lastPackageVersion = directoryPackages.ItemNodes
|
480
|
-
.Where(e => e.Name.Equals("PackageVersion", StringComparison.OrdinalIgnoreCase))
|
481
|
-
.LastOrDefault();
|
482
|
-
|
483
|
-
if (lastPackageVersion is null)
|
484
|
-
{
|
485
|
-
logger.Info($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not pinned.");
|
486
|
-
return [];
|
487
|
-
}
|
488
|
-
|
489
|
-
var lastItemGroup = lastPackageVersion.Parent;
|
490
|
-
|
491
|
-
IXmlElementSyntax updatedItemGroup;
|
492
|
-
if (existingPackageVersionElement is null)
|
493
|
-
{
|
494
|
-
// need to add a new entry
|
495
|
-
logger.Info(" New PackageVersion element added.");
|
496
|
-
var leadingTrivia = lastPackageVersion.AsNode.GetLeadingTrivia();
|
497
|
-
var packageVersionElement = XmlExtensions.CreateSingleLineXmlElementSyntax("PackageVersion", new SyntaxList<SyntaxNode>(leadingTrivia))
|
498
|
-
.WithAttribute("Include", dependencyName)
|
499
|
-
.WithAttribute("Version", newDependencyVersion);
|
500
|
-
updatedItemGroup = lastItemGroup.AddChild(packageVersionElement);
|
501
|
-
}
|
502
|
-
else
|
503
|
-
{
|
504
|
-
IXmlElementSyntax updatedPackageVersionElement;
|
505
|
-
var versionAttribute = existingPackageVersionElement.Attributes.FirstOrDefault(a => a.Name.Equals("Version", StringComparison.OrdinalIgnoreCase));
|
506
|
-
if (versionAttribute is null)
|
507
|
-
{
|
508
|
-
// need to add the version
|
509
|
-
logger.Info(" Adding version attribute to element.");
|
510
|
-
updatedPackageVersionElement = existingPackageVersionElement.WithAttribute("Version", newDependencyVersion);
|
511
|
-
}
|
512
|
-
else if (!versionAttribute.Value.Equals(newDependencyVersion, StringComparison.OrdinalIgnoreCase))
|
513
|
-
{
|
514
|
-
// need to update the version
|
515
|
-
logger.Info($" Updating version attribute of [{versionAttribute.Value}].");
|
516
|
-
var updatedVersionAttribute = versionAttribute.WithValue(newDependencyVersion);
|
517
|
-
updatedPackageVersionElement = existingPackageVersionElement.ReplaceAttribute(versionAttribute, updatedVersionAttribute);
|
518
|
-
}
|
519
|
-
else
|
520
|
-
{
|
521
|
-
logger.Info(" Existing PackageVersion element version was already correct.");
|
522
|
-
return [];
|
523
|
-
}
|
524
|
-
|
525
|
-
updatedItemGroup = lastItemGroup.ReplaceChildElement(existingPackageVersionElement, updatedPackageVersionElement);
|
526
|
-
}
|
527
|
-
|
528
|
-
var updatedXml = directoryPackages.Contents.ReplaceNode(lastItemGroup.AsNode, updatedItemGroup.AsNode);
|
529
|
-
directoryPackages.Update(updatedXml);
|
530
|
-
|
531
|
-
return [directoryPackages.Path];
|
532
|
-
}
|
533
|
-
|
534
|
-
/// <returns>The updated files.</returns>
|
535
|
-
private static async Task<IEnumerable<string>> AddTransitiveDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string newDependencyVersion, ExperimentsManager experimentsManager, ILogger logger)
|
536
|
-
{
|
537
|
-
var updatedFiles = new List<string>() { projectPath }; // assume this worked unless...
|
538
|
-
var projectDirectory = Path.GetDirectoryName(projectPath)!;
|
539
|
-
await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
|
540
|
-
{
|
541
|
-
logger.Info($" Adding [{dependencyName}/{newDependencyVersion}] as a top-level package reference.");
|
542
|
-
|
543
|
-
// see https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli
|
544
|
-
var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(
|
545
|
-
["add", projectPath, "package", dependencyName, "--version", newDependencyVersion],
|
546
|
-
projectDirectory,
|
547
|
-
experimentsManager
|
548
|
-
);
|
549
|
-
MSBuildHelper.ThrowOnError(stdout);
|
550
|
-
if (exitCode != 0)
|
551
|
-
{
|
552
|
-
logger.Warn($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
|
553
|
-
updatedFiles = [];
|
554
|
-
}
|
555
|
-
|
556
|
-
// output might contain a line like this:
|
557
|
-
// info : PackageReference for package 'Some.Package' added to 'C:\project.csproj' and PackageVersion added to central package management file 'C:\Directory.Packages.props'.
|
558
|
-
// we explicitly want to pull out this: ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
559
|
-
// so we can report all files updated on disk
|
560
|
-
var match = Regex.Match(stdout, @"added to central package management file '(?<CentralPackageManagementFilePath>[^']+)'");
|
561
|
-
if (match.Success)
|
562
|
-
{
|
563
|
-
var cpmFilePath = match.Groups["CentralPackageManagementFilePath"].Value;
|
564
|
-
updatedFiles.Add(cpmFilePath);
|
565
|
-
}
|
566
|
-
|
567
|
-
return exitCode;
|
568
|
-
}, logger, retainMSBuildSdks: true);
|
569
|
-
|
570
|
-
return updatedFiles;
|
571
|
-
}
|
572
|
-
|
573
|
-
/// <summary>
|
574
|
-
/// Gets the set of peer dependencies that need to be updated.
|
575
|
-
/// </summary>
|
576
|
-
/// <returns>Returns null if there are conflicting versions.</returns>
|
577
|
-
private static async Task<Dictionary<string, string>?> GetUpdatedPeerDependenciesAsync(
|
578
|
-
string repoRootPath,
|
579
|
-
string projectPath,
|
580
|
-
string[] tfms,
|
581
|
-
string dependencyName,
|
582
|
-
string newDependencyVersion,
|
583
|
-
ExperimentsManager experimentsManager,
|
584
|
-
ILogger logger)
|
585
|
-
{
|
586
|
-
var newDependency = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.Unknown) };
|
587
|
-
var tfmsAndDependencies = new Dictionary<string, ImmutableArray<Dependency>>();
|
588
|
-
foreach (var tfm in tfms)
|
589
|
-
{
|
590
|
-
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, newDependency, experimentsManager, logger);
|
591
|
-
tfmsAndDependencies[tfm] = dependencies;
|
592
|
-
}
|
593
|
-
|
594
|
-
var unupgradableTfms = tfmsAndDependencies.Where(kvp => !kvp.Value.Any()).Select(kvp => kvp.Key);
|
595
|
-
if (unupgradableTfms.Any())
|
596
|
-
{
|
597
|
-
logger.Info($" The following target frameworks could not find packages to upgrade: {string.Join(", ", unupgradableTfms)}");
|
598
|
-
return null;
|
599
|
-
}
|
600
|
-
|
601
|
-
var conflictingPackageVersionsFound = false;
|
602
|
-
var packagesAndVersions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
603
|
-
foreach (var (_, dependencies) in tfmsAndDependencies)
|
604
|
-
{
|
605
|
-
foreach (var dependency in dependencies)
|
606
|
-
{
|
607
|
-
var packageName = dependency.Name;
|
608
|
-
var packageVersion = dependency.Version;
|
609
|
-
if (packagesAndVersions.TryGetValue(packageName, out var existingVersion) &&
|
610
|
-
existingVersion != packageVersion)
|
611
|
-
{
|
612
|
-
logger.Info($" Package [{packageName}] tried to update to version [{packageVersion}], but found conflicting package version of [{existingVersion}].");
|
613
|
-
conflictingPackageVersionsFound = true;
|
614
|
-
}
|
615
|
-
else
|
616
|
-
{
|
617
|
-
packagesAndVersions[packageName] = packageVersion!;
|
618
|
-
}
|
619
|
-
}
|
620
|
-
}
|
621
|
-
|
622
|
-
// stop update process if we find conflicting package versions
|
623
|
-
if (conflictingPackageVersionsFound)
|
624
|
-
{
|
625
|
-
return null;
|
626
|
-
}
|
627
|
-
|
628
|
-
return packagesAndVersions;
|
629
|
-
}
|
630
|
-
|
631
|
-
private static async Task<IEnumerable<UpdateOperationBase>> UpdateTopLevelDepdendency(
|
632
|
-
string repoRootPath,
|
633
|
-
ImmutableArray<ProjectBuildFile> buildFiles,
|
634
|
-
string[] targetFrameworks,
|
635
|
-
string dependencyName,
|
636
|
-
string previousDependencyVersion,
|
637
|
-
string newDependencyVersion,
|
638
|
-
IDictionary<string, string> peerDependencies,
|
639
|
-
ExperimentsManager experimentsManager,
|
640
|
-
ILogger logger)
|
641
|
-
{
|
642
|
-
// update dependencies...
|
643
|
-
var updateOperations = new List<UpdateOperationBase>();
|
644
|
-
var (updateResult, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
|
645
|
-
if (updateResult == UpdateResult.NotFound)
|
646
|
-
{
|
647
|
-
logger.Info($" Root package [{dependencyName}/{previousDependencyVersion}] was not updated; skipping dependencies.");
|
648
|
-
return [];
|
649
|
-
}
|
650
|
-
|
651
|
-
updateOperations.AddRange(updateOperationsPerformed);
|
652
|
-
|
653
|
-
foreach (var (packageName, packageVersion) in peerDependencies.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0))
|
654
|
-
{
|
655
|
-
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
656
|
-
updateOperations.AddRange(updateOperationsPerformed);
|
657
|
-
}
|
658
|
-
|
659
|
-
// ...and make them all coherent
|
660
|
-
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToImmutableArray();
|
661
|
-
foreach (ProjectBuildFile projectFile in buildFiles)
|
662
|
-
{
|
663
|
-
foreach (string tfm in targetFrameworks)
|
664
|
-
{
|
665
|
-
var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRootPath, projectFile.Path, tfm, topLevelDependencies, experimentsManager, logger);
|
666
|
-
if (resolvedDependencies is null)
|
667
|
-
{
|
668
|
-
logger.Info($" Unable to resolve dependency conflicts for {projectFile.Path}.");
|
669
|
-
continue;
|
670
|
-
}
|
671
|
-
|
672
|
-
// ensure the originally requested dependency was resolved to the correct version
|
673
|
-
var specificResolvedDependency = resolvedDependencies.Value.Where(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
674
|
-
if (specificResolvedDependency is null)
|
675
|
-
{
|
676
|
-
logger.Info($" Unable to resolve requested dependency for {dependencyName} in {projectFile.Path}.");
|
677
|
-
continue;
|
678
|
-
}
|
679
|
-
|
680
|
-
if (!newDependencyVersion.Equals(specificResolvedDependency.Version, StringComparison.OrdinalIgnoreCase))
|
681
|
-
{
|
682
|
-
logger.Info($" Inconsistent resolution for {dependencyName}; attempted upgrade to {newDependencyVersion} but resolved {specificResolvedDependency.Version}.");
|
683
|
-
continue;
|
684
|
-
}
|
685
|
-
|
686
|
-
// update all versions
|
687
|
-
foreach (Dependency resolvedDependency in resolvedDependencies.Value
|
688
|
-
.Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
689
|
-
.Where(d => d.Version is not null))
|
690
|
-
{
|
691
|
-
(_, updateOperationsPerformed) = TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
|
692
|
-
updateOperations.AddRange(updateOperationsPerformed);
|
693
|
-
}
|
694
|
-
|
695
|
-
updateOperationsPerformed = await ComputeUpdateOperations(repoRootPath, projectFile.Path, tfm, topLevelDependencies, [new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference)], resolvedDependencies.Value, experimentsManager, logger);
|
696
|
-
updateOperations.AddRange(updateOperationsPerformed.Select(u => u with { UpdatedFiles = [projectFile.Path] }));
|
697
|
-
}
|
698
|
-
}
|
699
|
-
|
700
|
-
return updateOperations;
|
701
|
-
}
|
702
|
-
|
703
|
-
/// <returns>The updated files.</returns>
|
704
|
-
internal static (UpdateResult, IEnumerable<UpdateOperationBase>) TryUpdateDependencyVersion(
|
705
|
-
ImmutableArray<ProjectBuildFile> buildFiles,
|
706
|
-
string dependencyName,
|
707
|
-
string? previousDependencyVersion,
|
708
|
-
string newDependencyVersion,
|
709
|
-
ILogger logger)
|
710
|
-
{
|
711
|
-
var foundCorrect = false;
|
712
|
-
var foundUnsupported = false;
|
713
|
-
var updateWasPerformed = false;
|
714
|
-
var propertyNames = new List<string>();
|
715
|
-
var updateOperations = new List<UpdateOperationBase>();
|
716
|
-
|
717
|
-
// First we locate all the PackageReference, GlobalPackageReference, or PackageVersion which set the Version
|
718
|
-
// or VersionOverride attribute. In the simplest case we can update the version attribute directly then move
|
719
|
-
// on. When property substitution is used we have to additionally search for the property containing the version.
|
720
|
-
|
721
|
-
foreach (var buildFile in buildFiles)
|
722
|
-
{
|
723
|
-
var updateNodes = new List<XmlNodeSyntax>();
|
724
|
-
var packageNodes = FindPackageNodes(buildFile, dependencyName);
|
725
|
-
|
726
|
-
var previousPackageVersion = previousDependencyVersion;
|
727
|
-
|
728
|
-
foreach (var packageNode in packageNodes)
|
729
|
-
{
|
730
|
-
var versionAttribute = packageNode.GetAttribute("Version", StringComparison.OrdinalIgnoreCase)
|
731
|
-
?? packageNode.GetAttribute("VersionOverride", StringComparison.OrdinalIgnoreCase);
|
732
|
-
var versionElement = packageNode.Elements.FirstOrDefault(e => e.Name.Equals("Version", StringComparison.OrdinalIgnoreCase))
|
733
|
-
?? packageNode.Elements.FirstOrDefault(e => e.Name.Equals("VersionOverride", StringComparison.OrdinalIgnoreCase));
|
734
|
-
if (versionAttribute is not null)
|
735
|
-
{
|
736
|
-
// Is this the case where version is specified with property substitution?
|
737
|
-
if (MSBuildHelper.TryGetPropertyName(versionAttribute.Value, out var propertyName))
|
738
|
-
{
|
739
|
-
propertyNames.Add(propertyName);
|
740
|
-
}
|
741
|
-
// Is this the case that the version is specified directly in the package node?
|
742
|
-
else
|
743
|
-
{
|
744
|
-
var currentVersion = versionAttribute.Value.TrimStart('[', '(').TrimEnd(']', ')');
|
745
|
-
if (currentVersion.Contains(',') || currentVersion.Contains('*'))
|
746
|
-
{
|
747
|
-
logger.Warn($" Found unsupported [{packageNode.Name}] version attribute value [{versionAttribute.Value}] in [{buildFile.RelativePath}].");
|
748
|
-
foundUnsupported = true;
|
749
|
-
}
|
750
|
-
else if (string.Equals(currentVersion, previousDependencyVersion, StringComparison.Ordinal))
|
751
|
-
{
|
752
|
-
logger.Info($" Found incorrect [{packageNode.Name}] version attribute in [{buildFile.RelativePath}].");
|
753
|
-
updateNodes.Add(versionAttribute);
|
754
|
-
updateOperations.Add(new DirectUpdate()
|
755
|
-
{
|
756
|
-
DependencyName = dependencyName,
|
757
|
-
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
758
|
-
UpdatedFiles = [buildFile.Path],
|
759
|
-
});
|
760
|
-
}
|
761
|
-
else if (previousDependencyVersion == null && NuGetVersion.TryParse(currentVersion, out var previousVersion))
|
762
|
-
{
|
763
|
-
var newVersion = NuGetVersion.Parse(newDependencyVersion);
|
764
|
-
if (previousVersion < newVersion)
|
765
|
-
{
|
766
|
-
previousPackageVersion = currentVersion;
|
767
|
-
|
768
|
-
logger.Info($" Found incorrect peer [{packageNode.Name}] version attribute in [{buildFile.RelativePath}].");
|
769
|
-
updateNodes.Add(versionAttribute);
|
770
|
-
updateOperations.Add(new DirectUpdate()
|
771
|
-
{
|
772
|
-
DependencyName = dependencyName,
|
773
|
-
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
774
|
-
UpdatedFiles = [buildFile.Path],
|
775
|
-
});
|
776
|
-
}
|
777
|
-
}
|
778
|
-
else if (string.Equals(currentVersion, newDependencyVersion, StringComparison.Ordinal))
|
779
|
-
{
|
780
|
-
logger.Info($" Found correct [{packageNode.Name}] version attribute in [{buildFile.RelativePath}].");
|
781
|
-
foundCorrect = true;
|
782
|
-
}
|
783
|
-
}
|
784
|
-
}
|
785
|
-
else if (versionElement is not null)
|
786
|
-
{
|
787
|
-
var versionValue = versionElement.GetContentValue();
|
788
|
-
if (MSBuildHelper.TryGetPropertyName(versionValue, out var propertyName))
|
789
|
-
{
|
790
|
-
propertyNames.Add(propertyName);
|
791
|
-
}
|
792
|
-
else
|
793
|
-
{
|
794
|
-
var currentVersion = versionValue.TrimStart('[', '(').TrimEnd(']', ')');
|
795
|
-
if (currentVersion.Contains(',') || currentVersion.Contains('*'))
|
796
|
-
{
|
797
|
-
logger.Info($" Found unsupported [{packageNode.Name}] version node value [{versionValue}] in [{buildFile.RelativePath}].");
|
798
|
-
foundUnsupported = true;
|
799
|
-
}
|
800
|
-
else if (currentVersion == previousDependencyVersion)
|
801
|
-
{
|
802
|
-
logger.Info($" Found incorrect [{packageNode.Name}] version node in [{buildFile.RelativePath}].");
|
803
|
-
if (versionElement is XmlElementSyntax elementSyntax)
|
804
|
-
{
|
805
|
-
updateNodes.Add(elementSyntax);
|
806
|
-
updateOperations.Add(new DirectUpdate()
|
807
|
-
{
|
808
|
-
DependencyName = dependencyName,
|
809
|
-
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
810
|
-
UpdatedFiles = [buildFile.Path],
|
811
|
-
});
|
812
|
-
}
|
813
|
-
else
|
814
|
-
{
|
815
|
-
throw new InvalidDataException("A concrete type was required for updateNodes. This should not happen.");
|
816
|
-
}
|
817
|
-
}
|
818
|
-
else if (previousDependencyVersion == null && NuGetVersion.TryParse(currentVersion, out var previousVersion))
|
819
|
-
{
|
820
|
-
var newVersion = NuGetVersion.Parse(newDependencyVersion);
|
821
|
-
if (previousVersion < newVersion)
|
822
|
-
{
|
823
|
-
previousPackageVersion = currentVersion;
|
824
|
-
|
825
|
-
logger.Info($" Found incorrect peer [{packageNode.Name}] version node in [{buildFile.RelativePath}].");
|
826
|
-
if (versionElement is XmlElementSyntax elementSyntax)
|
827
|
-
{
|
828
|
-
updateNodes.Add(elementSyntax);
|
829
|
-
updateOperations.Add(new DirectUpdate()
|
830
|
-
{
|
831
|
-
DependencyName = dependencyName,
|
832
|
-
NewVersion = NuGetVersion.Parse(newDependencyVersion),
|
833
|
-
UpdatedFiles = [buildFile.Path],
|
834
|
-
});
|
835
|
-
}
|
836
|
-
else
|
837
|
-
{
|
838
|
-
// This only exists for completeness in case we ever add a new type of node we don't want to silently ignore them.
|
839
|
-
throw new InvalidDataException("A concrete type was required for updateNodes. This should not happen.");
|
840
|
-
}
|
841
|
-
}
|
842
|
-
}
|
843
|
-
else if (currentVersion == newDependencyVersion)
|
844
|
-
{
|
845
|
-
logger.Info($" Found correct [{packageNode.Name}] version node in [{buildFile.RelativePath}].");
|
846
|
-
foundCorrect = true;
|
847
|
-
}
|
848
|
-
}
|
849
|
-
}
|
850
|
-
else
|
851
|
-
{
|
852
|
-
// We weren't able to find the version node. Central package management?
|
853
|
-
logger.Warn(" Found package reference but was unable to locate version information.");
|
854
|
-
}
|
855
|
-
}
|
856
|
-
|
857
|
-
if (updateNodes.Count > 0)
|
858
|
-
{
|
859
|
-
var updatedXml = buildFile.Contents
|
860
|
-
.ReplaceNodes(updateNodes, (_, n) =>
|
861
|
-
{
|
862
|
-
if (n is XmlAttributeSyntax attributeSyntax)
|
863
|
-
{
|
864
|
-
return attributeSyntax.WithValue(attributeSyntax.Value.Replace(previousPackageVersion!, newDependencyVersion));
|
865
|
-
}
|
866
|
-
|
867
|
-
if (n is XmlElementSyntax elementsSyntax)
|
868
|
-
{
|
869
|
-
var modifiedContent = elementsSyntax.GetContentValue().Replace(previousPackageVersion!, newDependencyVersion);
|
870
|
-
|
871
|
-
var textSyntax = SyntaxFactory.XmlText(SyntaxFactory.Token(null, SyntaxKind.XmlTextLiteralToken, null, modifiedContent));
|
872
|
-
return elementsSyntax.WithContent(SyntaxFactory.SingletonList(textSyntax));
|
873
|
-
}
|
874
|
-
|
875
|
-
throw new InvalidDataException($"Unsupported SyntaxType {n.GetType().Name} marked for update");
|
876
|
-
});
|
877
|
-
buildFile.Update(updatedXml);
|
878
|
-
updateWasPerformed = true;
|
879
|
-
}
|
880
|
-
}
|
881
|
-
|
882
|
-
// If property substitution was used to set the Version, we must search for the property containing
|
883
|
-
// the version string. Since it could also be populated by property substitution this search repeats
|
884
|
-
// with the each new property name until the version string is located.
|
885
|
-
|
886
|
-
var processedPropertyNames = new HashSet<string>();
|
887
|
-
|
888
|
-
for (int propertyNameIndex = 0; propertyNameIndex < propertyNames.Count; propertyNameIndex++)
|
889
|
-
{
|
890
|
-
var propertyName = propertyNames[propertyNameIndex];
|
891
|
-
if (processedPropertyNames.Contains(propertyName))
|
892
|
-
{
|
893
|
-
continue;
|
894
|
-
}
|
895
|
-
|
896
|
-
processedPropertyNames.Add(propertyName);
|
897
|
-
|
898
|
-
foreach (var buildFile in buildFiles)
|
899
|
-
{
|
900
|
-
var updateProperties = new List<XmlElementSyntax>();
|
901
|
-
var propertyElements = buildFile.PropertyNodes
|
902
|
-
.Where(e => e.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
|
903
|
-
|
904
|
-
var previousPackageVersion = previousDependencyVersion;
|
905
|
-
|
906
|
-
foreach (var propertyElement in propertyElements)
|
907
|
-
{
|
908
|
-
var propertyContents = propertyElement.GetContentValue();
|
909
|
-
|
910
|
-
// Is this the case where this property contains another property substitution?
|
911
|
-
if (MSBuildHelper.TryGetPropertyName(propertyContents, out var propName))
|
912
|
-
{
|
913
|
-
propertyNames.Add(propName);
|
914
|
-
}
|
915
|
-
// Is this the case that the property contains the version?
|
916
|
-
else
|
917
|
-
{
|
918
|
-
var currentVersion = propertyContents.TrimStart('[', '(').TrimEnd(']', ')');
|
919
|
-
if (currentVersion.Contains(',') || currentVersion.Contains('*'))
|
920
|
-
{
|
921
|
-
logger.Warn($" Found unsupported version property [{propertyElement.Name}] value [{propertyContents}] in [{buildFile.RelativePath}].");
|
922
|
-
foundUnsupported = true;
|
923
|
-
}
|
924
|
-
else if (currentVersion == previousDependencyVersion)
|
925
|
-
{
|
926
|
-
logger.Info($" Found incorrect version property [{propertyElement.Name}] in [{buildFile.RelativePath}].");
|
927
|
-
updateProperties.Add((XmlElementSyntax)propertyElement.AsNode);
|
928
|
-
}
|
929
|
-
else if (previousDependencyVersion is null && NuGetVersion.TryParse(currentVersion, out var previousVersion))
|
930
|
-
{
|
931
|
-
var newVersion = NuGetVersion.Parse(newDependencyVersion);
|
932
|
-
if (previousVersion < newVersion)
|
933
|
-
{
|
934
|
-
previousPackageVersion = currentVersion;
|
935
|
-
|
936
|
-
logger.Info($" Found incorrect peer version property [{propertyElement.Name}] in [{buildFile.RelativePath}].");
|
937
|
-
updateProperties.Add((XmlElementSyntax)propertyElement.AsNode);
|
938
|
-
}
|
939
|
-
}
|
940
|
-
else if (currentVersion == newDependencyVersion)
|
941
|
-
{
|
942
|
-
logger.Info($" Found correct version property [{propertyElement.Name}] in [{buildFile.RelativePath}].");
|
943
|
-
foundCorrect = true;
|
944
|
-
}
|
945
|
-
}
|
946
|
-
}
|
947
|
-
|
948
|
-
if (updateProperties.Count > 0)
|
949
|
-
{
|
950
|
-
var updatedXml = buildFile.Contents
|
951
|
-
.ReplaceNodes(updateProperties, (o, n) => n.WithContent(o.GetContentValue().Replace(previousPackageVersion!, newDependencyVersion)).AsNode);
|
952
|
-
buildFile.Update(updatedXml);
|
953
|
-
updateWasPerformed = true;
|
954
|
-
}
|
955
|
-
}
|
956
|
-
}
|
957
|
-
|
958
|
-
var updateResult = updateWasPerformed
|
959
|
-
? UpdateResult.Updated
|
960
|
-
: foundCorrect
|
961
|
-
? UpdateResult.Correct
|
962
|
-
: foundUnsupported
|
963
|
-
? UpdateResult.NotSupported
|
964
|
-
: UpdateResult.NotFound;
|
965
|
-
return (updateResult, updateOperations);
|
966
|
-
}
|
967
|
-
|
968
|
-
private static IEnumerable<IXmlElementSyntax> FindPackageNodes(
|
969
|
-
ProjectBuildFile buildFile,
|
970
|
-
string packageName)
|
971
|
-
=> buildFile.PackageItemNodes.Where(e =>
|
972
|
-
{
|
973
|
-
// Attempt to get "Include" or "Update" attribute values
|
974
|
-
var includeOrUpdateValue = e.GetAttributeOrSubElementValue("Include", StringComparison.OrdinalIgnoreCase)
|
975
|
-
?? e.GetAttributeOrSubElementValue("Update", StringComparison.OrdinalIgnoreCase);
|
976
|
-
// Trim and split if there's a valid value
|
977
|
-
var packageNames = includeOrUpdateValue?
|
978
|
-
.Trim()
|
979
|
-
.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
980
|
-
.Select(t => t.Trim())
|
981
|
-
.Where(t => t.Equals(packageName.Trim(), StringComparison.OrdinalIgnoreCase));
|
982
|
-
// Check if there's a matching package name and a non-null version attribute
|
983
|
-
return packageNames?.Any() == true &&
|
984
|
-
(e.GetAttributeOrSubElementValue("Version", StringComparison.OrdinalIgnoreCase)
|
985
|
-
?? e.GetAttributeOrSubElementValue("VersionOverride", StringComparison.OrdinalIgnoreCase)) is not null;
|
986
|
-
});
|
987
|
-
|
988
|
-
private static async Task<bool> AreDependenciesCoherentAsync(
|
989
|
-
string repoRootPath,
|
990
|
-
string projectPath,
|
991
|
-
string dependencyName,
|
992
|
-
ImmutableArray<ProjectBuildFile> buildFiles,
|
993
|
-
string[] tfms,
|
994
|
-
ExperimentsManager experimentsManager,
|
995
|
-
ILogger logger
|
996
|
-
)
|
997
|
-
{
|
998
|
-
var updatedTopLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
|
999
|
-
foreach (var tfm in tfms)
|
1000
|
-
{
|
1001
|
-
var updatedPackages = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, updatedTopLevelDependencies, experimentsManager, logger);
|
1002
|
-
var dependenciesAreCoherent = await MSBuildHelper.DependenciesAreCoherentAsync(repoRootPath, projectPath, tfm, updatedPackages, experimentsManager, logger);
|
1003
|
-
if (!dependenciesAreCoherent)
|
1004
|
-
{
|
1005
|
-
logger.Warn($" Package [{dependencyName}] could not be updated in [{projectPath}] because it would cause a dependency conflict.");
|
1006
|
-
return false;
|
1007
|
-
}
|
1008
|
-
}
|
1009
|
-
|
1010
|
-
return true;
|
1011
|
-
}
|
1012
|
-
|
1013
|
-
private static async Task SaveBuildFilesAsync(ImmutableArray<ProjectBuildFile> buildFiles, ILogger logger)
|
1014
|
-
{
|
1015
|
-
foreach (var buildFile in buildFiles)
|
1016
|
-
{
|
1017
|
-
if (await buildFile.SaveAsync())
|
1018
|
-
{
|
1019
|
-
logger.Info($" Saved [{buildFile.RelativePath}].");
|
1020
|
-
}
|
1021
|
-
}
|
1022
|
-
}
|
1023
168
|
}
|