From 4ef0a40eb7538cae35b99ee551902e740f75bb24 Mon Sep 17 00:00:00 2001 From: Russell Mora Date: Tue, 10 Jan 2017 20:22:15 -0500 Subject: [PATCH] (GH-533) Support packages.config file in upgrade Implements a subset of the functionality, support for packages.config files in the upgrade command. Also extended the tests to test the new code. I did not see any value in implementing package.config support for uninstall because the problem I am interested in solving is being able to upgrade multiple packages each with a specific version in one command. Since uninstall (generally?) doesn't require specifying a version this can be done on the uninstall command line, i.e. just give the package list to uninstall. --- Scenarios.md | 14 +- .../scenarios/UpgradeScenarios.cs | 126 +++++++++++++++++- .../commands/ChocolateyUpgradeCommand.cs | 33 ++++- .../services/ChocolateyPackageService.cs | 34 +++-- 4 files changed, 183 insertions(+), 24 deletions(-) diff --git a/Scenarios.md b/Scenarios.md index 2f388803ca..6cc45996c4 100644 --- a/Scenarios.md +++ b/Scenarios.md @@ -649,7 +649,7 @@ * should throw an error that it is not allowed -### ChocolateyUpgradeCommand [ 36 Scenario(s), 295 Observation(s) ] +### ChocolateyUpgradeCommand [ 36 Scenario(s), 305 Observation(s) ] #### when force upgrading a package @@ -1052,4 +1052,14 @@ #### when upgrading packages with packages config - * should throw an error that it is not allowed + * should contain a message that upgradepackage with an expected specified version was not installed + * should contain a warning message that it upgraded 3 out of 6 packages successfully + * should have a successful package result for all but expected missing package + * should install expected packages in the lib directory + * should install the dependency in the lib directory + * should install where install location reports + * should not have a successful package result for missing package + * should not have inconclusive package result for all but expected install and upgrade packages + * should not have warning package result + * should print out package from config file in message + * should specify config file is being used in message diff --git a/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs b/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs index dd1352fdf7..f7d8f6b6a8 100644 --- a/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs +++ b/src/chocolatey.tests.integration/scenarios/UpgradeScenarios.cs @@ -18,6 +18,7 @@ namespace chocolatey.tests.integration.scenarios { using System; using System.Collections.Concurrent; + using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -1204,17 +1205,136 @@ public override void Context() base.Context(); var packagesConfig = "{0}\\context\\testing.packages.config".format_with(Scenario.get_top_level()); Configuration.PackageNames = Configuration.Input = packagesConfig; + Scenario.add_packages_to_source_location(Configuration, "hasdependency.1.0.0*" + Constants.PackageExtension); + Scenario.add_packages_to_source_location(Configuration, "isdependency.1.0.0*" + Constants.PackageExtension); + Scenario.add_packages_to_source_location(Configuration, "isexactversiondependency*" + Constants.PackageExtension); + Scenario.add_packages_to_source_location(Configuration, "upgradepackage*" + Constants.PackageExtension); + Configuration.UpgradeCommand.FailOnNotInstalled = false; } public override void Because() { + Results = Service.upgrade_run(Configuration); } [Fact] - [ExpectedException(typeof(ApplicationException))] - public void should_throw_an_error_that_it_is_not_allowed() + public void should_install_where_install_location_reports() { - Results = Service.upgrade_run(Configuration); + foreach (var packageResult in Results) + { + if (packageResult.Value.Name.is_equal_to("missingpackage")) continue; + Directory.Exists(packageResult.Value.InstallLocation).ShouldBeTrue(); + } + } + + [Fact] + public void should_install_expected_packages_in_the_lib_directory() + { + var packagesExpected = new List { "installpackage", "hasdependency", "isdependency", "upgradepackage" }; + foreach (var package in packagesExpected) + { + var packageDir = Path.Combine(Scenario.get_top_level(), "lib", package); + Directory.Exists(packageDir).ShouldBeTrue(); + } + } + + [Fact] + public void should_install_the_dependency_in_the_lib_directory() + { + var packageDir = Path.Combine(Scenario.get_top_level(), "lib", "isdependency"); + Directory.Exists(packageDir).ShouldBeTrue(); + } + + [Fact] + public void should_contain_a_warning_message_that_it_upgraded_3_out_of_6_packages_successfully() + { + bool upgradedSuccessfully = false; + foreach (var message in MockLogger.MessagesFor(LogLevel.Warn).or_empty_list_if_null()) + { + if (message.Contains("3/6")) upgradedSuccessfully = true; + } + + upgradedSuccessfully.ShouldBeTrue(); + } + + [Fact] + public void should_contain_a_message_that_upgradepackage_with_an_expected_specified_version_was_not_installed() + { + bool expectedMessage = false; + foreach (var message in MockLogger.MessagesFor(LogLevel.Info).or_empty_list_if_null()) + { + if (message.Contains("upgradepackage v1.0.0 is the latest version available based on your source")) expectedMessage = true; + } + + expectedMessage.ShouldBeTrue(); + } + + [Fact] + public void should_have_a_successful_package_result_for_all_but_expected_missing_package() + { + foreach (var packageResult in Results) + { + if (packageResult.Value.Name.is_equal_to("missingpackage")) continue; + + packageResult.Value.Success.ShouldBeTrue(); + } + } + + [Fact] + public void should_not_have_a_successful_package_result_for_missing_package() + { + foreach (var packageResult in Results) + { + if (!packageResult.Value.Name.is_equal_to("missingpackage")) continue; + + packageResult.Value.Success.ShouldBeFalse(); + } + } + + [Fact] + public void should_not_have_inconclusive_package_result_for_all_but_expected_install_and_upgrade_packages() + { + foreach (var packageResult in Results) + { + // These two packages don't upgrade because there is no newer version available + if (packageResult.Value.Name.is_equal_to("installpackage") || packageResult.Value.Name.is_equal_to("upgradepackage")) + packageResult.Value.Inconclusive.ShouldBeTrue(); + else + packageResult.Value.Inconclusive.ShouldBeFalse(); + } + } + + [Fact] + public void should_not_have_warning_package_result() + { + foreach (var packageResult in Results) + { + packageResult.Value.Warning.ShouldBeFalse(); + } + } + + [Fact] + public void should_specify_config_file_is_being_used_in_message() + { + bool expectedMessage = false; + foreach (var message in MockLogger.MessagesFor(LogLevel.Info).or_empty_list_if_null()) + { + if (message.Contains("Upgrading from config file:")) expectedMessage = true; + } + + expectedMessage.ShouldBeTrue(); + } + + [Fact] + public void should_print_out_package_from_config_file_in_message() + { + bool expectedMessage = false; + foreach (var message in MockLogger.MessagesFor(LogLevel.Info).or_empty_list_if_null()) + { + if (message.Contains("installpackage")) expectedMessage = true; + } + + expectedMessage.ShouldBeTrue(); } } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs index 4be428123b..4046ee89c2 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs @@ -222,9 +222,10 @@ public virtual void help_message(ChocolateyConfiguration configuration) { this.Log().Info(ChocolateyLoggers.Important, "Upgrade Command"); this.Log().Info(@" -Upgrades a package or a list of packages. Some may prefer to use `cup` - as a shortcut for `choco upgrade`. If you do not have a package - installed, upgrade will install it. +Upgrades a package or a list of packages(sometimes specified as a + packages.config). Some may prefer to use `cup` as a shortcut for + `choco upgrade`. If you do not have a package installed, upgrade + will install it. NOTE: 100% compatible with older Chocolatey client (0.9.8.x and below) with options and switches. Add `-y` for previous behavior with no @@ -234,12 +235,15 @@ prompt. In most cases you can still pass options and switches with one "chocolatey".Log().Info(ChocolateyLoggers.Important, "Usage"); "chocolatey".Log().Info(@" - choco upgrade [ ] [] - cup [ ] [] + choco upgrade [ ] [] + cup [ ] [] NOTE: `all` is a special package keyword that will allow you to upgrade all currently installed packages. +NOTE: Any package name ending with .config is considered a + 'packages.config' file. Please see https://bit.ly/packages_config + Skip upgrading certain packages with `choco pin` or with the option `--except`. @@ -266,6 +270,25 @@ choco upgrade all "); + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Packages.config"); + "chocolatey".Log().Info(@" +Alternative to PackageName. This is a list of packages in an xml manifest for Chocolatey to upgrade. This is like the packages.config that NuGet uses except it also adds other options and switches. This can also be the path to the packages.config file if it is not in the current working directory. + +NOTE: The filename is only required to end in .config, the name is not required to be packages.config. + + + + + + + + + +"); "chocolatey".Log().Info(ChocolateyLoggers.Important, "Options and Switches"); "chocolatey".Log().Info(@" diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index b0c933d991..3c2dd411c8 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -684,35 +684,41 @@ public void upgrade_noop(ChocolateyConfiguration config) public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config) { - this.Log().Info(@"Upgrading the following packages:"); + this.Log().Info(is_packages_config_file(config.PackageNames) ? @"Upgrading from config file:" : @"Upgrading the following packages:"); this.Log().Info(ChocolateyLoggers.Important, @"{0}".format_with(config.PackageNames)); + var packageUpgrades = new ConcurrentDictionary(); + if (string.IsNullOrWhiteSpace(config.Sources)) { this.Log().Error(ChocolateyLoggers.Important, @"Upgrading was NOT successful. There are no sources enabled for packages and none were passed as arguments."); Environment.ExitCode = 1; - return new ConcurrentDictionary(); + return packageUpgrades; } this.Log().Info(@"By upgrading you accept licenses for the packages."); - foreach (var packageConfigFile in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries).or_empty_list_if_null().Where(p => p.EndsWith(".config")).ToList()) - { - throw new ApplicationException("A packages.config file is only used with installs."); - } + get_environment_before(config, allowLogging: true); - Action action = null; - if (config.SourceType == SourceType.normal) + foreach (var packageConfig in set_config_from_package_names_and_packages_config(config, packageUpgrades).or_empty_list_if_null()) { - action = (packageResult) => handle_package_result(packageResult, config, CommandNameType.upgrade); - } + Action action = null; + if (config.SourceType == SourceType.normal) + { + action = (packageResult) => handle_package_result(packageResult, packageConfig, CommandNameType.upgrade); + } - get_environment_before(config, allowLogging: true); - var beforeUpgradeAction = new Action(packageResult => before_package_modify(packageResult, config)); - var packageUpgrades = perform_source_runner_function(config, r => r.upgrade_run(config, action, beforeUpgradeAction)); - + var beforeUpgradeAction = new Action(packageResult => before_package_modify(packageResult, packageConfig)); + var results = perform_source_runner_function(config, r => r.upgrade_run(packageConfig, action, beforeUpgradeAction)); + + foreach (var result in results) + { + packageUpgrades.GetOrAdd(result.Key, result.Value); + } + } + var upgradeFailures = report_action_summary(packageUpgrades, "upgraded"); if (upgradeFailures != 0 && Environment.ExitCode == 0) {