NuGet / Home

Repo for NuGet Client issues
Other
1.5k stars 253 forks source link

New dependency resolver does not properly handle floating prerelease versions #13833

Open jeffkl opened 15 hours ago

jeffkl commented 15 hours ago

NuGet Product Used

dotnet.exe, MSBuild.exe

Product Version

6.12

Worked before?

No response

Impact

I'm unable to use this version

Repro Steps & Context

When you have package versions in your graph like 1.0.0-preview.* and 1.0.0-preview.1, the new dependency resolver does not properly handle it. This is because it currently only compares the version ranges and considers those to be identical. When a floating version is used, the resolver should be comparing the resolved versions instead.

Verbose Logs

No response

jeffkl commented 15 hours ago

This test shows the broken behavior:

// P1 -> P2 -> B 2.0.0-preview.*
// P1 -> A -> B 2.0.0-preview.1
[Fact] // TODO: REMOVE
public async Task RestoreCommand_WithPrereleaseTransitiveVersionWithFullyMatchedReleaseLabel_VerifiesEquivalency()
{
    // Arrange
    using var pathContext = new SimpleTestPathContext();

    var versionRange = VersionRange.Parse("2.0.0-preview.*");

    await SimpleTestPackageUtility.CreatePackagesAsync(
        pathContext.PackageSource,
        new SimpleTestPackageContext("a", "1.0.0")
        {
            Dependencies = [new SimpleTestPackageContext("b", "2.0.0-preview.1")],
        },
        new SimpleTestPackageContext("b", "2.0.0-preview.2")
        );

    // Setup project
    var spec1 = @"
        {
            ""frameworks"": {
            ""net472"": {
                ""dependencies"": {
                    ""a"": {
                        ""version"": ""[1.0.0,)"",
                        ""target"": ""Package"",
                    },
                }
            }
            }
        }";

    var spec2 = @"
        {
            ""frameworks"": {
            ""net472"": {
                ""dependencies"": {
                    ""b"": {
                        ""version"": """ + versionRange.ToString() + @""",
                        ""target"": ""Package"",
                    },
                }
            }
            }
        }";

    // Setup project
    var project1 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project1", pathContext.SolutionRoot, spec1);
    var project2 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project2", pathContext.SolutionRoot, spec2);
    project1 = project1.WithTestProjectReference(project2);

    // Act & Assert
    (var result, _) = await ValidateRestoreAlgorithmEquivalency(pathContext, project1, project2);
    result.Success.Should().BeTrue();
    result.LockFile.Targets.Should().HaveCount(1);
    result.LockFile.Targets[0].Libraries.Should().HaveCount(3);
    result.LockFile.Targets[0].Libraries[0].Name.Should().Be("a");
    result.LockFile.Targets[0].Libraries[0].Version.Should().Be(new NuGetVersion("1.0.0"));
    result.LockFile.Targets[0].Libraries[1].Name.Should().Be("b");
    result.LockFile.Targets[0].Libraries[1].Version.Should().Be(new NuGetVersion("2.0.0-preview.2"));
    result.LockFile.Targets[0].Libraries[2].Name.Should().Be("Project2");
    result.LockFile.Targets[0].Libraries[2].Version.Should().Be(new NuGetVersion("1.0.0"));
}

And this is a proposed fix I was working on:

ersionRange nvr = currentRef.LibraryRange.VersionRange ?? VersionRange.All;
VersionRange ovr = chosenRef.LibraryRange.VersionRange ?? VersionRange.All;

// First determine if the version range is greater than or equal to the chosen version range.
bool isGreaterThanOrEqual = RemoteDependencyWalker.IsGreaterThanOrEqualTo(ovr, nvr);

// If either version range is floating and the version range is considered equal, we need to compare the resolved version instead
if (isGreaterThanOrEqual && (nvr.IsFloating || ovr.IsFloating) && resolvedItemTask != null)
{
    FindLibraryEntryResult resolvedItem = await resolvedItemTask;

    NuGetVersion nearVersion = refItemResult.Item.Key.Version;
    NuGetVersion farVersion = resolvedItem.Item.Key.Version;

    isGreaterThanOrEqual = farVersion >= nearVersion;
}