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(); + } + } + } } } 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..502ddb9aba 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); @@ -757,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 { @@ -1013,7 +1017,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); @@ -1366,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 @@ -1812,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; @@ -1820,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; @@ -1840,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); @@ -1965,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); @@ -2206,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) { @@ -2236,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; @@ -2315,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. 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);