diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolverEnvironment.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolverEnvironment.cs new file mode 100644 index 00000000000..7d47ff271ff --- /dev/null +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolverEnvironment.cs @@ -0,0 +1,12 @@ +namespace NuGetUpdater.Core.Test; + +/// +/// Prepares the environment to use the new dependency solver. +/// +public class DependencySolverEnvironment : TemporaryEnvironment +{ + public DependencySolverEnvironment(bool useDependencySolver = true) + : base([("UseNewNugetPackageResolver", useDependencySolver ? "true" : "false")]) + { + } +} diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs index e3bc939c89e..20bc39ebf51 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs @@ -54,12 +54,12 @@ await TestUpdateForProject("Some.Package", "9.0.1", "13.0.1", } [Theory] - [InlineData("true")] - [InlineData(null)] - public async Task UpdateVersionChildElement_InProjectFile_ForPackageReferenceIncludeTheory(string variableValue) + [InlineData(true)] + [InlineData(false)] + public async Task UpdateVersionChildElement_InProjectFile_ForPackageReferenceIncludeTheory(bool useDependencySolver) { // update Some.Package from 9.0.1 to 13.0.1 - using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]); + using var _ = new DependencySolverEnvironment(useDependencySolver); await TestUpdateForProject("Some.Package", "9.0.1", "13.0.1", packages: [ @@ -95,11 +95,72 @@ await TestUpdateForProject("Some.Package", "9.0.1", "13.0.1", ); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task PeerDependenciesAreUpdatedEvenWhenNotExplicit(bool useDependencySolver) + { + using var _ = new DependencySolverEnvironment(useDependencySolver); + await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0", + packages: + [ + MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0", [(null, [("Transitive.Package", "[1.0.0]")])]), + MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0", [(null, [("Transitive.Package", "[2.0.0]")])]), + MockNuGetPackage.CreateSimplePackage("Transitive.Package", "1.0.0", "net8.0"), + MockNuGetPackage.CreateSimplePackage("Transitive.Package", "2.0.0", "net8.0"), + ], + projectFile: ("a/a.csproj", """ + + + net8.0 + true + + + + + + """), + additionalFiles: + [ + ("Directory.Packages.props", """ + + + + + + + """) + ], + expectedProjectContents: """ + + + net8.0 + true + + + + + + """, + additionalFilesExpected: + [ + ("Directory.Packages.props", """ + + + + + + + """) + ] + ); + } + [Fact] public async Task CallingResolveDependencyConflictsNew() { // update Microsoft.CodeAnalysis.Common from 4.9.2 to 4.10.0 - using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", "true")]); + using var _ = new DependencySolverEnvironment(); await TestUpdateForProject("Microsoft.CodeAnalysis.Common", "4.9.2", "4.10.0", // initial projectContents: $""" @@ -535,11 +596,11 @@ await TestUpdateForProject("Some.Package", "9.0.1", "13.0.1", } [Theory] - [InlineData(null)] - [InlineData("true")] - public async Task AddPackageReference_InProjectFile_ForTransientDependency(string variableValue) + [InlineData(true)] + [InlineData(false)] + public async Task AddPackageReference_InProjectFile_ForTransientDependency(bool useDependencySolver) { - using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]); + using var _ = new DependencySolverEnvironment(useDependencySolver); // add transient package Some.Transient.Dependency from 5.0.1 to 5.0.2 await TestUpdateForProject("Some.Transient.Dependency", "5.0.1", "5.0.2", isTransitive: true, packages: @@ -2914,12 +2975,51 @@ await TestUpdateForProject("Some.Package", "12.0.1", "13.0.1", ); } + [Fact] + public async Task UpdatingTransitiveDependencyWithNewSolverCanUpdateJustTheTopLevelPackage() + { + // we've been asked to explicitly update a transitive dependency, but we can solve it by updating the top-level package instead + using var _ = new DependencySolverEnvironment(); + await TestUpdateForProject("Transitive.Package", "1.0.0", "2.0.0", + isTransitive: true, + packages: + [ + MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[1.0.0]")])]), + MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[2.0.0]")])]), + MockNuGetPackage.CreateSimplePackage("Transitive.Package", "1.0.0", "net8.0"), + MockNuGetPackage.CreateSimplePackage("Transitive.Package", "2.0.0", "net8.0"), + ], + projectContents: """ + + + net8.0 + false + + + + + + """, + expectedProjectContents: """ + + + net8.0 + false + + + + + + """ + ); + } + [Theory] - [InlineData("true")] - [InlineData(null)] - public async Task NoChange_IfThereAreIncoherentVersions(string variableValue) + [InlineData(true)] + [InlineData(false)] + public async Task NoChange_IfThereAreIncoherentVersions(bool useDependencySolver) { - using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]); + using var _ = new DependencySolverEnvironment(useDependencySolver); // trying to update `Transitive.Dependency` to 1.1.0 would normally pull `Some.Package` from 1.0.0 to 1.1.0, // but the TFM doesn't allow it @@ -3005,11 +3105,11 @@ await TestUpdateForProject("Some.Package", "7.0.1", "13.0.1", } [Theory] - [InlineData("true")] - [InlineData(null)] - public async Task UnresolvablePropertyDoesNotStopOtherUpdates(string variableValue) + [InlineData(true)] + [InlineData(false)] + public async Task UnresolvablePropertyDoesNotStopOtherUpdates(bool useDependencySolver) { - using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]); + using var _ = new DependencySolverEnvironment(useDependencySolver); // the property `$(SomeUnresolvableProperty)` cannot be resolved await TestUpdateForProject("Some.Package", "7.0.1", "13.0.1", @@ -3045,11 +3145,11 @@ await TestUpdateForProject("Some.Package", "7.0.1", "13.0.1", } [Theory] - [InlineData("true")] - [InlineData(null)] - public async Task UpdatingPackageAlsoUpdatesAnythingWithADependencyOnTheUpdatedPackage(string variableValue) + [InlineData(true)] + [InlineData(false)] + public async Task UpdatingPackageAlsoUpdatesAnythingWithADependencyOnTheUpdatedPackage(bool useDependencySolver) { - using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]); + using var _ = new DependencySolverEnvironment(useDependencySolver); // updating Some.Package from 3.3.30 requires that Some.Package.Extensions also be updated await TestUpdateForProject("Some.Package", "3.3.30", "3.4.3", diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs index 337cc4f3fa9..c419bf9faba 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs @@ -442,9 +442,9 @@ [new Dependency("Package.A", "1.0.0", DependencyType.Unknown)] } [Fact] - public async Task DependencyConflictsCanBeResolved() + public async Task DependencyConflictsCanBeResolvedWithBruteForce() { - var repoRoot = Directory.CreateTempSubdirectory($"test_{nameof(DependencyConflictsCanBeResolved)}_"); + var repoRoot = Directory.CreateTempSubdirectory($"test_{nameof(DependencyConflictsCanBeResolvedWithBruteForce)}_"); MockNuGetPackage[] testPackages = [ // some base packages @@ -483,7 +483,7 @@ await File.WriteAllTextAsync(projectPath, """ { new Dependency("Some.Other.Package", "1.2.0", DependencyType.PackageReference), }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRoot.FullName, projectPath, "net8.0", dependencies, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(2, resolvedDependencies.Length); Assert.Equal("Some.Package", resolvedDependencies[0].Name); @@ -533,7 +533,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("CS-Script.Core", "2.0.0", DependencyType.PackageReference), }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(3, resolvedDependencies.Length); Assert.Equal("CS-Script.Core", resolvedDependencies[0].Name); @@ -578,7 +578,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Microsoft.Bcl.AsyncInterfaces", "1.1.1", DependencyType.Unknown) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Single(resolvedDependencies); Assert.Equal("Azure.Core", resolvedDependencies[0].Name); @@ -621,7 +621,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Newtonsoft.Json", "13.0.1", DependencyType.Unknown) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(2, resolvedDependencies.Length); Assert.Equal("Newtonsoft.Json.Bson", resolvedDependencies[0].Name); @@ -671,7 +671,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Microsoft.CodeAnalysis.Common", "4.10.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(3, resolvedDependencies.Length); Assert.Equal("Microsoft.CodeAnalysis.Compilers", resolvedDependencies[0].Name); @@ -723,7 +723,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Microsoft.CodeAnalysis.Common", "4.10.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(4, resolvedDependencies.Length); Assert.Equal("Microsoft.CodeAnalysis.Compilers", resolvedDependencies[0].Name); @@ -779,7 +779,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Newtonsoft.Json", "13.0.1", DependencyType.Unknown) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(5, resolvedDependencies.Length); Assert.Equal("Microsoft.CodeAnalysis.Compilers", resolvedDependencies[0].Name); @@ -838,7 +838,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Buildalyzer", "7.0.1", DependencyType.PackageReference), }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(4, resolvedDependencies.Length); Assert.Equal("Buildalyzer", resolvedDependencies[0].Name); @@ -895,7 +895,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Azure.Core", "1.22.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(4, resolvedDependencies.Length); Assert.Equal("System.Collections.Immutable", resolvedDependencies[0].Name); @@ -952,7 +952,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Azure.Core", "1.22.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(5, resolvedDependencies.Length); Assert.Equal("System.Collections.Immutable", resolvedDependencies[0].Name); @@ -1007,7 +1007,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("AutoMapper.Collection", "10.0.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(3, resolvedDependencies.Length); Assert.Equal("AutoMapper.Extensions.Microsoft.DependencyInjection", resolvedDependencies[0].Name); @@ -1054,7 +1054,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Microsoft.Extensions.Caching.Memory", "8.0.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(2, resolvedDependencies.Length); Assert.Equal("Microsoft.EntityFrameworkCore", resolvedDependencies[0].Name); @@ -1104,7 +1104,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Microsoft.EntityFrameworkCore.Analyzers", "8.0.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(4, resolvedDependencies.Length); Assert.Equal("Microsoft.EntityFrameworkCore.Design", resolvedDependencies[0].Name); @@ -1156,7 +1156,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("Microsoft.EntityFrameworkCore.Analyzers", "8.0.0", DependencyType.PackageReference) }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(3, resolvedDependencies.Length); Assert.Equal("Microsoft.EntityFrameworkCore.Design", resolvedDependencies[0].Name); @@ -1208,7 +1208,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("System.Collections.Immutable", "8.0.0", DependencyType.PackageReference), }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(4, resolvedDependencies.Length); Assert.Equal("System.Collections.Immutable", resolvedDependencies[0].Name); @@ -1260,7 +1260,7 @@ await File.WriteAllTextAsync(projectPath, """ new Dependency("System.Collections.Immutable", "8.0.0", DependencyType.PackageReference), }; - var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger()); Assert.NotNull(resolvedDependencies); Assert.Equal(3, resolvedDependencies.Length); Assert.Equal("Microsoft.CodeAnalysis.CSharp.Workspaces", resolvedDependencies[0].Name); diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs index 7513e075ad3..b3da76eaaae 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs @@ -30,19 +30,26 @@ public static async Task UpdateDependencyAsync( return; } - if (isTransitive) + var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, logger); + if (MSBuildHelper.UseNewDependencySolver()) { - await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger); + await UpdateDependencyWithConflictResolution(repoRootPath, buildFiles, tfms, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, peerDependencies, logger); } else { - var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, logger); - if (peerDependencies is null) + if (isTransitive) { - return; + await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger); } + else + { + if (peerDependencies is null) + { + return; + } - await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger); + await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger); + } } if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, logger, buildFiles, tfms)) @@ -53,6 +60,61 @@ public static async Task UpdateDependencyAsync( await SaveBuildFilesAsync(buildFiles, logger); } + public static async Task UpdateDependencyWithConflictResolution( + string repoRootPath, + ImmutableArray buildFiles, + string[] targetFrameworks, + string projectPath, + string dependencyName, + string previousDependencyVersion, + string newDependencyVersion, + bool isTransitive, + IDictionary peerDependencies, + ILogger logger) + { + var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray(); + var isDependencyTopLevel = topLevelDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)); + var dependenciesToUpdate = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference) }; + + // update the initial dependency... + TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger); + + // ...and the peer dependencies... + foreach (var (packageName, packageVersion) in peerDependencies.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0)) + { + TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger); + } + + // ...and everything else + foreach (var projectFile in buildFiles) + { + foreach (var tfm in targetFrameworks) + { + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, topLevelDependencies, dependenciesToUpdate, logger); + if (resolvedDependencies is null) + { + logger.Log($" Unable to resolve dependency conflicts for {projectFile.Path}."); + continue; + } + + var isDependencyInResolutionSet = resolvedDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)); + if (isTransitive && !isDependencyTopLevel && isDependencyInResolutionSet) + { + // a transitive dependency had to be pinned; add it here + await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger); + } + + // update all resolved dependencies that aren't the initial dependency + foreach (var resolvedDependency in resolvedDependencies + .Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)) + .Where(d => d.Version is not null)) + { + TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger); + } + } + } + } + /// /// Verifies that the package does not already satisfy the requested dependency version. /// @@ -307,7 +369,7 @@ private static async Task UpdateTopLevelDepdendency( IDictionary peerDependencies, ILogger logger) { - + // update dependencies... var result = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger); if (result == UpdateResult.NotFound) { @@ -320,26 +382,13 @@ private static async Task UpdateTopLevelDepdendency( TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger); } - // now make all dependency requirements coherent + // ...and make them all coherent Dependency[] updatedTopLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray(); foreach (ProjectBuildFile projectFile in buildFiles) { foreach (string tfm in targetFrameworks) { - if (MSBuildHelper.UseNewDependencySolver()) - { - // Find the index of the dependency we are updating and revert it to the previous version - int dependencyIndex = Array.FindIndex(updatedTopLevelDependencies, d => string.Equals(d.Name, dependencyName, StringComparison.OrdinalIgnoreCase)); - if (dependencyIndex != -1) - { - var originalDependency = updatedTopLevelDependencies[dependencyIndex]; - updatedTopLevelDependencies[dependencyIndex] = originalDependency with { Version = previousDependencyVersion }; - } - - } - Dependency[] update = [new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference)]; - Dependency[]? resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, updatedTopLevelDependencies, update, logger); - + var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRootPath, projectFile.Path, tfm, updatedTopLevelDependencies, logger); if (resolvedDependencies is null) { logger.Log($" Unable to resolve dependency conflicts for {projectFile.Path}."); @@ -360,7 +409,7 @@ private static async Task UpdateTopLevelDepdendency( continue; } - // update all dependencies + // update all versions foreach (Dependency resolvedDependency in resolvedDependencies .Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)) .Where(d => d.Version is not null)) diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs index d1998d925c7..2d65c39aba6 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs @@ -341,18 +341,6 @@ internal static bool UseNewDependencySolver() } internal static async Task ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, ILogger logger) - { - if (UseNewDependencySolver()) - { - return await ResolveDependencyConflictsNew(repoRoot, projectPath, targetFramework, packages, update, logger); - } - else - { - return await ResolveDependencyConflictsOld(repoRoot, projectPath, targetFramework, packages, logger); - } - } - - internal static async Task ResolveDependencyConflictsNew(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, ILogger logger) { var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_"); PackageManager packageManager = new PackageManager(repoRoot, projectPath); @@ -510,7 +498,7 @@ internal static bool UseNewDependencySolver() } } - internal static async Task ResolveDependencyConflictsOld(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ILogger logger) + internal static async Task ResolveDependencyConflictsWithBruteForce(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ILogger logger) { var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_"); try