From 29e8f04f21c8dd600db6b8feaf492b741de821e2 Mon Sep 17 00:00:00 2001 From: Rain Sallow Date: Mon, 27 Feb 2023 16:52:48 -0500 Subject: [PATCH 1/3] (#1092) Update ISourceRunner install_run This addition is necessary, as many occasions may arise where installing one package involves updating an already-installed dependency. In these cases we should still be running a beforeModify script if it's present, and this facilitates that. (cherry picked from commit 3a30cbc4b6982df8f06f752ce72fc5523221538f) --- .../services/ChocolateyPackageServiceSpecs.cs | 10 +++++----- .../services/ChocolateyPackageService.cs | 3 ++- .../infrastructure.app/services/CygwinService.cs | 5 +++++ .../infrastructure.app/services/ISourceRunner.cs | 11 ++++++++++- .../infrastructure.app/services/NugetService.cs | 7 ++++++- .../infrastructure.app/services/PythonService.cs | 5 +++++ .../infrastructure.app/services/RubyGemsService.cs | 5 +++++ .../services/WindowsFeatureService.cs | 5 +++++ 8 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/chocolatey.tests/infrastructure.app/services/ChocolateyPackageServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/ChocolateyPackageServiceSpecs.cs index e48450c9b8..09e1637315 100644 --- a/src/chocolatey.tests/infrastructure.app/services/ChocolateyPackageServiceSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/services/ChocolateyPackageServiceSpecs.cs @@ -90,9 +90,9 @@ public override void Context() var expectedResult = new ConcurrentDictionary(); expectedResult.TryAdd("test-feature", new PackageResult(package.Object, "windowsfeatures", null)); - FeaturesRunner.Setup(r => r.install_run(It.IsAny(), It.IsAny>())) + FeaturesRunner.Setup(r => r.install_run(It.IsAny(), It.IsAny>(), It.IsAny>())) .Returns(expectedResult); - NormalRunner.Setup(r => r.install_run(It.IsAny(), It.IsAny>())) + NormalRunner.Setup(r => r.install_run(It.IsAny(), It.IsAny>(), It.IsAny>())) .Returns(new ConcurrentDictionary()); SourceRunners.AddRange(new[] { NormalRunner.Object, FeaturesRunner.Object }); @@ -127,20 +127,20 @@ public void should_return_package_that_should_have_been_installed() [Test] public void should_have_called_runner_for_windows_features_source() { - FeaturesRunner.Verify(r => r.install_run(It.Is(c => c.PackageNames == "test-feature"), It.IsAny>()), Times.Once); + FeaturesRunner.Verify(r => r.install_run(It.Is(c => c.PackageNames == "test-feature"), It.IsAny>(), It.IsAny>()), Times.Once); } [Test] public void should_not_have_called_runner_for_windows_features_source_with_other_package_names() { - FeaturesRunner.Verify(r => r.install_run(It.Is(c => c.PackageNames != "test-feature"), It.IsAny>()), Times.Never); + FeaturesRunner.Verify(r => r.install_run(It.Is(c => c.PackageNames != "test-feature"), It.IsAny>(), It.IsAny>()), Times.Never); } [Test] public void should_not_have_called_normal_source_runner_for_non_empty_packages() { // The normal source runners will be called with an argument - NormalRunner.Verify(r => r.install_run(It.Is(c => c.PackageNames != string.Empty), It.IsAny>()), Times.Never); + NormalRunner.Verify(r => r.install_run(It.Is(c => c.PackageNames != string.Empty), It.IsAny>(), It.IsAny>()), Times.Never); } } diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index 5817c556a9..0f6cacdd87 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -613,7 +613,8 @@ public virtual ConcurrentDictionary install_run(Chocolate action = (packageResult, configuration) => handle_package_result(packageResult, configuration, CommandNameType.install); } - var results = perform_source_runner_function(packageConfig, r => r.install_run(packageConfig, action)); + var beforeModifyAction = new Action((packageResult, configuration) => before_package_modify(packageResult, configuration)); + var results = perform_source_runner_function(packageConfig, r => r.install_run(packageConfig, action, beforeModifyAction)); foreach (var result in results) { diff --git a/src/chocolatey/infrastructure.app/services/CygwinService.cs b/src/chocolatey/infrastructure.app/services/CygwinService.cs index f4ddb5b50e..92c11effbc 100644 --- a/src/chocolatey/infrastructure.app/services/CygwinService.cs +++ b/src/chocolatey/infrastructure.app/services/CygwinService.cs @@ -234,6 +234,11 @@ public void install_noop(ChocolateyConfiguration config, Action install_run(ChocolateyConfiguration config, Action continueAction) + { + return install_run(config, continueAction, beforeModifyAction: null); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction, Action beforeModifyAction) { var args = build_args(config, _installArguments); var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); diff --git a/src/chocolatey/infrastructure.app/services/ISourceRunner.cs b/src/chocolatey/infrastructure.app/services/ISourceRunner.cs index dc3a19fdb8..fb90f5e7bc 100644 --- a/src/chocolatey/infrastructure.app/services/ISourceRunner.cs +++ b/src/chocolatey/infrastructure.app/services/ISourceRunner.cs @@ -77,6 +77,15 @@ public interface ISourceRunner /// results of installs ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction); + /// + /// Installs packages from the source feed + /// + /// The configuration. + /// The action to continue with when install is successful. + /// The action (if any) to run on any currently installed package dependencies before triggering the install or updating those dependencies. + /// results of installs + ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction, Action beforeModifyAction); + /// /// Run upgrade in noop mode /// @@ -89,7 +98,7 @@ public interface ISourceRunner /// /// The configuration. /// The action to continue with when upgrade is successful. - /// The action (if any) to run on any currently installed package before triggering the upgrade. + /// The action (if any) to run on any currently installed package or its dependencies before triggering the upgrade. /// results of installs ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction, Action beforeUpgradeAction = null); diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index cb283f0041..4525d2290a 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -457,6 +457,11 @@ public void install_noop(ChocolateyConfiguration config, Action install_run(ChocolateyConfiguration config, Action continueAction) + { + return install_run(config, continueAction, beforeModifyAction: null); + } + + public virtual ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction, Action beforeModifyAction) { _fileSystem.create_directory_if_not_exists(ApplicationParameters.PackagesLocation); var packageResultsToReturn = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); @@ -1013,7 +1018,7 @@ public virtual ConcurrentDictionary upgrade_run(Chocolate } else { - var installResults = install_run(config, continueAction); + var installResults = install_run(config, continueAction, beforeUpgradeAction); foreach (var result in installResults) { packageResultsToReturn.GetOrAdd(result.Key, result.Value); diff --git a/src/chocolatey/infrastructure.app/services/PythonService.cs b/src/chocolatey/infrastructure.app/services/PythonService.cs index 5bec1e15cc..9286981148 100644 --- a/src/chocolatey/infrastructure.app/services/PythonService.cs +++ b/src/chocolatey/infrastructure.app/services/PythonService.cs @@ -337,6 +337,11 @@ public void install_noop(ChocolateyConfiguration config, Action install_run(ChocolateyConfiguration config, Action continueAction) + { + return install_run(config, continueAction, beforeModifyAction: null); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction, Action beforeModifyAction) { set_executable_path_if_not_set(); var args = build_args(config, _installArguments); diff --git a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs index 67aeac78a9..212fe8f7dc 100644 --- a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs +++ b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs @@ -196,6 +196,11 @@ public void install_noop(ChocolateyConfiguration config, Action install_run(ChocolateyConfiguration config, Action continueAction) + { + return install_run(config, continueAction, beforeModifyAction: null); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction, Action beforeModifyAction) { var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); diff --git a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs index 1b8e91fe5a..2700e757fa 100644 --- a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs +++ b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs @@ -258,6 +258,11 @@ public void install_noop(ChocolateyConfiguration config, Action install_run(ChocolateyConfiguration config, Action continueAction) + { + return install_run(config, continueAction, beforeModifyAction: null); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction, Action beforeModifyAction) { set_executable_path_if_not_set(); var args = build_args(config, _installArguments); From e9be55bfeb2e634b65cbe1936f3d0122ab815194 Mon Sep 17 00:00:00 2001 From: Rain Sallow Date: Fri, 10 Mar 2023 16:51:12 -0500 Subject: [PATCH 2/3] (#1092) Add tests for dependencies' beforeModify This test added ensures that when we are upgrading a package, both its own and any dependencies' beforeModify are run so that we can properly update dependencies which might need to shut down a long-running service before being upgraded. (cherry picked from commit 49cc8d7e2cd4e03d02854b7cdf81acb9fe9f7015) # Conflicts: # src/chocolatey.tests.integration/chocolatey.tests.integration.csproj # src/chocolatey.tests.integration/scenarios/InstallScenarios.cs # src/chocolatey.tests.integration/scenarios/UninstallScenarios.cs # src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs --- .../hasdependencywithbeforemodify.nuspec | 22 +++ .../1.0.0/tools/chocolateyBeforeModify.ps1 | 1 + .../1.0.0/tools/chocolateyinstall.ps1 | 1 + .../1.0.0/tools/chocolateyuninstall.ps1 | 1 + .../1.0.0/tools/purpose.txt | 1 + .../hasdependencywithbeforemodify.nuspec | 22 +++ .../2.0.0/tools/chocolateyBeforeModify.ps1 | 1 + .../2.0.0/tools/chocolateyinstall.ps1 | 1 + .../2.0.0/tools/chocolateyuninstall.ps1 | 1 + .../2.0.0/tools/purpose.txt | 1 + .../1.0.0/isdependencywithbeforemodify.nuspec | 18 ++ .../1.0.0/tools/chocolateyBeforeModify.ps1 | 1 + .../1.0.0/tools/chocolateyinstall.ps1 | 1 + .../1.0.0/tools/chocolateyuninstall.ps1 | 1 + .../1.0.0/tools/purpose.txt | 1 + .../2.0.0/isdependencywithbeforemodify.nuspec | 18 ++ .../2.0.0/tools/chocolateyBeforeModify.ps1 | 1 + .../2.0.0/tools/chocolateyinstall.ps1 | 1 + .../2.0.0/tools/chocolateyuninstall.ps1 | 1 + .../2.0.0/tools/purpose.txt | 1 + .../scenarios/InstallScenarios.cs | 99 +++++++++++ .../scenarios/UninstallScenarios.cs | 88 ++++++++++ .../scenarios/UpgradeScenarios.cs | 164 ++++++++++++++++++ 23 files changed, 447 insertions(+) create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/hasdependencywithbeforemodify.nuspec create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/purpose.txt create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/hasdependencywithbeforemodify.nuspec create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/purpose.txt create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/isdependencywithbeforemodify.nuspec create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/purpose.txt create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/isdependencywithbeforemodify.nuspec create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 create mode 100644 src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/purpose.txt diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/hasdependencywithbeforemodify.nuspec b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/hasdependencywithbeforemodify.nuspec new file mode 100644 index 0000000000..b458c99e13 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/hasdependencywithbeforemodify.nuspec @@ -0,0 +1,22 @@ + + + + hasdependencywithbeforemodify + 1.0.0 + hasdependencywithbeforemodify + __REPLACE_AUTHORS_OF_SOFTWARE__ + __REPLACE_YOUR_NAME__ + false + __REPLACE__ + __REPLACE__ + + + hasdependencywithbeforemodify admin + + + + + + + + \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 new file mode 100644 index 0000000000..d7bc904e0f --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 @@ -0,0 +1 @@ +Write-Output "Ran BeforeModify: $env:PackageName $env:PackageVersion" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 new file mode 100644 index 0000000000..d64eb8f47b --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Installed" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 new file mode 100644 index 0000000000..9ead91ffa3 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Uninstalled" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/purpose.txt b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/purpose.txt new file mode 100644 index 0000000000..5032f1e436 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/1.0.0/tools/purpose.txt @@ -0,0 +1 @@ +This package is used to test out upgrade behaviour with beforeModify scripts in play for the main package and dependencies. \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/hasdependencywithbeforemodify.nuspec b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/hasdependencywithbeforemodify.nuspec new file mode 100644 index 0000000000..ad40c22bc8 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/hasdependencywithbeforemodify.nuspec @@ -0,0 +1,22 @@ + + + + hasdependencywithbeforemodify + 2.0.0 + hasdependencywithbeforemodify + __REPLACE_AUTHORS_OF_SOFTWARE__ + __REPLACE_YOUR_NAME__ + false + __REPLACE__ + __REPLACE__ + + + hasdependencywithbeforemodify admin + + + + + + + + \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 new file mode 100644 index 0000000000..d7bc904e0f --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 @@ -0,0 +1 @@ +Write-Output "Ran BeforeModify: $env:PackageName $env:PackageVersion" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 new file mode 100644 index 0000000000..d64eb8f47b --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Installed" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 new file mode 100644 index 0000000000..9ead91ffa3 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Uninstalled" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/purpose.txt b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/purpose.txt new file mode 100644 index 0000000000..5032f1e436 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/hasdependencywithbeforemodify/2.0.0/tools/purpose.txt @@ -0,0 +1 @@ +This package is used to test out upgrade behaviour with beforeModify scripts in play for the main package and dependencies. \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/isdependencywithbeforemodify.nuspec b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/isdependencywithbeforemodify.nuspec new file mode 100644 index 0000000000..f81eaa394b --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/isdependencywithbeforemodify.nuspec @@ -0,0 +1,18 @@ + + + + isdependencywithbeforemodify + 1.0.0 + isdependencywithbeforemodify + __REPLACE_AUTHORS_OF_SOFTWARE__ + __REPLACE_YOUR_NAME__ + false + __REPLACE__ + __REPLACE__ + + isdependencywithbeforemodify admin + + + + + \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 new file mode 100644 index 0000000000..d7bc904e0f --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyBeforeModify.ps1 @@ -0,0 +1 @@ +Write-Output "Ran BeforeModify: $env:PackageName $env:PackageVersion" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 new file mode 100644 index 0000000000..d64eb8f47b --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyinstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Installed" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 new file mode 100644 index 0000000000..9ead91ffa3 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/chocolateyuninstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Uninstalled" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/purpose.txt b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/purpose.txt new file mode 100644 index 0000000000..5032f1e436 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/1.0.0/tools/purpose.txt @@ -0,0 +1 @@ +This package is used to test out upgrade behaviour with beforeModify scripts in play for the main package and dependencies. \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/isdependencywithbeforemodify.nuspec b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/isdependencywithbeforemodify.nuspec new file mode 100644 index 0000000000..cba1fb9059 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/isdependencywithbeforemodify.nuspec @@ -0,0 +1,18 @@ + + + + isdependencywithbeforemodify + 2.0.0 + isdependencywithbeforemodify + __REPLACE_AUTHORS_OF_SOFTWARE__ + __REPLACE_YOUR_NAME__ + false + __REPLACE__ + __REPLACE__ + + isdependencywithbeforemodify admin + + + + + \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 new file mode 100644 index 0000000000..d7bc904e0f --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyBeforeModify.ps1 @@ -0,0 +1 @@ +Write-Output "Ran BeforeModify: $env:PackageName $env:PackageVersion" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 new file mode 100644 index 0000000000..d64eb8f47b --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyinstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Installed" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 new file mode 100644 index 0000000000..9ead91ffa3 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/chocolateyuninstall.ps1 @@ -0,0 +1 @@ +Write-Output "$env:PackageName $env:PackageVersion Uninstalled" \ No newline at end of file diff --git a/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/purpose.txt b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/purpose.txt new file mode 100644 index 0000000000..5032f1e436 --- /dev/null +++ b/src/chocolatey.tests.integration/context/dependenciesbeforemodify/isdependencywithbeforemodify/2.0.0/tools/purpose.txt @@ -0,0 +1 @@ +This package is used to test out upgrade behaviour with beforeModify scripts in play for the main package and dependencies. \ No newline at end of file diff --git a/src/chocolatey.tests.integration/scenarios/InstallScenarios.cs b/src/chocolatey.tests.integration/scenarios/InstallScenarios.cs index 1e0e91effb..98a825d1b8 100644 --- a/src/chocolatey.tests.integration/scenarios/InstallScenarios.cs +++ b/src/chocolatey.tests.integration/scenarios/InstallScenarios.cs @@ -5461,5 +5461,104 @@ public override void Context() Configuration.Version = NonNormalizedVersion; } } + + public class when_installing_a_package_that_requires_updating_a_dependency : ScenariosBase + { + private const string TargetPackageName = "hasdependencywithbeforemodify"; + private const string DependencyName = "isdependencywithbeforemodify"; + + public override void Context() + { + base.Context(); + + Scenario.add_packages_to_source_location(Configuration, "{0}.*".format_with(TargetPackageName) + NuGetConstants.PackageExtension); + Scenario.add_packages_to_source_location(Configuration, "{0}.*".format_with(DependencyName) + NuGetConstants.PackageExtension); + Scenario.install_package(Configuration, DependencyName, "1.0.0"); + + Configuration.PackageNames = Configuration.Input = TargetPackageName; + } + + public override void Because() + { + Results = Service.install_run(Configuration); + } + + [Fact] + public void should_install_the_package() + { + var packageFile = Path.Combine(Scenario.get_top_level(), "lib", TargetPackageName, "{0}.nupkg".format_with(TargetPackageName)); + using (var packageReader = new PackageArchiveReader(packageFile)) + { + packageReader.NuspecReader.GetVersion().to_string().ShouldEqual("2.0.0"); + } + } + + [Fact] + public void should_upgrade_the_minimum_version_dependency() + { + var packageFile = Path.Combine(Scenario.get_top_level(), "lib", DependencyName, "{0}.nupkg".format_with(DependencyName)); + using (var packageReader = new PackageArchiveReader(packageFile)) + { + packageReader.NuspecReader.GetVersion().to_string().ShouldEqual("2.0.0"); + } + } + + [Fact] + public void should_contain_a_message_that_everything_installed_successfully() + { + MockLogger.contains_message("installed 2/2", LogLevel.Warn).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_not_run_target_package_beforeModify_for_upgraded_version() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(TargetPackageName, "2.0.0"), LogLevel.Info).ShouldBeFalse(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_already_installed_dependency_package_beforeModify() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(DependencyName, "1.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_not_run_dependency_package_beforeModify_for_upgraded_version() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(DependencyName, "2.0.0"), LogLevel.Info).ShouldBeFalse(); + } + + [Fact] + public void should_have_a_successful_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Success.ShouldBeTrue(); + } + } + + [Fact] + public void should_not_have_inconclusive_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Inconclusive.ShouldBeFalse(); + } + } + + [Fact] + public void should_not_have_warning_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Warning.ShouldBeFalse(); + } + } + } } } diff --git a/src/chocolatey.tests.integration/scenarios/UninstallScenarios.cs b/src/chocolatey.tests.integration/scenarios/UninstallScenarios.cs index f549f40c92..e2c3511279 100644 --- a/src/chocolatey.tests.integration/scenarios/UninstallScenarios.cs +++ b/src/chocolatey.tests.integration/scenarios/UninstallScenarios.cs @@ -1527,5 +1527,93 @@ public void should_have_executed_chocolateyUninstall_script() MockLogger.contains_message("upgradepackage {0} Uninstalled".format_with(NonNormalizedVersion), LogLevel.Info).ShouldBeTrue(); } } + + public class when_uninstalling_a_package_with_remove_dependencies_with_beforeModify : ScenariosBase + { + private const string TargetPackageName = "hasdependencywithbeforemodify"; + private const string DependencyName = "isdependencywithbeforemodify"; + + public override void Context() + { + base.Context(); + + Scenario.add_packages_to_source_location(Configuration, "{0}.*".format_with(TargetPackageName) + NuGetConstants.PackageExtension); + Scenario.add_packages_to_source_location(Configuration, "{0}.*".format_with(DependencyName) + NuGetConstants.PackageExtension); + Scenario.install_package(Configuration, TargetPackageName, "1.0.0"); + Scenario.install_package(Configuration, DependencyName, "1.0.0"); + + Configuration.PackageNames = Configuration.Input = TargetPackageName; + Configuration.ForceDependencies = true; + } + + public override void Because() + { + Results = Service.uninstall_run(Configuration); + } + + [Fact] + public void should_uninstall_the_package() + { + var packageFile = Path.Combine(Scenario.get_top_level(), "lib", TargetPackageName, "{0}.nupkg".format_with(TargetPackageName)); + File.Exists(packageFile).ShouldBeFalse(); + } + + [Fact] + public void should_uninstall_the_dependency() + { + var packageFile = Path.Combine(Scenario.get_top_level(), "lib", DependencyName, "{0}.nupkg".format_with(DependencyName)); + File.Exists(packageFile).ShouldBeFalse(); + } + + [Fact] + public void should_contain_a_message_that_everything_uninstalled_successfully() + { + MockLogger.contains_message("uninstalled 2/2", LogLevel.Warn).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_target_package_beforeModify() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(TargetPackageName, "1.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_dependency_package_beforeModify() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(DependencyName, "1.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + public void should_have_a_successful_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Success.ShouldBeTrue(); + } + } + + [Fact] + public void should_not_have_inconclusive_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Inconclusive.ShouldBeFalse(); + } + } + + [Fact] + public void should_not_have_warning_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Warning.ShouldBeFalse(); + } + + } + } } } diff --git a/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs b/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs index c1a95f19d5..3ed7a8ae53 100644 --- a/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs +++ b/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs @@ -5373,5 +5373,169 @@ public override void Context() } } + + public class when_upgrading_a_package_with_beforeModify_script_with_dependencies_with_beforeModify_scripts_and_hooks : ScenariosBase + { + private const string TargetPackageName = "hasdependencywithbeforemodify"; + private const string DependencyName = "isdependencywithbeforemodify"; + + public override void Context() + { + base.Context(); + + Scenario.add_packages_to_source_location(Configuration, "{0}.*".format_with(TargetPackageName) + NuGetConstants.PackageExtension); + Scenario.add_packages_to_source_location(Configuration, "{0}.*".format_with(DependencyName) + NuGetConstants.PackageExtension); + Scenario.add_packages_to_source_location(Configuration, "scriptpackage.hook" + "*" + NuGetConstants.PackageExtension); + Scenario.install_package(Configuration, DependencyName, "1.0.0"); + Scenario.install_package(Configuration, TargetPackageName, "1.0.0"); + Scenario.install_package(Configuration, "scriptpackage.hook", "1.0.0"); + + Configuration.PackageNames = Configuration.Input = TargetPackageName; + } + + public override void Because() + { + Results = Service.upgrade_run(Configuration); + } + + [Fact] + public void should_upgrade_the_minimum_version_dependency() + { + var packageFile = Path.Combine(Scenario.get_top_level(), "lib", DependencyName, "{0}.nupkg".format_with(DependencyName)); + using (var packageReader = new PackageArchiveReader(packageFile)) + { + packageReader.NuspecReader.GetVersion().to_string().ShouldEqual("2.0.0"); + } + } + + [Fact] + public void should_contain_a_message_that_everything_upgraded_successfully() + { + MockLogger.contains_message("upgraded 2/2", LogLevel.Warn).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_beforemodify_hook_script_for_previous_version_of_target() + { + MockLogger.contains_message("pre-beforemodify-all.ps1 hook ran for {0} {1}".format_with(TargetPackageName, "1.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_already_installed_target_package_beforeModify() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(TargetPackageName, "1.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_not_run_beforemodify_hook_script_for_upgrade_version_of_target() + { + MockLogger.contains_message("pre-beforemodify-all.ps1 hook ran for {0} {1}".format_with(TargetPackageName, "2.0.0"), LogLevel.Info).ShouldBeFalse(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_not_run_target_package_beforeModify_for_upgraded_version() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(TargetPackageName, "2.0.0"), LogLevel.Info).ShouldBeFalse(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_pre_all_hook_script_for_upgraded_version_of_target() + { + MockLogger.contains_message("pre-install-all.ps1 hook ran for {0} {1}".format_with(TargetPackageName, "2.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_post_all_hook_script_for_upgraded_version_of_target() + { + MockLogger.contains_message("post-install-all.ps1 hook ran for {0} {1}".format_with(TargetPackageName, "2.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_beforemodify_hook_script_for_previous_version_of_dependency() + { + MockLogger.contains_message("pre-beforemodify-all.ps1 hook ran for {0} {1}".format_with(DependencyName, "1.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_already_installed_dependency_package_beforeModify() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(DependencyName, "1.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_not_run_beforemodify_hook_script_for_upgrade_version_of_dependency() + { + MockLogger.contains_message("pre-beforemodify-all.ps1 hook ran for {0} {1}".format_with(DependencyName, "2.0.0"), LogLevel.Info).ShouldBeFalse(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_not_run_dependency_package_beforeModify_for_upgraded_version() + { + MockLogger.contains_message("Ran BeforeModify: {0} {1}".format_with(DependencyName, "2.0.0"), LogLevel.Info).ShouldBeFalse(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_pre_all_hook_script_for_upgraded_version_of_dependency() + { + MockLogger.contains_message("pre-install-all.ps1 hook ran for {0} {1}".format_with(DependencyName, "2.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + [WindowsOnly] + [Platform(Exclude = "Mono")] + public void should_run_post_all_hook_script_for_upgraded_version_of_dependency() + { + MockLogger.contains_message("post-install-all.ps1 hook ran for {0} {1}".format_with(DependencyName, "2.0.0"), LogLevel.Info).ShouldBeTrue(); + } + + [Fact] + public void should_have_a_successful_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Success.ShouldBeTrue(); + } + } + + [Fact] + public void should_not_have_inconclusive_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Inconclusive.ShouldBeFalse(); + } + } + + [Fact] + public void should_not_have_warning_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Warning.ShouldBeFalse(); + } + } + } } } From ea7fe9c307e09400d0423bc180a0a884fd584717 Mon Sep 17 00:00:00 2001 From: Rain Sallow Date: Tue, 14 Mar 2023 13:47:34 -0400 Subject: [PATCH 3/3] (#1092) Pull beforeModify changes into develop Changes originally in 9f5d9ccfb83fd78542073477d066e1586b3ed90e, adapted for the current develop branch and the changes therein. Additionally, replaced and obsoleted some overloads of public (why?) methods that have unused method arguments and made them protected. --- .../services/NugetService.cs | 119 +++++++++++++----- 1 file changed, 89 insertions(+), 30 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 4525d2290a..502ddb9aba 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -762,8 +762,7 @@ Version was specified as '{0}'. It is possible that version if (packageToUninstall != null) { shouldAddForcedResultMessage = true; - remove_rollback_directory_if_exists(packageRemoteMetadata.Identity.Id); - backup_existing_version(config, packageToUninstall.PackageMetadata, _packageInfoService.get_package_information(packageToUninstall.PackageMetadata)); + backup_and_before_modify(packageToUninstall, config, beforeModifyAction); packageToUninstall.InstallLocation = pathResolver.GetInstallPath(packageToUninstall.Identity); try { @@ -1371,21 +1370,11 @@ public virtual ConcurrentDictionary upgrade_run(Chocolate try { - remove_rollback_directory_if_exists(packageName); - if (packageToUninstall != null) { var oldPkgInfo = _packageInfoService.get_package_information(packageToUninstall.PackageMetadata); - if (beforeUpgradeAction != null && packageToUninstall.PackageMetadata != null) - { - beforeUpgradeAction(packageToUninstall, config); - } - - ensure_package_files_have_compatible_attributes(config, packageToUninstall.PackageMetadata, oldPkgInfo); - rename_legacy_package_version(config, packageToUninstall.PackageMetadata, oldPkgInfo); - backup_existing_version(config, packageToUninstall.PackageMetadata, oldPkgInfo); - remove_shim_directors(config, packageToUninstall.PackageMetadata, pkgInfo); + backup_and_before_modify(packageToUninstall, oldPkgInfo, config, beforeUpgradeAction); packageToUninstall.InstallLocation = pathResolver.GetInstallPath(packageToUninstall.Identity); try @@ -1817,7 +1806,11 @@ private string get_install_directory(ChocolateyConfiguration config, IPackageMet return installDirectory; } + [Obsolete("This overload is obsolete and will be removed in a future version.")] public virtual void ensure_package_files_have_compatible_attributes(ChocolateyConfiguration config, IPackageMetadata installedPackage, ChocolateyPackageInformation pkgInfo) + => ensure_package_files_have_compatible_attributes(config, installedPackage); + + protected virtual void ensure_package_files_have_compatible_attributes(ChocolateyConfiguration config, IPackageMetadata installedPackage) { var installDirectory = get_install_directory(config, installedPackage); if (!_fileSystem.directory_exists(installDirectory)) return; @@ -1825,7 +1818,11 @@ public virtual void ensure_package_files_have_compatible_attributes(ChocolateyCo _filesService.ensure_compatible_file_attributes(installDirectory, config); } + [Obsolete("This overload is obsolete and will be removed in a future version.")] public virtual void rename_legacy_package_version(ChocolateyConfiguration config, IPackageMetadata installedPackage, ChocolateyPackageInformation pkgInfo) + => normalize_package_legacy_folder_name(config, installedPackage, pkgInfo); + + protected virtual void normalize_package_legacy_folder_name(ChocolateyConfiguration config, IPackageMetadata installedPackage, ChocolateyPackageInformation pkgInfo) { if (pkgInfo != null && pkgInfo.IsSideBySide) return; @@ -1845,15 +1842,19 @@ public virtual void rename_legacy_package_version(ChocolateyConfiguration config } + [Obsolete("This overload is obsolete and will be removed in a future version.")] public virtual void backup_existing_version(ChocolateyConfiguration config, IPackageMetadata installedPackage, ChocolateyPackageInformation packageInfo) + => backup_existing_version(config, packageInfo); + + protected virtual void backup_existing_version(ChocolateyConfiguration config, ChocolateyPackageInformation packageInfo) { _fileSystem.create_directory_if_not_exists(ApplicationParameters.PackageBackupLocation); - var pkgInstallPath = get_install_directory(config, installedPackage); + var pkgInstallPath = get_install_directory(config, packageInfo.Package); if (_fileSystem.directory_exists(pkgInstallPath)) { - this.Log().Debug("Backing up existing {0} prior to operation.".format_with(installedPackage.Id)); + this.Log().Debug("Backing up existing {0} prior to operation.".format_with(packageInfo.Package.Id)); var backupLocation = pkgInstallPath.Replace(ApplicationParameters.PackagesLocation, ApplicationParameters.PackageBackupLocation); @@ -1970,7 +1971,7 @@ public virtual void backup_changed_files(string packageInstallPath, ChocolateyCo /// The configuration. /// The installed package. /// The package information. - private void remove_shim_directors(ChocolateyConfiguration config, IPackageMetadata installedPackage, ChocolateyPackageInformation pkgInfo) + private void remove_shim_directors(ChocolateyConfiguration config, IPackageMetadata installedPackage) { var pkgInstallPath = get_install_directory(config, installedPackage); @@ -2211,7 +2212,6 @@ public virtual ConcurrentDictionary uninstall_run(Chocola var pathResolver = NugetCommon.GetPathResolver(config, _fileSystem); var nugetProject = new FolderNuGetProject(ApplicationParameters.PackagesLocation, pathResolver, NuGetFramework.AnyFramework); - var pkgInfo = _packageInfoService.get_package_information(installedPackage.PackageMetadata); if (pkgInfo != null && pkgInfo.IsPinned) { @@ -2241,21 +2241,10 @@ public virtual ConcurrentDictionary uninstall_run(Chocola foreach (var packageToUninstall in packagesToUninstall) { - if (beforeUninstallAction != null) - { - // guessing this is not added so that it doesn't fail the action if an error is recorded? - //var currentPackageResult = packageUninstalls.GetOrAdd(packageName, new PackageResult(packageVersion, get_install_directory(config, packageVersion))); - beforeUninstallAction(packageToUninstall, config); - } - - var uninstallPkgInfo = _packageInfoService.get_package_information(packageToUninstall.PackageMetadata); - try { - ensure_package_files_have_compatible_attributes(config, packageToUninstall.PackageMetadata, uninstallPkgInfo); - rename_legacy_package_version(config, packageToUninstall.PackageMetadata, uninstallPkgInfo); - remove_rollback_directory_if_exists(packageName); - backup_existing_version(config, packageToUninstall.PackageMetadata, uninstallPkgInfo); + var uninstallPkgInfo = _packageInfoService.get_package_information(packageToUninstall.PackageMetadata); + backup_and_before_modify(packageToUninstall, uninstallPkgInfo, config, beforeUninstallAction); var packageResult = packageResultsToReturn.GetOrAdd(packageToUninstall.Name + "." + packageToUninstall.Version.to_string(), packageToUninstall); packageResult.InstallLocation = packageToUninstall.InstallLocation; @@ -2320,6 +2309,76 @@ public virtual ConcurrentDictionary uninstall_run(Chocola return packageResultsToReturn; } + /// + /// This method should be called before any modifications are made to a package. + /// Typically this should be called before doing an uninstall of an existing package + /// or package dependency during an install, upgrade, or uninstall operation. + /// + /// The package currently being modified. + /// The current configuration. + /// Any action to run before performing backup operations. Typically this is an invocation of the chocolateyBeforeModify script. + protected void backup_and_before_modify( + PackageResult packageResult, + ChocolateyConfiguration config, + Action beforeModifyAction) + { + var packageInformation = _packageInfoService.get_package_information(packageResult.PackageMetadata); + backup_and_before_modify(packageResult, packageInformation, config, beforeModifyAction); + } + + /// + /// This method should be called before any modifications are made to a package. + /// Typically this should be called before doing an uninstall of an existing package + /// or package dependency during an install, upgrade, or uninstall operation. + /// + /// The package currently being modified. + /// The package information for the package being modified. + /// The current configuration. + /// Any action to run before performing backup operations. Typically this is an invocation of the chocolateyBeforeModify script. + protected virtual void backup_and_before_modify( + PackageResult packageResult, + ChocolateyPackageInformation packageInformation, + ChocolateyConfiguration config, + Action beforeModifyAction) + { + try + { + if (packageResult.InstallLocation != null) + { + // If this is an already installed package we're modifying, ensure we run its beforeModify script and back it up properly. + if (beforeModifyAction != null) + { + "chocolatey".Log().Debug("Running beforeModify step for '{0}'", packageResult.PackageMetadata.Id); + beforeModifyAction(packageResult, config); + } + + "chocolatey".Log().Debug("Backing up package files for '{0}'", packageResult.PackageMetadata.Id); + + backup_existing_package_files(config, packageResult.PackageMetadata, packageInformation); + } + } + catch (Exception error) + { + "chocolatey".Log().Error("Failed to run backup or beforeModify steps for package '{0}': {1}", packageResult.PackageMetadata.Id, error.Message); + "chocolatey".Log().Trace(error.StackTrace); + } + } + + /// + /// Takes a backup of the existing package files. + /// + /// The current configuration settings + /// The metadata for the package to backup + /// The package information to backup + protected void backup_existing_package_files(ChocolateyConfiguration config, IPackageMetadata package, ChocolateyPackageInformation packageInformation) + { + remove_rollback_directory_if_exists(package.Id); + ensure_package_files_have_compatible_attributes(config, package); + normalize_package_legacy_folder_name(config, package, packageInformation); + backup_existing_version(config, packageInformation); + remove_shim_directors(config, package); + } + /// /// NuGet will happily report a package has been uninstalled, even if it doesn't always remove the nupkg. /// Ensure that the package is deleted or throw an error.