Skip to content

Commit 3e616e8

Browse files
allow exiting from a parent package lookup if no better match was found (#12128)
Co-authored-by: Abhishek <[email protected]>
1 parent 072e729 commit 3e616e8

File tree

2 files changed

+63
-32
lines changed

2 files changed

+63
-32
lines changed

nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,11 +1370,11 @@ await File.WriteAllTextAsync(projectPath, """
13701370
""");
13711371
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory([
13721372
// initial packages
1373-
MockNuGetPackage.CreateSimplePackage("Microsoft.EntityFrameworkCore", "7.0.11", "net8.0", [(null, [("Microsoft.EntityFrameworkCore.Analyzers", "7.0.11"), ("Microsoft.Extensions.Caching.Memory", "7.0.0")])]),
1373+
MockNuGetPackage.CreateSimplePackage("Microsoft.EntityFrameworkCore", "7.0.11", "net8.0", [(null, [("Microsoft.EntityFrameworkCore.Analyzers", "7.0.11"), ("Microsoft.Extensions.Caching.Memory", "[7.0.0]")])]),
13741374
MockNuGetPackage.CreateSimplePackage("Microsoft.EntityFrameworkCore.Analyzers", "7.0.11", "net8.0"),
13751375
MockNuGetPackage.CreateSimplePackage("Microsoft.Extensions.Caching.Memory", "7.0.0", "net8.0"),
13761376
// available packages
1377-
MockNuGetPackage.CreateSimplePackage("Microsoft.EntityFrameworkCore", "8.0.0", "net8.0", [(null, [("Microsoft.EntityFrameworkCore.Analyzers", "8.0.0"), ("Microsoft.Extensions.Caching.Memory", "8.0.0")])]),
1377+
MockNuGetPackage.CreateSimplePackage("Microsoft.EntityFrameworkCore", "8.0.0", "net8.0", [(null, [("Microsoft.EntityFrameworkCore.Analyzers", "8.0.0"), ("Microsoft.Extensions.Caching.Memory", "[8.0.0]")])]),
13781378
MockNuGetPackage.CreateSimplePackage("Microsoft.EntityFrameworkCore.Analyzers", "8.0.0", "net8.0"),
13791379
MockNuGetPackage.CreateSimplePackage("Microsoft.Extensions.Caching.Memory", "8.0.0", "net8.0"),
13801380
], tempDirectory.DirectoryPath);
@@ -1627,16 +1627,13 @@ await UpdateWorkerTestBase.MockNuGetPackagesInDirectory([
16271627
// initial packages
16281628
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.CSharp.Workspaces", "4.8.0", "net8.0", [(null, [("Microsoft.CodeAnalysis.CSharp", "[4.8.0]"), ("Microsoft.CodeAnalysis.Common", "[4.8.0]")])]),
16291629
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.CSharp", "4.8.0", "net8.0", [(null, [("Microsoft.CodeAnalysis.Common", "[4.8.0]")])]),
1630-
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.Common", "4.8.0", "net8.0", [(null, [("System.Collections.Immutable", "7.0.0")])]),
1630+
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.Common", "4.8.0", "net8.0", [(null, [("System.Collections.Immutable", "[7.0.0]")])]),
16311631
MockNuGetPackage.CreateSimplePackage("System.Collections.Immutable", "7.0.0", "net8.0"),
16321632
// available packages
16331633
MockNuGetPackage.CreateSimplePackage("System.Collections.Immutable", "8.0.0", "net8.0"),
16341634
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.CSharp.Workspaces", "4.9.2", "net8.0", [(null, [("Microsoft.CodeAnalysis.CSharp", "[4.9.2]"), ("Microsoft.CodeAnalysis.Common", "[4.9.2]")])]),
16351635
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.CSharp", "4.9.2", "net8.0", [(null, [("Microsoft.CodeAnalysis.Common", "[4.9.2]")])]),
1636-
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.Common", "4.9.2", "net8.0", [(null, [("System.Collections.Immutable", "8.0.0")])]),
1637-
//MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.CSharp.Workspaces", "4.10.0", "net8.0", [(null, [("Microsoft.CodeAnalysis.CSharp", "[4.10.0]"), ("Microsoft.CodeAnalysis.Common", "[4.10.0]")])]),
1638-
//MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.CSharp", "4.10.0", "net8.0", [(null, [("Microsoft.CodeAnalysis.Common", "[4.10.0]")])]),
1639-
//MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.Common", "4.10.0", "net8.0", [(null, [("System.Collections.Immutable", "8.0.0")])])
1636+
MockNuGetPackage.CreateSimplePackage("Microsoft.CodeAnalysis.Common", "4.9.2", "net8.0", [(null, [("System.Collections.Immutable", "[8.0.0]")])]),
16401637
], tempDirectory.DirectoryPath);
16411638

16421639
var dependencies = new[]
@@ -1669,6 +1666,59 @@ await UpdateWorkerTestBase.MockNuGetPackagesInDirectory([
16691666
};
16701667
AssertEx.Equal(expectedResolvedDependencies, actualResolvedDependencies);
16711668
}
1669+
1670+
[Fact(Timeout = 120_000)] // 2m
1671+
public async Task DependencyConflictsCanBeResolved_TopLevelDependencyHasNewerVersionsThatDoNotPullUpTransitive()
1672+
{
1673+
using var tempDirectory = new TemporaryDirectory();
1674+
var projectPath = Path.Join(tempDirectory.DirectoryPath, "project.csproj");
1675+
await File.WriteAllTextAsync(projectPath, """
1676+
<Project Sdk="Microsoft.NET.Sdk">
1677+
<PropertyGroup>
1678+
<TargetFramework>net8.0</TargetFramework>
1679+
</PropertyGroup>
1680+
<ItemGroup>
1681+
<PackageReference Include="Top.Level.Package" Version="1.41.0" />
1682+
</ItemGroup>
1683+
</Project>
1684+
""");
1685+
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory([
1686+
// initial packages
1687+
MockNuGetPackage.CreateSimplePackage("Top.Level.Package", "1.41.0", "net8.0", [(null, [("Transitive.Package", "6.0.0")])]),
1688+
MockNuGetPackage.CreateSimplePackage("Transitive.Package", "6.0.0", "net8.0"),
1689+
// available packages
1690+
MockNuGetPackage.CreateSimplePackage("Top.Level.Package", "1.45.0", "net8.0", [(null, [("Transitive.Package", "6.0.0")])]),
1691+
MockNuGetPackage.CreateSimplePackage("Transitive.Package", "8.0.5", "net8.0"),
1692+
], tempDirectory.DirectoryPath);
1693+
1694+
var dependencies = new[]
1695+
{
1696+
new Dependency("Top.Level.Package", "1.41.0", DependencyType.PackageReference),
1697+
}.ToImmutableArray();
1698+
var update = new[]
1699+
{
1700+
new Dependency("Transitive.Package", "8.0.5", DependencyType.PackageReference),
1701+
}.ToImmutableArray();
1702+
1703+
var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(
1704+
tempDirectory.DirectoryPath,
1705+
projectPath,
1706+
"net8.0",
1707+
dependencies,
1708+
update,
1709+
new ExperimentsManager() { InstallDotnetSdks = true },
1710+
new TestLogger()
1711+
);
1712+
Assert.NotNull(resolvedDependencies);
1713+
var actualResolvedDependencies = resolvedDependencies.Value.Select(d => $"{d.Name}/{d.Version}").ToArray();
1714+
var expectedResolvedDependencies = new[]
1715+
{
1716+
"Top.Level.Package/1.41.0",
1717+
"Transitive.Package/8.0.5",
1718+
};
1719+
AssertEx.Equal(expectedResolvedDependencies, actualResolvedDependencies);
1720+
}
1721+
16721722
#endregion
16731723

16741724
[Theory]

nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -465,19 +465,16 @@ public async Task<string> UpdateVersion(List<PackageToUpdate> existingPackages,
465465
NuGetVersion latestVersion = versions.Where(v => !v.IsPrerelease).Max();
466466

467467
// Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
468-
for (NuGetVersion version = currentVersionParent; version <= latestVersion; version = NextPatch(version, versions))
468+
for (NuGetVersion? version = currentVersionParent; version is not null && version <= latestVersion; version = NextPatch(version, versions))
469469
{
470-
string parentVersion = version.ToString();
471-
parent.NewVersion = parentVersion;
472-
473470
// Check if the parent needs to be updated since the child isn't in the existing package list and the parent can update to a newer version to remove the dependency
474471
List<PackageToUpdate> dependencyListParentTemp = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
475472
PackageToUpdate parentDependencyTemp = dependencyListParentTemp.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
476473

477474
// If the newer package version of the parent has the same version as the parent's previous dependency, update
478475
if (parentDependencyTemp.CurrentVersion == package.CurrentVersion)
479476
{
480-
parent.NewVersion = parentVersion;
477+
parent.NewVersion = version.ToString();
481478
parent.CurrentVersion = null;
482479
await UpdateVersion(existingPackages, parent, targetFramework, projectDirectory, logger);
483480
package.IsSpecific = true;
@@ -534,18 +531,10 @@ public async Task<bool> IsCompatibleAsync(PackageToUpdate parent, PackageToUpdat
534531
}
535532

536533
// Method to update a version to the next available version for a package
537-
public NuGetVersion NextPatch(NuGetVersion version, IEnumerable<NuGetVersion> allVersions)
534+
private static NuGetVersion? NextPatch(NuGetVersion version, IEnumerable<NuGetVersion> allVersions)
538535
{
539-
var versions = allVersions.Where(v => v > version);
540-
541-
if (!versions.Any())
542-
{
543-
// If there are no greater versions, return current version
544-
return version;
545-
}
546-
547-
// Find smallest version in the versions
548-
return versions.Min();
536+
var candidateVersions = allVersions.Where(v => v > version).ToArray();
537+
return candidateVersions.Min();
549538
}
550539

551540
// Method to find a compatible version with the child for the parent to update to
@@ -588,18 +577,10 @@ public async Task<NuGetVersion> FindCompatibleVersionAsync(List<PackageToUpdate>
588577
}
589578

590579
// Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
591-
for (NuGetVersion version = CurrentVersion; version <= latestVersion; version = NextPatch(version, versions))
580+
for (NuGetVersion? version = CurrentVersion; version is not null && version <= latestVersion; version = NextPatch(version, versions))
592581
{
593582
possibleParent.NewVersion = version.ToString();
594583

595-
NuGetVersion nextPatch = NextPatch(version, versions);
596-
597-
// If the next patch is the same as the CurrentVersion, then nothing is needed
598-
if (nextPatch == version)
599-
{
600-
return nextPatch;
601-
}
602-
603584
// Check if there's compatibility with parent and dependency
604585
if (await IsCompatibleAsync(possibleParent, possibleDependency, targetFramework, nugetContext.CurrentDirectory, logger))
605586
{

0 commit comments

Comments
 (0)