From 3ea8ae76ae8864166b6a5995e88ab9c4dddfb4ab Mon Sep 17 00:00:00 2001 From: "Brett V. Forsgren" Date: Thu, 19 Dec 2024 16:48:47 -0700 Subject: [PATCH] move package correlator to common location --- .../Model/PackageMapper.cs | 10 ++-- .../Discover/SdkProjectDiscovery.cs | 41 +---------------- .../Updater/PackageReferenceUpdater.cs | 26 +++++++++-- .../DotNetPackageCorrelationManager.cs | 46 +++++++++++++++++++ 4 files changed, 73 insertions(+), 50 deletions(-) create mode 100644 nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs diff --git a/nuget/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs b/nuget/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs index ec2d8676fe..931e560246 100644 --- a/nuget/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs +++ b/nuget/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs @@ -4,11 +4,11 @@ namespace DotNetPackageCorrelation; public class PackageMapper { - private readonly RuntimePackages _runtimePackages; + public RuntimePackages RuntimePackages { get; } private PackageMapper(RuntimePackages runtimePackages) { - _runtimePackages = runtimePackages; + RuntimePackages = runtimePackages; } /// @@ -24,7 +24,7 @@ private PackageMapper(RuntimePackages runtimePackages) return null; } - var candidateRuntimeVersions = _runtimePackages.Runtimes.Keys + var candidateRuntimeVersions = RuntimePackages.Runtimes.Keys .Where(v => v.Major == runtimeVersion.Major) .Where(v => v.ComparePrecedenceTo(runtimeVersion) <= 0) .OrderBy(v => v, SemVerComparer.Instance) @@ -32,7 +32,7 @@ private PackageMapper(RuntimePackages runtimePackages) .ToArray(); foreach (var candidateRuntimeVersion in candidateRuntimeVersions) { - if (!_runtimePackages.Runtimes.TryGetValue(candidateRuntimeVersion, out var packageSet)) + if (!RuntimePackages.Runtimes.TryGetValue(candidateRuntimeVersion, out var packageSet)) { continue; } @@ -49,7 +49,7 @@ private PackageMapper(RuntimePackages runtimePackages) private SemVersion? GetRuntimeVersionFromPackage(string packageName, SemVersion packageVersion) { // TODO: linear search is slow - foreach (var runtime in _runtimePackages.Runtimes) + foreach (var runtime in RuntimePackages.Runtimes) { if (runtime.Value.Packages.TryGetValue(packageName, out var foundPackageVersion) && foundPackageVersion == packageVersion) diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs index 6438296bc5..1d143d006c 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs @@ -1,11 +1,8 @@ using System.Collections.Immutable; using System.Reflection; -using System.Text.Json; using System.Xml.Linq; using System.Xml.XPath; -using DotNetPackageCorrelation; - using Microsoft.Build.Logging.StructuredLogger; using NuGet.Versioning; @@ -20,16 +17,6 @@ namespace NuGetUpdater.Core.Discover; internal static class SdkProjectDiscovery { - private static readonly PackageMapper _packageMapper; - private static readonly Dictionary _packageMapperByOverrideFile = new(); - - static SdkProjectDiscovery() - { - var packageCorrelationPath = Path.Combine(Path.GetDirectoryName(typeof(SdkProjectDiscovery).Assembly.Location)!, "dotnet-package-correlation.json"); - var runtimePackages = LoadRuntimePackagesFromFile(packageCorrelationPath); - _packageMapper = PackageMapper.Load(runtimePackages); - } - private static readonly HashSet TopLevelPackageItemNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "PackageReference" @@ -252,7 +239,7 @@ public static async Task> DiscoverWithBin runtimePackageVersion is not null && SemVersion.TryParse(runtimePackageVersion, out var parsedRuntimePackageVersion)) { - var packageMapper = GetPackageMapper(); + var packageMapper = DotNetPackageCorrelationManager.GetPackageMapper(); var replacementPackageVersion = packageMapper.GetPackageVersionThatShippedWithOtherPackage(runtimePackageName, parsedRuntimePackageVersion, removedPackageName); if (replacementPackageVersion is not null) { @@ -378,32 +365,6 @@ runtimePackageVersion is not null && return projectDiscoveryResults; } - private static PackageMapper GetPackageMapper() - { - var packageCorrelationFileOverride = Environment.GetEnvironmentVariable("DOTNET_PACKAGE_CORRELATION_FILE_PATH"); - if (packageCorrelationFileOverride is not null) - { - // this is used as a test hook to allow unit tests to be SDK agnostic - if (_packageMapperByOverrideFile.TryGetValue(packageCorrelationFileOverride, out var packageMapper)) - { - return packageMapper; - } - - var runtimePackages = LoadRuntimePackagesFromFile(packageCorrelationFileOverride); - packageMapper = PackageMapper.Load(runtimePackages); - _packageMapperByOverrideFile[packageCorrelationFileOverride] = packageMapper; - return packageMapper; - } - - return _packageMapper; - } - - private static RuntimePackages LoadRuntimePackagesFromFile(string filePath) - { - var packageCorrelationJson = File.ReadAllText(filePath); - return JsonSerializer.Deserialize(packageCorrelationJson, Correlator.SerializerOptions)!; - } - private static void ProcessResolvedPackageReference( NamedNode node, Dictionary>> packagesPerProject, // projectPath -> tfm -> (packageName, packageVersion) diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs index 4ad7045fef..cbf7ba7d2a 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs @@ -4,6 +4,8 @@ using NuGet.Versioning; +using NuGetUpdater.Core.Utilities; + namespace NuGetUpdater.Core; /// @@ -36,6 +38,25 @@ public static async Task UpdateDependencyAsync( // Get the set of all top-level dependencies in the current project var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray(); + var isDependencyTopLevel = topLevelDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)); + if (isDependencyTopLevel) + { + var packageMapper = DotNetPackageCorrelationManager.GetPackageMapper(); + // TODO: this is slow + var isSdkReplacementPackage = packageMapper.RuntimePackages.Runtimes.Any(r => + { + return r.Value.Packages.Any(p => dependencyName.Equals(p.Key, StringComparison.Ordinal)); + }); + if (isSdkReplacementPackage) + { + // If we're updating a top level SDK replacement package, the version listed in the project file won't + // necessarily match the resolved version that caused the update because the SDK might have replaced + // the package. To handle this scenario, we pretend the version we're searching for is the actual + // version in the file, not the resolved version. This allows us to keep a strict equality check when + // finding the file to update. + previousDependencyVersion = topLevelDependencies.First(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).Version!; + } + } if (!await DoesDependencyRequireUpdateAsync(repoRootPath, projectPath, tfms, topLevelDependencies, dependencyName, newDependencyVersion, experimentsManager, logger)) { @@ -95,11 +116,6 @@ public static async Task UpdateDependencyWithConflictResolution( var dependenciesToUpdate = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference) }; // update the initial dependency... - if (isDependencyTopLevel) - { - // TODO: is SDK replacement package - previousDependencyVersion = topLevelDependencies.First(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).Version!; - } TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger); // ...and the peer dependencies... diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs new file mode 100644 index 0000000000..6238767410 --- /dev/null +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs @@ -0,0 +1,46 @@ +using System.Text.Json; + +using DotNetPackageCorrelation; + +using NuGetUpdater.Core.Discover; + +namespace NuGetUpdater.Core.Utilities; + +internal static class DotNetPackageCorrelationManager +{ + private static readonly PackageMapper _packageMapper; + private static readonly Dictionary _packageMapperByOverrideFile = new(); + + static DotNetPackageCorrelationManager() + { + var packageCorrelationPath = Path.Combine(Path.GetDirectoryName(typeof(SdkProjectDiscovery).Assembly.Location)!, "dotnet-package-correlation.json"); + var runtimePackages = LoadRuntimePackagesFromFile(packageCorrelationPath); + _packageMapper = PackageMapper.Load(runtimePackages); + } + + public static PackageMapper GetPackageMapper() + { + var packageCorrelationFileOverride = Environment.GetEnvironmentVariable("DOTNET_PACKAGE_CORRELATION_FILE_PATH"); + if (packageCorrelationFileOverride is not null) + { + // this is used as a test hook to allow unit tests to be SDK agnostic + if (_packageMapperByOverrideFile.TryGetValue(packageCorrelationFileOverride, out var packageMapper)) + { + return packageMapper; + } + + var runtimePackages = LoadRuntimePackagesFromFile(packageCorrelationFileOverride); + packageMapper = PackageMapper.Load(runtimePackages); + _packageMapperByOverrideFile[packageCorrelationFileOverride] = packageMapper; + return packageMapper; + } + + return _packageMapper; + } + + private static RuntimePackages LoadRuntimePackagesFromFile(string filePath) + { + var packageCorrelationJson = File.ReadAllText(filePath); + return JsonSerializer.Deserialize(packageCorrelationJson, Correlator.SerializerOptions)!; + } +}