From 346d00811c96dd2327a32b6001b7287e0f9091f5 Mon Sep 17 00:00:00 2001 From: Cory Knox Date: Mon, 15 Jul 2024 08:23:17 -0700 Subject: [PATCH] (#3487) Prevent package success on dependency fail When a package dependency fails to install, we will now fail the package as well. --- src/chocolatey/StringResources.cs | 1 + .../services/NugetService.cs | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/chocolatey/StringResources.cs b/src/chocolatey/StringResources.cs index 92950f39fe..e28c11eb75 100644 --- a/src/chocolatey/StringResources.cs +++ b/src/chocolatey/StringResources.cs @@ -62,6 +62,7 @@ public static class ErrorMessages [EditorBrowsable(EditorBrowsableState.Never)] [Browsable(false)] internal const string UnableToDowngrade = "A newer version of {0} (v{1}) is already installed.{2} Use --allow-downgrade or --force to attempt to install older versions."; + internal const string DependencyFailedToInstall = "Failed to install {0} because a previous dependency failed."; } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 568be108e7..03ed7e4fb6 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -578,6 +578,12 @@ public virtual ConcurrentDictionary Install(ChocolateyCon foreach (var packageName in packageNames.OrEmpty()) { + if (packageResultsToReturn.ContainsKey(packageName)) + { + // We have already processed this package (likely during dependency resolution of another package). + continue; + } + // We need to ensure we are using a clean configuration file // before we start reading it. config.RevertChanges(); @@ -793,6 +799,23 @@ Version was specified as '{0}'. It is possible that version foreach (SourcePackageDependencyInfo packageDependencyInfo in resolvedPackages) { + // Don't attempt to action this package if dependencies failed. + if (packageDependencyInfo != null && packageResultsToReturn.Any(r => r.Value.Success != true && packageDependencyInfo.Dependencies.Any(d => d.Id == r.Value.Identity.Id))) + { + var logMessage = StringResources.ErrorMessages.DependencyFailedToInstall.FormatWith(packageDependencyInfo.Id); + var x = new PackageResult(packageDependencyInfo.Id, packageDependencyInfo.Version.ToStringSafe(), string.Empty); + var nullResult = packageResultsToReturn.GetOrAdd(packageDependencyInfo.Id, x); + nullResult.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + this.Log().Error(ChocolateyLoggers.Important, logMessage); + + if (config.Features.StopOnFirstPackageFailure) + { + throw new ApplicationException("Stopping further execution as {0} has failed install.".FormatWith(packageDependencyInfo.Id)); + } + + continue; + } + var packageRemoteMetadata = packagesToInstall.FirstOrDefault(p => p.Identity.Equals(packageDependencyInfo)); if (packageRemoteMetadata is null) @@ -816,6 +839,12 @@ Version was specified as '{0}'. It is possible that version var nullResult = packageResultsToReturn.GetOrAdd(packageToUninstall.Name, packageToUninstall); nullResult.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); this.Log().Error(ChocolateyLoggers.Important, logMessage); + + if (config.Features.StopOnFirstPackageFailure) + { + throw new ApplicationException("Stopping further execution as {0} has failed install.".FormatWith(packageToUninstall.Identity.Id)); + } + continue; } @@ -1061,6 +1090,12 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon foreach (var packageName in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries).OrEmpty()) { + if (packageResultsToReturn.ContainsKey(packageName)) + { + // We have already processed this package (likely during dependency resolution of another package). + continue; + } + // We need to ensure we are using a clean configuration file // before we start reading it. config.RevertChanges(); @@ -1569,6 +1604,23 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon break; } + // Don't attempt to action this package if dependencies failed. + if (packageResultsToReturn.Any(r => r.Value.Success != true && packageDependencyInfo.Dependencies.Any(d => d.Id == r.Value.Identity.Id))) + { + var logMessage = StringResources.ErrorMessages.DependencyFailedToInstall.FormatWith(packageDependencyInfo.Id, packageDependencyInfo.Version); + var x = new PackageResult(packageDependencyInfo.Id, packageDependencyInfo.Version.ToStringSafe(), string.Empty); + var nullResult = packageResultsToReturn.GetOrAdd(packageDependencyInfo.Id, x); + nullResult.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + this.Log().Error(ChocolateyLoggers.Important, logMessage); + + if (config.Features.StopOnFirstPackageFailure) + { + throw new ApplicationException("Stopping further execution as {0} has failed.".FormatWith(packageDependencyInfo.Id)); + } + + continue; + } + var packageRemoteMetadata = packagesToInstall.FirstOrDefault(p => p.Identity.Equals(packageDependencyInfo)); if (packageRemoteMetadata is null) @@ -1593,9 +1645,25 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon var nullResult = packageResultsToReturn.GetOrAdd(packageToUninstall.Name, packageToUninstall); nullResult.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); this.Log().Error(ChocolateyLoggers.Important, logMessage); + + if (config.Features.StopOnFirstPackageFailure) + { + throw new ApplicationException("Stopping further execution as {0} has failed.".FormatWith(packageDependencyInfo.Id)); + } + continue; } + // Package was previously marked inconclusive (not upgraded). We need remove the message since we are now upgrading it. + // Packages that are inconclusive but successfull are not labeled as successful at the end of the run. + var checkResult = packageResultsToReturn.GetOrAdd(packageToUninstall.Name, packageToUninstall); + + while (checkResult.Inconclusive) + { + checkResult.Messages.Remove(checkResult.Messages.FirstOrDefault((m) => m.MessageType == ResultType.Inconclusive)); + } + + var oldPkgInfo = _packageInfoService.Get(packageToUninstall.PackageMetadata); BackupAndRunBeforeModify(packageToUninstall, oldPkgInfo, config, beforeUpgradeAction);