Skip to content

Commit

Permalink
Merge pull request #3260 from gep13/package-resolution-fix
Browse files Browse the repository at this point in the history
(#3237) Reduce number of queries for dependencies
  • Loading branch information
vexx32 authored Jul 11, 2023
2 parents 03c5aaa + 46edab5 commit a3305eb
Showing 1 changed file with 83 additions and 19 deletions.
102 changes: 83 additions & 19 deletions src/chocolatey/infrastructure.app/services/NugetService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,14 +1215,27 @@ public virtual ConcurrentDictionary<string, PackageResult> Upgrade(ChocolateyCon
null,
null));

// For an initial attempt at finding a package resolution solution, check for all parent packages (i.e. locally installed packages
// that take a dependency on the package that is currently being upgraded) and find the depdendencies associated with these packages.
// NOTE: All the latest availble package version, as well as the specifically requested package version (if applicable) will be
// searched for. If this don't provide enough information to obtain a solution, then a follow up query in the catch block for this
// section of the code will be completed.
var parentInfos = new HashSet<SourcePackageDependencyInfo>(PackageIdentityComparer.Default);
NugetCommon.GetPackageParents(availablePackage.Identity.Id, parentInfos, localPackagesDependencyInfos).GetAwaiter().GetResult();
foreach (var parentPackage in parentInfos)
{
foreach (var packageVersion in NugetList.FindAllPackageVersions(parentPackage.Id, config, _nugetLogger, sourceCacheContext, remoteEndpoints))
if (version != null)
{
NugetCommon.GetPackageDependencies(packageVersion.Identity, NuGetFramework.AnyFramework, sourceCacheContext, _nugetLogger, remoteEndpoints, sourcePackageDependencyInfos, sourceDependencyCache, config).GetAwaiter().GetResult();
var requestedPackageDependency = NugetList.FindPackage(parentPackage.Id, config, _nugetLogger, sourceCacheContext, remoteEndpoints, version);

if (requestedPackageDependency != null)
{
NugetCommon.GetPackageDependencies(requestedPackageDependency.Identity, NuGetFramework.AnyFramework, sourceCacheContext, _nugetLogger, remoteEndpoints, sourcePackageDependencyInfos, sourceDependencyCache, config).GetAwaiter().GetResult();
}
}

var availablePackageDependency = NugetList.FindPackage(parentPackage.Id, config, _nugetLogger, sourceCacheContext, remoteEndpoints);
NugetCommon.GetPackageDependencies(availablePackageDependency.Identity, NuGetFramework.AnyFramework, sourceCacheContext, _nugetLogger, remoteEndpoints, sourcePackageDependencyInfos, sourceDependencyCache, config).GetAwaiter().GetResult();
}

var removedSources = RemovePinnedSourceDependencies(sourcePackageDependencyInfos, allLocalPackages);
Expand Down Expand Up @@ -1259,17 +1272,6 @@ public virtual ConcurrentDictionary<string, PackageResult> Upgrade(ChocolateyCon
//var allPackagesIdentities = allLocalPackages.Select(p => p.SearchMetadata.Identity).ToList();
var allPackagesReferences = allPackagesIdentities.Select(p => new PackageReference(p, NuGetFramework.AnyFramework));

var resolverContext = new PackageResolverContext(
dependencyBehavior: DependencyBehavior.Highest,
targetIds: targetIdsToInstall,
requiredPackageIds: allPackagesIdentities.Select(p => p.Id),
packagesConfig: allPackagesReferences,
preferredVersions: allPackagesIdentities,
availablePackages: sourcePackageDependencyInfos,
packageSources: remoteRepositories.Select(s => s.PackageSource),
log: _nugetLogger
);

IEnumerable<SourcePackageDependencyInfo> resolvedPackages = new List<SourcePackageDependencyInfo>();
if (config.IgnoreDependencies)
{
Expand Down Expand Up @@ -1299,6 +1301,17 @@ public virtual ConcurrentDictionary<string, PackageResult> Upgrade(ChocolateyCon
}
else
{
var resolverContext = new PackageResolverContext(
dependencyBehavior: DependencyBehavior.Highest,
targetIds: targetIdsToInstall,
requiredPackageIds: allPackagesIdentities.Select(p => p.Id),
packagesConfig: allPackagesReferences,
preferredVersions: allPackagesIdentities,
availablePackages: sourcePackageDependencyInfos,
packageSources: remoteRepositories.Select(s => s.PackageSource),
log: _nugetLogger
);

try
{
resolvedPackages = dependencyResolver.Resolve(resolverContext, CancellationToken.None)
Expand All @@ -1314,15 +1327,66 @@ public virtual ConcurrentDictionary<string, PackageResult> Upgrade(ChocolateyCon
packagesToUninstall.AddRange(allLocalPackages.Where(p => resolvedPackages.Select(x => x.Id).Contains(p.Name, StringComparer.OrdinalIgnoreCase)));
}
}
catch (NuGetResolverConstraintException ex)
catch (NuGetResolverConstraintException)
{
var logMessage = GetDependencyResolutionErrorMessage(ex);
this.Log().Error(ChocolateyLoggers.Important, logMessage);
this.Log().Warn("Re-attempting package dependency resolution using additional available package information...");

try
{
// If for some reason, it hasn't been possible to find a solution from the resolverContext, it could be that
// we haven't provided enough information about the available package versions in the sourcePackageDependencyInfos
// object. If we get here, assume that this is the case and re-attempt the upgrade, by pulling in ALL the
// dependency information, rather than only the latest package version, and specified package version.

// NOTE: There is duplication of work here, compared to what is done above, but further refactoring of this
// entire method would need to be done in order to make it more usable/maintable going forward. In the
// interim, the duplication is "acceptable" as it is hoped that the need to find ALL package dependencies
// will be the edge case, and not the rule.
foreach (var parentPackage in parentInfos)
{
foreach (var packageVersion in NugetList.FindAllPackageVersions(parentPackage.Id, config, _nugetLogger, sourceCacheContext, remoteEndpoints))
{
NugetCommon.GetPackageDependencies(packageVersion.Identity, NuGetFramework.AnyFramework, sourceCacheContext, _nugetLogger, remoteEndpoints, sourcePackageDependencyInfos, sourceDependencyCache, config).GetAwaiter().GetResult();
}
}

resolverContext = new PackageResolverContext(
dependencyBehavior: DependencyBehavior.Highest,
targetIds: targetIdsToInstall,
requiredPackageIds: allPackagesIdentities.Select(p => p.Id),
packagesConfig: allPackagesReferences,
preferredVersions: allPackagesIdentities,
availablePackages: sourcePackageDependencyInfos,
packageSources: remoteRepositories.Select(s => s.PackageSource),
log: _nugetLogger
);

resolvedPackages = dependencyResolver.Resolve(resolverContext, CancellationToken.None)
.Select(p => sourcePackageDependencyInfos.SingleOrDefault(x => PackageIdentityComparer.Default.Equals(x, p)));

if (!config.ForceDependencies)
{
var identitiesToUninstall = packagesToUninstall.Select(x => x.Identity);
resolvedPackages = resolvedPackages.Where(p => !(localPackagesDependencyInfos.Contains(p) && !identitiesToUninstall.Contains(p)));

foreach (var pkgMetadata in packagesToInstall)
// If forcing dependencies, then dependencies already added to packages to remove.
// If not forcing dependencies, then package needs to be removed so it can be upgraded to the new version required by the parent
packagesToUninstall.AddRange(allLocalPackages.Where(p => resolvedPackages.Select(x => x.Id).Contains(p.Name, StringComparer.OrdinalIgnoreCase)));
}
}
catch (NuGetResolverConstraintException nestedEx)
{
var errorResult = packageResultsToReturn.GetOrAdd(pkgMetadata.Identity.Id, new PackageResult(pkgMetadata, pathResolver.GetInstallPath(pkgMetadata.Identity)));
errorResult.Messages.Add(new ResultMessage(ResultType.Error, logMessage));
// If we get here, both the inital attempt to resolve a solution didn't work, as well as a second
// attempt using all available package versions didn't work, so this time around we hard fail, and
// provide information to the user about the conflicts for the package resolution.
var logMessage = GetDependencyResolutionErrorMessage(nestedEx);
this.Log().Error(ChocolateyLoggers.Important, logMessage);

foreach (var pkgMetadata in packagesToInstall)
{
var errorResult = packageResultsToReturn.GetOrAdd(pkgMetadata.Identity.Id, new PackageResult(pkgMetadata, pathResolver.GetInstallPath(pkgMetadata.Identity)));
errorResult.Messages.Add(new ResultMessage(ResultType.Error, logMessage));
}
}
}
catch (Exception ex)
Expand Down

0 comments on commit a3305eb

Please sign in to comment.