From 8702e01df8eb5cc6847ad205bbd99c6f337493bd Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 3 Mar 2015 02:12:20 -0500 Subject: [PATCH 01/61] (GH-132) Add ChocolateySource class distinct from serialized source --- src/chocolatey/chocolatey.csproj | 1 + .../configuration/ChocolateySource.cs | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index b008ab60a3..379c389c23 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -90,6 +90,7 @@ + diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs new file mode 100644 index 0000000000..5c163e98c6 --- /dev/null +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs @@ -0,0 +1,25 @@ +// Copyright © 2015 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.configuration +{ + public class ChocolateySource + { + public string Id { get; set; } + public string Value { get; set; } + public bool Disabled { get; set; } + public bool Authenticated { get; set; } + } +} From 8364b0e2ae7cce0ff304efd48e6df18321b072fe Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 3 Mar 2015 02:16:13 -0500 Subject: [PATCH 02/61] (GH-132) Change source_list to return a list of sources --- .../ChocolateyConfigSettingsService.cs | 19 +++++++++++++++---- .../IChocolateyConfigSettingsService.cs | 5 +++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs index f45624b3e9..d1f4db70ca 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs @@ -16,6 +16,7 @@ namespace chocolatey.infrastructure.app.services { using System; + using System.Collections.Generic; using System.Linq; using configuration; using infrastructure.services; @@ -43,12 +44,22 @@ public void noop(ChocolateyConfiguration configuration) this.Log().Info("Would have made a change to the configuration."); } - public void source_list(ChocolateyConfiguration configuration) + public IEnumerable source_list(ChocolateyConfiguration configuration) { + var list = new List(); foreach (var source in configFileSettings.Sources) { - this.Log().Info(() => "{0}{1} - {2}".format_with(source.Id, source.Disabled ? " [Disabled]" : string.Empty, source.Value)); - } + if (configuration.RegularOuptut) { + this.Log().Info(() => "{0}{1} - {2}".format_with(source.Id, source.Disabled ? " [Disabled]" : string.Empty, source.Value)); + } + list.Add(new ChocolateySource { + Id = source.Id, + Value = source.Value, + Disabled = source.Disabled, + Authenticated = string.IsNullOrWhiteSpace(source.Password) + }); + } + return list; } public void source_add(ChocolateyConfiguration configuration) @@ -232,4 +243,4 @@ public void set_api_key(ChocolateyConfiguration configuration) } } } -} \ No newline at end of file +} diff --git a/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs index 3d29562e6b..80628a4281 100644 --- a/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs +++ b/src/chocolatey/infrastructure.app/services/IChocolateyConfigSettingsService.cs @@ -16,12 +16,13 @@ namespace chocolatey.infrastructure.app.services { using System; + using System.Collections.Generic; using configuration; public interface IChocolateyConfigSettingsService { void noop(ChocolateyConfiguration configuration); - void source_list(ChocolateyConfiguration configuration); + IEnumerable source_list(ChocolateyConfiguration configuration); void source_add(ChocolateyConfiguration configuration); void source_remove(ChocolateyConfiguration configuration); void source_disable(ChocolateyConfiguration configuration); @@ -32,4 +33,4 @@ public interface IChocolateyConfigSettingsService string get_api_key(ChocolateyConfiguration configuration, Action keyAction); void set_api_key(ChocolateyConfiguration configuration); } -} \ No newline at end of file +} From cec61459f3033dca4777869d33a16732b9946ffe Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 3 Mar 2015 02:18:37 -0500 Subject: [PATCH 03/61] (GH-132) Change ChocolateySourceCommand to an IListCommand In order to make commands support listing, add a new command interface the IListCommand interface is an ICommand with an extra method "list" --- .../commands/ChocolateySourceCommandSpecs.cs | 20 ++++++++++++++- src/chocolatey/chocolatey.csproj | 1 + .../commands/ChocolateySourceCommand.cs | 9 +++++-- .../infrastructure/commands/IListCommand.cs | 25 +++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 src/chocolatey/infrastructure/commands/IListCommand.cs diff --git a/src/chocolatey.tests/infrastructure.app/commands/ChocolateySourceCommandSpecs.cs b/src/chocolatey.tests/infrastructure.app/commands/ChocolateySourceCommandSpecs.cs index aecaf9d987..55e6654e34 100644 --- a/src/chocolatey.tests/infrastructure.app/commands/ChocolateySourceCommandSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/commands/ChocolateySourceCommandSpecs.cs @@ -355,7 +355,7 @@ public void should_call_service_source_add_when_command_is_add() because(); configSettingsService.Verify(c => c.source_add(configuration), Times.Once); } - + [Fact] public void should_call_service_source_remove_when_command_is_remove() { @@ -380,5 +380,23 @@ public void should_call_service_source_enable_when_command_is_enable() configSettingsService.Verify(c => c.source_enable(configuration), Times.Once); } } + + public class when_list_is_called : ChocolateySourceCommandSpecsBase + { + private Action because; + + public override void Because() + { + because = () => command.list(configuration); + } + + [Fact] + public void should_call_service_source_list_when_command_is_list() + { + configuration.SourceCommand.Command = SourceCommandType.list; + because(); + configSettingsService.Verify(c => c.source_list(configuration), Times.Once); + } + } } } \ No newline at end of file diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 379c389c23..854bd24c55 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -161,6 +161,7 @@ + diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs index e62cafa7f8..537a24a7dc 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs @@ -28,7 +28,7 @@ namespace chocolatey.infrastructure.app.commands [CommandFor(CommandNameType.sources)] [CommandFor(CommandNameType.source)] - public sealed class ChocolateySourceCommand : ICommand + public sealed class ChocolateySourceCommand : IListCommand { private readonly IChocolateyConfigSettingsService _configSettingsService; @@ -139,5 +139,10 @@ public void run(ChocolateyConfiguration configuration) break; } } + + public IEnumerable list(ChocolateyConfiguration configuration) + { + return _configSettingsService.source_list(configuration); + } } -} \ No newline at end of file +} diff --git a/src/chocolatey/infrastructure/commands/IListCommand.cs b/src/chocolatey/infrastructure/commands/IListCommand.cs new file mode 100644 index 0000000000..d24a89ccb2 --- /dev/null +++ b/src/chocolatey/infrastructure/commands/IListCommand.cs @@ -0,0 +1,25 @@ +// Copyright © 2015 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.commands +{ + using System.Collections.Generic; + using app.configuration; + + public interface IListCommand : ICommand + { + IEnumerable list(ChocolateyConfiguration config); + } +} From 80a84e4335cdf1dfe6f5edc3f8492d73746747f5 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 3 Mar 2015 02:24:36 -0500 Subject: [PATCH 04/61] (GH-132) Add a list method to the GenericRunner Refactor GenericRunner.run to a common find_command and separate the run and list methods which call different methods on the underlying command (IListCommand). Previously we only supported GenericRunner.run, this also updates GetChocolatey to add a list method that calls GenericRunner.list --- src/chocolatey/GetChocolatey.cs | 11 ++++- .../runners/GenericRunner.cs | 40 +++++++++++++++---- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/chocolatey/GetChocolatey.cs b/src/chocolatey/GetChocolatey.cs index e6487625e5..b7c82b4bec 100644 --- a/src/chocolatey/GetChocolatey.cs +++ b/src/chocolatey/GetChocolatey.cs @@ -213,7 +213,16 @@ private void extract_resources() AssemblyFileExtractor.extract_all_resources_to_relative_directory(_container.GetInstance(), Assembly.GetAssembly(typeof(ChocolateyResourcesAssembly)), ApplicationParameters.InstallLocation, folders, ApplicationParameters.ChocolateyFileResources); } + + public IEnumerable List() + { + extract_resources(); + var configuration = create_configuration(new List()); + configuration.RegularOuptut = true; + var runner = new GenericRunner(); + return runner.list(configuration, _container, isConsole: false, parseArgs: null); + } } // ReSharper restore InconsistentNaming -} \ No newline at end of file +} diff --git a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs index 99b14f1ace..f75e16ebbb 100644 --- a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs +++ b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs @@ -13,10 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. + namespace chocolatey.infrastructure.app.runners { using System; using System.Linq; + using System.Collections.Generic; using SimpleInjector; using adapters; using attributes; @@ -28,7 +30,7 @@ namespace chocolatey.infrastructure.app.runners public sealed class GenericRunner { - public void run(ChocolateyConfiguration config, Container container, bool isConsole, Action parseArgs) + private ICommand find_command(ChocolateyConfiguration config, Container container, bool isConsole, Action parseArgs) { var commands = container.GetAllInstances(); var command = commands.Where((c) => @@ -83,7 +85,7 @@ Chocolatey is not an official build (bypassed with --allow-unofficial). If you are seeing this message and it is not expected, your system may now be in a bad state. Only official builds are to be trusted. " - ); + ); } } @@ -96,14 +98,38 @@ now be in a bad state. Only official builds are to be trusted. } command.noop(config); + return null; } - else + } + return command; + } + + public void run(ChocolateyConfiguration config, Container container, bool isConsole, Action parseArgs) + { + var command = find_command(config, container, isConsole, parseArgs); + if(command != null) + { + this.Log().Debug("_ {0}:{1} - Normal Run Mode _".format_with(ApplicationParameters.Name, command.GetType().Name)); + command.run(config); + } + } + + public IEnumerable list(ChocolateyConfiguration config, Container container, bool isConsole, Action parseArgs) + { + var command = find_command(config, container, isConsole, parseArgs) as IListCommand; + if (command == null) + { + if (!string.IsNullOrWhiteSpace(config.CommandName)) { - this.Log().Debug("_ {0}:{1} - Normal Run Mode _".format_with(ApplicationParameters.Name, command.GetType().Name)); - command.run(config); + throw new Exception("The implementation of '{0}' does not support listing '{1}'".format_with(config.CommandName, typeof(T).Name)); } + return new List(); + } + else + { + this.Log().Debug("_ {0}:{1} - Normal List Mode _".format_with(ApplicationParameters.Name, command.GetType().Name)); + return command.list(config); } } } - -} \ No newline at end of file +} From 33cf250e9155ef4106cb5c79d249895ca89d7d40 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Fri, 6 Mar 2015 18:25:08 -0600 Subject: [PATCH 05/61] (maint) fix other typos after merging GH-148 --- src/chocolatey/GetChocolatey.cs | 2 +- .../services/ChocolateyConfigSettingsService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chocolatey/GetChocolatey.cs b/src/chocolatey/GetChocolatey.cs index b7c82b4bec..c8883f2192 100644 --- a/src/chocolatey/GetChocolatey.cs +++ b/src/chocolatey/GetChocolatey.cs @@ -218,7 +218,7 @@ public IEnumerable List() { extract_resources(); var configuration = create_configuration(new List()); - configuration.RegularOuptut = true; + configuration.RegularOutput = true; var runner = new GenericRunner(); return runner.list(configuration, _container, isConsole: false, parseArgs: null); } diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs index d1f4db70ca..5f60b3feb8 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs @@ -49,7 +49,7 @@ public IEnumerable source_list(ChocolateyConfiguration configu var list = new List(); foreach (var source in configFileSettings.Sources) { - if (configuration.RegularOuptut) { + if (configuration.RegularOutput) { this.Log().Info(() => "{0}{1} - {2}".format_with(source.Id, source.Disabled ? " [Disabled]" : string.Empty, source.Value)); } list.Add(new ChocolateySource { From 010b896f75962dce98337e2cd26d82184dd9ebd9 Mon Sep 17 00:00:00 2001 From: Igal Tabachnik Date: Wed, 20 May 2015 10:02:48 +0300 Subject: [PATCH 06/61] (doc) rename "awhile" -> "a while" per http://www.chicagomanualofstyle.org/qanda/data/faq/topics/Usage/faq0016.html :) --- nuget/chocolatey/tools/chocolateysetup.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuget/chocolatey/tools/chocolateysetup.psm1 b/nuget/chocolatey/tools/chocolateysetup.psm1 index e6520e5d1b..f0a560b0b8 100644 --- a/nuget/chocolatey/tools/chocolateysetup.psm1 +++ b/nuget/chocolatey/tools/chocolateysetup.psm1 @@ -465,7 +465,7 @@ param( $netFx4InstallTries += 1 if (!(Test-Path $NetFx4Installer)) { - Write-Host "Downloading `'$NetFx4Url`' to `'$NetFx4Installer`' - the installer is 40+ MBs, so this could take awhile on a slow connection." + Write-Host "Downloading `'$NetFx4Url`' to `'$NetFx4Installer`' - the installer is 40+ MBs, so this could take a while on a slow connection." (New-Object Net.WebClient).DownloadFile("$NetFx4Url","$NetFx4Installer") } From 55d3f94fb5b341a94047f3aa8cc67b5f530ed13a Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 13:24:46 -0500 Subject: [PATCH 07/61] (GH-308) AutoUninstaller on by default Switch AutoUninstaller feature to be on by default. Set up feature flags to allow changing the default value if the feature was not set explicitly by a user. --- .../builders/ConfigurationBuilder.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs index c0a41987d4..b44789a827 100644 --- a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs +++ b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs @@ -127,23 +127,28 @@ private static void set_machine_sources(ChocolateyConfiguration config, ConfigFi private static void set_feature_flags(ChocolateyConfiguration config, ConfigFileSettings configFileSettings) { - config.Features.CheckSumFiles = set_feature_flag(ApplicationParameters.Features.CheckSumFiles, configFileSettings); - config.Features.AutoUninstaller = set_feature_flag(ApplicationParameters.Features.AutoUninstaller, configFileSettings); - config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings); + config.Features.CheckSumFiles = set_feature_flag(ApplicationParameters.Features.CheckSumFiles, configFileSettings, defaultEnabled: true); + config.Features.AutoUninstaller = set_feature_flag(ApplicationParameters.Features.AutoUninstaller, configFileSettings, defaultEnabled: true); + config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false); } - private static bool set_feature_flag(string featureName, ConfigFileSettings configFileSettings) + private static bool set_feature_flag(string featureName, ConfigFileSettings configFileSettings, bool defaultEnabled) { - var enabled = false; var feature = configFileSettings.Features.FirstOrDefault(f => f.Name.is_equal_to(featureName)); - if (feature != null && feature.Enabled) enabled = true; if (feature == null) { - configFileSettings.Features.Add(new ConfigFileFeatureSetting {Name = featureName, Enabled = enabled}); + configFileSettings.Features.Add(new ConfigFileFeatureSetting {Name = featureName, Enabled = defaultEnabled}); + } + else + { + if (!feature.SetExplicitly && feature.Enabled != defaultEnabled) + { + feature.Enabled = defaultEnabled; + } } - return enabled; + return feature != null ? feature.Enabled : defaultEnabled; } private static void set_global_options(IList args, ChocolateyConfiguration config) From cf2f6650db2c06b4bf60a057fa88de9d06a5d6af Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:32:12 -0500 Subject: [PATCH 08/61] (GH-14) Define ISourceRunner --- src/chocolatey/chocolatey.csproj | 3 +- .../{SpecialSourceType.cs => SourceType.cs} | 2 +- .../services/ChocolateyPackageService.cs | 4 +- .../services/ISourceRunner.cs | 100 ++++++++++++++++++ 4 files changed, 105 insertions(+), 4 deletions(-) rename src/chocolatey/infrastructure.app/domain/{SpecialSourceType.cs => SourceType.cs} (93%) create mode 100644 src/chocolatey/infrastructure.app/services/ISourceRunner.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 99d7158316..df979ddb99 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -109,6 +109,7 @@ + @@ -183,7 +184,7 @@ - + diff --git a/src/chocolatey/infrastructure.app/domain/SpecialSourceType.cs b/src/chocolatey/infrastructure.app/domain/SourceType.cs similarity index 93% rename from src/chocolatey/infrastructure.app/domain/SpecialSourceType.cs rename to src/chocolatey/infrastructure.app/domain/SourceType.cs index 2ae07f5e69..6c193ef063 100644 --- a/src/chocolatey/infrastructure.app/domain/SpecialSourceType.cs +++ b/src/chocolatey/infrastructure.app/domain/SourceType.cs @@ -18,7 +18,7 @@ namespace chocolatey.infrastructure.app.domain /// /// Special source modifiers that use alternate sources for packages /// - public enum SpecialSourceType + public enum SourceType { //this is what it should be when it's not set normal, diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index 2cddc469d6..77f7b4e04d 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -59,7 +59,7 @@ public ChocolateyPackageService(INugetService nugetService, IPowershellService p public void list_noop(ChocolateyConfiguration config) { - if (config.Sources.is_equal_to(SpecialSourceType.webpi.to_string())) + if (config.Sources.is_equal_to(SourceType.webpi.to_string())) { //todo: webpi } @@ -73,7 +73,7 @@ public void list_run(ChocolateyConfiguration config, bool logResults) { this.Log().Debug(() => "Searching for package information"); - if (config.Sources.is_equal_to(SpecialSourceType.webpi.to_string())) + if (config.Sources.is_equal_to(SourceType.webpi.to_string())) { //todo: webpi //install webpi if not installed diff --git a/src/chocolatey/infrastructure.app/services/ISourceRunner.cs b/src/chocolatey/infrastructure.app/services/ISourceRunner.cs new file mode 100644 index 0000000000..5a8eedd95b --- /dev/null +++ b/src/chocolatey/infrastructure.app/services/ISourceRunner.cs @@ -0,0 +1,100 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.services +{ + using System; + using System.Collections.Concurrent; + using configuration; + using domain; + using results; + + public interface ISourceRunner + { + /// + /// Gets the source type the source runner implements + /// + /// + /// The type of the source. + /// + SourceType SourceType { get; } + + /// + /// Ensures the application that controls a source is installed + /// + /// The configuration. + /// The action to continue with as part of the install + void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction); + + /// + /// Run list in noop mode + /// + /// The configuration. + void list_noop(ChocolateyConfiguration config); + + /// + /// Lists/searches for packages from the source feed + /// + /// The configuration. + /// Should results be logged? + /// + ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults); + + /// + /// Run install in noop mode + /// + /// The configuration. + /// The action to continue with for each noop test install. + void install_noop(ChocolateyConfiguration config, Action continueAction); + + /// + /// Installs packages from the source feed + /// + /// The configuration. + /// The action to continue with when install is successful. + /// results of installs + ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction); + + /// + /// Run upgrade in noop mode + /// + /// The configuration. + /// The action to continue with for each noop test upgrade. + ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction); + + /// + /// Upgrades packages from NuGet related feeds + /// + /// The configuration. + /// The action to continue with when upgrade is successful. + /// results of installs + ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction); + + /// + /// Run uninstall in noop mode + /// + /// The configuration. + /// The action to continue with for each noop test upgrade. + void uninstall_noop(ChocolateyConfiguration config, Action continueAction); + + /// + /// Uninstalls packages from NuGet related feeds + /// + /// The configuration. + /// The action to continue with when upgrade is successful. + /// results of installs + ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction); + } +} \ No newline at end of file From 83c4b62a2c0da82a0bb2c127e9ed10832e107463 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:34:19 -0500 Subject: [PATCH 09/61] (GH-14) Add SourceType to config Set the value for source type based what is passed in, default to normal. --- .../configuration/ChocolateyConfiguration.cs | 2 ++ .../infrastructure.app/runners/GenericRunner.cs | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index b92b9bd8c5..46445dd0ea 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -33,6 +33,7 @@ public ChocolateyConfiguration() { RegularOutput = true; PromptForConfirmation = true; + SourceType = SourceType.normal; Information = new InformationCommandConfiguration(); Features = new FeaturesConfiguration(); NewCommand = new NewCommandConfiguration(); @@ -143,6 +144,7 @@ private void append_output(StringBuilder propertyValues, string append) /// One or more source locations set by configuration or by command line. Separated by semi-colon /// public string Sources { get; set; } + public SourceType SourceType { get; set; } // top level commands diff --git a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs index a19b845135..6ba73330ec 100644 --- a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs +++ b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs @@ -24,6 +24,7 @@ namespace chocolatey.infrastructure.app.runners using attributes; using commandline; using configuration; + using domain; using infrastructure.commands; using logging; using Console = System.Console; @@ -62,6 +63,8 @@ private ICommand find_command(ChocolateyConfiguration config, Container containe parseArgs.Invoke(command); } + set_source_type(config); + this.Log().Debug(() => "Configuration: {0}".format_with(config.ToString())); @@ -110,6 +113,13 @@ now be in a bad state. Only official builds are to be trusted. return command; } + private static void set_source_type(ChocolateyConfiguration config) + { + var sourceType = SourceType.normal; + Enum.TryParse(config.Sources, true, out sourceType); + config.SourceType = sourceType; + } + public void run(ChocolateyConfiguration config, Container container, bool isConsole, Action parseArgs) { var command = find_command(config, container, isConsole, parseArgs); From 0e9b3761532cad7d0df34337839d8053278bdf64 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:36:54 -0500 Subject: [PATCH 10/61] (refactor) CommandExecutor instance method Move remaining static `exec` method to instance method, recreate the static with the name `execute_static`. --- .../commands/CommandExecutorSpecs.cs | 2 +- .../commands/CommandExecutor.cs | 21 ++++++++++++++++++- .../commands/ICommandExecutor.cs | 10 +++++++++ .../commands/PowershellExecutor.cs | 2 +- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/chocolatey.tests.integration/infrastructure/commands/CommandExecutorSpecs.cs b/src/chocolatey.tests.integration/infrastructure/commands/CommandExecutorSpecs.cs index 80b2868545..b375cab433 100644 --- a/src/chocolatey.tests.integration/infrastructure/commands/CommandExecutorSpecs.cs +++ b/src/chocolatey.tests.integration/infrastructure/commands/CommandExecutorSpecs.cs @@ -47,7 +47,7 @@ public override void Context() public override void Because() { - result = CommandExecutor.execute("cmd.exe", "/c bob123123", ApplicationParameters.DefaultWaitForExitInSeconds, fileSystem.get_current_directory(), null, (s, e) => { errorOutput += e.Data; }, updateProcessPath: false); + result = commandExecutor.execute("cmd.exe", "/c bob123123", ApplicationParameters.DefaultWaitForExitInSeconds, fileSystem.get_current_directory(), null, (s, e) => { errorOutput += e.Data; }, updateProcessPath: false); } [Fact] diff --git a/src/chocolatey/infrastructure/commands/CommandExecutor.cs b/src/chocolatey/infrastructure/commands/CommandExecutor.cs index 409850d7b8..56a5cce761 100644 --- a/src/chocolatey/infrastructure/commands/CommandExecutor.cs +++ b/src/chocolatey/infrastructure/commands/CommandExecutor.cs @@ -76,7 +76,26 @@ public int execute(string process, string arguments, int waitForExitInSeconds, s return execute(process, arguments, waitForExitInSeconds, workingDirectory, null, null, updateProcessPath: true); } - public static int execute(string process, + public int execute(string process, + string arguments, + int waitForExitInSeconds, + string workingDirectory, + Action stdOutAction, + Action stdErrAction, + bool updateProcessPath + ) + { + return execute_static(process, + arguments, + waitForExitInSeconds, + file_system.get_directory_name(Assembly.GetExecutingAssembly().Location), + stdOutAction, + stdErrAction, + updateProcessPath + ); + } + + public static int execute_static(string process, string arguments, int waitForExitInSeconds, string workingDirectory, diff --git a/src/chocolatey/infrastructure/commands/ICommandExecutor.cs b/src/chocolatey/infrastructure/commands/ICommandExecutor.cs index 5e8a89dfa2..8d5226598b 100644 --- a/src/chocolatey/infrastructure/commands/ICommandExecutor.cs +++ b/src/chocolatey/infrastructure/commands/ICommandExecutor.cs @@ -30,6 +30,16 @@ int execute( Action stdOutAction, Action stdErrAction, bool updateProcessPath + ); + + int execute( + string process, + string arguments, + int waitForExitInSeconds, + string workingDirectory, + Action stdOutAction, + Action stdErrAction, + bool updateProcessPath ); } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure/commands/PowershellExecutor.cs b/src/chocolatey/infrastructure/commands/PowershellExecutor.cs index 1092a87e6a..1a630310aa 100644 --- a/src/chocolatey/infrastructure/commands/PowershellExecutor.cs +++ b/src/chocolatey/infrastructure/commands/PowershellExecutor.cs @@ -46,7 +46,7 @@ Action stdErrAction //-NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%DIR%chocolatey.ps1' %PS_ARGS%" string arguments = "-NoProfile -NoLogo -ExecutionPolicy Bypass -Command \"{0}\"".format_with(command); - return CommandExecutor.execute( + return CommandExecutor.execute_static( _powershell, arguments, waitForExitSeconds, From 6af746f5a7c55656e754238a06fe07687217bba1 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:37:34 -0500 Subject: [PATCH 11/61] (maint) removing commented code --- src/chocolatey/infrastructure.app/services/RegistryService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/RegistryService.cs b/src/chocolatey/infrastructure.app/services/RegistryService.cs index e5ba45371f..812a751e83 100644 --- a/src/chocolatey/infrastructure.app/services/RegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/RegistryService.cs @@ -206,8 +206,6 @@ public bool value_exists(string keyPath, string value) { //todo: make this check less crazy... return get_installer_keys().RegistryKeys.Any(k => k.KeyPath == keyPath); - - //return Microsoft.Win32.Registry.GetValue(keyPath, value, null) != null; } public Registry read_from_file(string filePath) From 5311ce02aa3b28d9692773f1368f234af8047530 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:40:50 -0500 Subject: [PATCH 12/61] (GH-14) INugetService implements ISourceRunner --- .../services/INugetService.cs | 67 +------------------ .../services/NugetService.cs | 10 +++ 2 files changed, 12 insertions(+), 65 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/INugetService.cs b/src/chocolatey/infrastructure.app/services/INugetService.cs index 63d8ac92bb..4a4eb79905 100644 --- a/src/chocolatey/infrastructure.app/services/INugetService.cs +++ b/src/chocolatey/infrastructure.app/services/INugetService.cs @@ -15,27 +15,10 @@ namespace chocolatey.infrastructure.app.services { - using System; - using System.Collections.Concurrent; using configuration; - using results; - public interface INugetService + public interface INugetService : ISourceRunner { - /// - /// Run list in noop mode - /// - /// The configuration. - void list_noop(ChocolateyConfiguration config); - - /// - /// Lists/searches for package against nuget related feeds. - /// - /// The configuration. - /// Should results be logged? - /// - ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults); - /// /// Run pack in noop mode. /// @@ -61,53 +44,7 @@ public interface INugetService void push_run(ChocolateyConfiguration config); /// - /// Run install in noop mode - /// - /// The configuration. - /// The action to continue with for each noop test install. - void install_noop(ChocolateyConfiguration config, Action continueAction); - - /// - /// Installs packages from NuGet related feeds - /// - /// The configuration. - /// The action to continue with when install is successful. - /// results of installs - ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction); - - /// - /// Run upgrade in noop mode - /// - /// The configuration. - /// The action to continue with for each noop test upgrade. - /// what would have upgraded - ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction); - - /// - /// Upgrades packages from NuGet related feeds - /// - /// The configuration. - /// The action to continue with when upgrade is successful. - /// results of upgrades - ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction); - - /// - /// Run uninstall in noop mode - /// - /// The configuration. - /// The action to continue with for each noop test upgrade. - void uninstall_noop(ChocolateyConfiguration config, Action continueAction); - - /// - /// Uninstalls packages from NuGet related feeds - /// - /// The configuration. - /// The action to continue with when upgrade is successful. - /// results of uninstalls - ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction); - - /// - /// Remove the rollback directory for a package if it exists + /// Remove the rollback directory for a package if it exists /// /// Name of the package. void remove_rollback_directory_if_exists(string packageName); diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 6ce9fa32a4..8a4477c4c8 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -65,6 +65,16 @@ public NugetService(IFileSystem fileSystem, ILogger nugetLogger, IChocolateyPack _filesService = filesService; } + public SourceType SourceType + { + get { return SourceType.normal; } + } + + public void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction) + { + // nothing to do. Nuget.Core is already part of Chocolatey + } + public void list_noop(ChocolateyConfiguration config) { this.Log().Info("{0} would have searched for '{1}' against the following source(s) :\"{2}\"".format_with( From 0f125569cbbc83742c0ac8da514fbb94a4af0ba1 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:48:05 -0500 Subject: [PATCH 13/61] (GH-14) Web Platform Installer source This implements the WebPI alternate source. WebPI can install itself, list and install applications from WebPI. It does not implement the ability to upgrade or uninstall applications that it installs. --- .../ApplicationParameters.cs | 23 +- .../services/WebPiService.cs | 224 +++++++++++++----- 2 files changed, 179 insertions(+), 68 deletions(-) diff --git a/src/chocolatey/infrastructure.app/ApplicationParameters.cs b/src/chocolatey/infrastructure.app/ApplicationParameters.cs index 86e9a0ab38..c6d94a64f8 100644 --- a/src/chocolatey/infrastructure.app/ApplicationParameters.cs +++ b/src/chocolatey/infrastructure.app/ApplicationParameters.cs @@ -54,6 +54,7 @@ public static class ApplicationParameters public static readonly string ChocolateyPackageInfoStoreLocation = _fileSystem.combine_paths(InstallLocation, ".chocolatey"); public static readonly string ExtensionsLocation = _fileSystem.combine_paths(InstallLocation, "extensions"); public static readonly string ChocolateyCommunityFeedPushSource = "https://chocolatey.org/"; + public static readonly string ChocolateyCommunityFeedSource = "https://chocolatey.org/api/v2/"; public static readonly string UserAgent = "Chocolatey Command Line"; public static readonly string RegistryValueInstallLocation = "InstallLocation"; public static readonly string AllPackages = "all"; @@ -86,19 +87,23 @@ public static class Messages public static readonly string NugetEventActionHeader = "Nuget called an event"; } + public static class SourceRunner + { + public static readonly string WebPiName = "Web Platform Installer"; + public static readonly string WebPiPackage = "webpicmd"; + public static readonly string WebPiExe = "webpicmd.exe"; + } + public static class OutputParser { - //todo: This becomes the WebPI parsing stuff instead - public static class Nuget + public static class WebPi { public const string PACKAGE_NAME_GROUP = "PkgName"; - public const string PACKAGE_VERSION_GROUP = "PkgVersion"; - public static readonly Regex AlreadyInstalled = new Regex(@"already installed", RegexOptions.Compiled); - public static readonly Regex NotInstalled = new Regex(@"not installed", RegexOptions.Compiled); - public static readonly Regex Installing = new Regex(@"Installing", RegexOptions.Compiled); - public static readonly Regex ResolvingDependency = new Regex(@"Attempting to resolve dependency", RegexOptions.Compiled); - public static readonly Regex PackageName = new Regex(@"'(?<{0}>[.\S]+)\s?".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); - public static readonly Regex PackageVersion = new Regex(@"(?<{0}>[\d\.]+[\-\w]*)[[)]?'".format_with(PACKAGE_VERSION_GROUP), RegexOptions.Compiled); + public static readonly Regex Installing =new Regex(@"Started installing:", RegexOptions.Compiled); + public static readonly Regex Installed = new Regex(@"Install completed \(Success\):", RegexOptions.Compiled); + public static readonly Regex AlreadyInstalled = new Regex(@"No products to be installed \(either not available or already installed\)", RegexOptions.Compiled); + //public static readonly Regex NotInstalled = new Regex(@"not installed", RegexOptions.Compiled); + public static readonly Regex PackageName = new Regex(@"'(?<{0}>[^']*)'".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); } } diff --git a/src/chocolatey/infrastructure.app/services/WebPiService.cs b/src/chocolatey/infrastructure.app/services/WebPiService.cs index 25ada7c261..88e9b6f6ea 100644 --- a/src/chocolatey/infrastructure.app/services/WebPiService.cs +++ b/src/chocolatey/infrastructure.app/services/WebPiService.cs @@ -20,26 +20,25 @@ namespace chocolatey.infrastructure.app.services using System.Collections.Generic; using System.Text.RegularExpressions; using configuration; + using domain; using infrastructure.commands; using logging; using results; - public interface IWebPiService - { - } - - //todo this is the old nuget.exe installer code that needs cleaned up for webpi - public class WebPiService : IWebPiService + public sealed class WebPiService : ISourceRunner { private readonly ICommandExecutor _commandExecutor; + private readonly INugetService _nugetService; private const string PACKAGE_NAME_TOKEN = "{{packagename}}"; - private readonly string _webPiExePath = "webpicmd"; //ApplicationParameters.Tools.NugetExe; - private readonly IDictionary _webPiListArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - private readonly IDictionary _webPiInstallArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly string _exePath = ApplicationParameters.SourceRunner.WebPiExe; + private readonly string _appName = ApplicationParameters.SourceRunner.WebPiName; + private readonly IDictionary _listArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _installArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - public WebPiService(ICommandExecutor commandExecutor) + public WebPiService(ICommandExecutor commandExecutor, INugetService nugetService) { _commandExecutor = commandExecutor; + _nugetService = nugetService; set_cmd_args_dictionaries(); } @@ -48,93 +47,179 @@ public WebPiService(ICommandExecutor commandExecutor) /// private void set_cmd_args_dictionaries() { - //set_webpi_list_dictionary(); - set_webpi_install_dictionary(); + set_webpi_list_dictionary(_listArguments); + set_webpi_install_dictionary(_installArguments); + } + + /// + /// Sets webpicmd list dictionary + /// + private void set_webpi_list_dictionary(IDictionary args) + { + args.Add("_action_", new ExternalCommandArgument {ArgumentOption = "/List", Required = true}); + args.Add("_list_option_", new ExternalCommandArgument {ArgumentOption = "/ListOption:All", Required = true}); } /// /// Sets webpicmd install dictionary /// - private void set_webpi_install_dictionary() + private void set_webpi_install_dictionary(IDictionary args) { - _webPiInstallArguments.Add("_install_", new ExternalCommandArgument {ArgumentOption = "install", Required = true}); - _webPiInstallArguments.Add("_package_name_", new ExternalCommandArgument {ArgumentOption = PACKAGE_NAME_TOKEN, Required = true}); - _webPiInstallArguments.Add("Version", new ExternalCommandArgument {ArgumentOption = "-version ",}); - _webPiInstallArguments.Add("_output_directory_", new ExternalCommandArgument + args.Add("_action_", new ExternalCommandArgument {ArgumentOption = "/Install", Required = true}); + args.Add("_accept_eula_", new ExternalCommandArgument {ArgumentOption = "/AcceptEula", Required = true}); + args.Add("_suppress_reboot_", new ExternalCommandArgument {ArgumentOption = "/SuppressReboot", Required = true}); + args.Add("_package_name_", new ExternalCommandArgument { - ArgumentOption = "-outputdirectory ", - ArgumentValue = "{0}".format_with(ApplicationParameters.PackagesLocation), - QuoteValue = true, + ArgumentOption = "/Products:", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, Required = true }); - _webPiInstallArguments.Add("Sources", new ExternalCommandArgument {ArgumentOption = "-source ", QuoteValue = true}); - _webPiInstallArguments.Add("Prerelease", new ExternalCommandArgument {ArgumentOption = "-prerelease"}); - _webPiInstallArguments.Add("_non_interactive_", new ExternalCommandArgument {ArgumentOption = "-noninteractive", Required = true}); - _webPiInstallArguments.Add("_no_cache_", new ExternalCommandArgument {ArgumentOption = "-nocache", Required = true}); + } + + public SourceType SourceType + { + get { return SourceType.webpi; } + } + + public void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction) + { + var runnerConfig = new ChocolateyConfiguration + { + PackageNames = ApplicationParameters.SourceRunner.WebPiPackage, + Sources = ApplicationParameters.PackagesLocation, + Debug = config.Debug, + Force = config.Force, + Verbose = config.Verbose, + CommandExecutionTimeoutSeconds = config.CommandExecutionTimeoutSeconds, + CacheLocation = config.CacheLocation, + RegularOutput = config.RegularOutput, + PromptForConfirmation = false, + AcceptLicense = true, + }; + runnerConfig.ListCommand.LocalOnly = true; + + var localPackages = _nugetService.list_run(runnerConfig, logResults: false); + + if (!localPackages.ContainsKey(ApplicationParameters.SourceRunner.WebPiPackage)) + { + runnerConfig.Sources = ApplicationParameters.ChocolateyCommunityFeedSource; + + var prompt = config.PromptForConfirmation; + config.PromptForConfirmation = false; + _nugetService.install_run(runnerConfig, ensureAction.Invoke); + config.PromptForConfirmation = prompt; + } + } + + public void list_noop(ChocolateyConfiguration config) + { + var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + } + + public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults) + { + var packageResults = new ConcurrentDictionary(); + var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments); + + //var whereToStartRecording = "---"; + //var whereToStopRecording = "--"; + //var recordingValues = false; + + Environment.ExitCode = _commandExecutor.execute( + _exePath, + args, + config.CommandExecutionTimeoutSeconds, + workingDirectory: ApplicationParameters.ShimsLocation, + stdOutAction: (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + if (logResults) + { + this.Log().Info(e.Data); + } + else + { + this.Log().Debug(() => "[{0}] {1}".format_with(_appName, logMessage)); + } + + //if (recordingValues) + //{ + // var lineParts = logMessage.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + // if (lineParts.Length > 1) + // { + // var pkgResult = new PackageResult(lineParts[0], null, null); + // packageResults.GetOrAdd(lineParts[0], pkgResult); + // } + //} + + //if (logMessage.Contains(whereToStartRecording)) recordingValues = true; + }, + stdErrAction: (s, e) => + { + if (string.IsNullOrWhiteSpace(e.Data)) return; + this.Log().Error(() => "{0}".format_with(e.Data)); + }, + updateProcessPath: false + ); + + return packageResults; + } + + public void install_noop(ChocolateyConfiguration config, Action continueAction) + { + var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); + args = args.Replace(PACKAGE_NAME_TOKEN, config.PackageNames.Replace(';', ',')); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); } public ConcurrentDictionary install_run(ChocolateyConfiguration configuration, Action continueAction) { var packageInstalls = new ConcurrentDictionary(); - - var args = ExternalCommandArgsBuilder.build_arguments(configuration, _webPiInstallArguments); + var args = ExternalCommandArgsBuilder.build_arguments(configuration, _installArguments); foreach (var packageToInstall in configuration.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) { var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); var exitCode = _commandExecutor.execute( - _webPiExePath, argsForPackage, configuration.CommandExecutionTimeoutSeconds, + _exePath, + argsForPackage, + configuration.CommandExecutionTimeoutSeconds, + ApplicationParameters.ShimsLocation, (s, e) => { var logMessage = e.Data; if (string.IsNullOrWhiteSpace(logMessage)) return; - this.Log().Debug(() => " [WebPI] {0}".format_with(logMessage)); - - var packageName = get_value_from_output(logMessage, ApplicationParameters.OutputParser.Nuget.PackageName, ApplicationParameters.OutputParser.Nuget.PACKAGE_NAME_GROUP); - var packageVersion = get_value_from_output(logMessage, ApplicationParameters.OutputParser.Nuget.PackageVersion, ApplicationParameters.OutputParser.Nuget.PACKAGE_VERSION_GROUP); - - if (ApplicationParameters.OutputParser.Nuget.ResolvingDependency.IsMatch(logMessage)) - { - return; - } - - var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, packageVersion, _webPiInstallArguments["_output_directory_"].ArgumentValue)); - - if (ApplicationParameters.OutputParser.Nuget.NotInstalled.IsMatch(logMessage)) - { - this.Log().Error("{0} not installed: {1}".format_with(packageName, logMessage)); - results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); - - return; - } + this.Log().Info(() => " [{0}] {1}".format_with(_appName, logMessage)); - if (ApplicationParameters.OutputParser.Nuget.Installing.IsMatch(logMessage)) + var packageName = get_value_from_output(logMessage, ApplicationParameters.OutputParser.WebPi.PackageName, ApplicationParameters.OutputParser.WebPi.PACKAGE_NAME_GROUP); + var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + if (ApplicationParameters.OutputParser.WebPi.AlreadyInstalled.IsMatch(logMessage)) { + results.Messages.Add(new ResultMessage(ResultType.Inconclusive, packageName)); + this.Log().Warn(ChocolateyLoggers.Important, " [{0}] {1} already installed or doesn't exist. --force has no effect.".format_with(_appName, string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); return; } - if (string.IsNullOrWhiteSpace(packageName)) return; - - this.Log().Info(ChocolateyLoggers.Important, "{0} {1}".format_with(packageName, !string.IsNullOrWhiteSpace(packageVersion) ? "v" + packageVersion : string.Empty)); - - if (ApplicationParameters.OutputParser.Nuget.AlreadyInstalled.IsMatch(logMessage) && !configuration.Force) + if (ApplicationParameters.OutputParser.WebPi.Installing.IsMatch(logMessage)) { - results.Messages.Add(new ResultMessage(ResultType.Inconclusive, packageName)); - this.Log().Warn(" Already installed."); - this.Log().Warn(ChocolateyLoggers.Important, " Use -force if you want to reinstall.".format_with(Environment.NewLine)); + this.Log().Info(ChocolateyLoggers.Important, "{0}".format_with(packageName)); return; } + + //if (string.IsNullOrWhiteSpace(packageName)) return; - results.Messages.Add(new ResultMessage(ResultType.Debug, ApplicationParameters.Messages.ContinueChocolateyAction)); - if (continueAction != null) + if (ApplicationParameters.OutputParser.WebPi.Installed.IsMatch(logMessage)) { - continueAction.Invoke(results); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); } }, (s, e) => { if (string.IsNullOrWhiteSpace(e.Data)) return; - this.Log().Error(() => "{0}".format_with(e.Data)); + this.Log().Error(() => "[{0}] {1}".format_with(_appName, e.Data)); }, updateProcessPath: false ); @@ -147,6 +232,27 @@ public ConcurrentDictionary install_run(ChocolateyConfigu return packageInstalls; } + public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) + { + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(_appName)); + return new ConcurrentDictionary(); + } + + public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) + { + throw new NotImplementedException("{0} does not implement upgrade".format_with(_appName)); + } + + public void uninstall_noop(ChocolateyConfiguration config, Action continueAction) + { + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement uninstall".format_with(_appName)); + } + + public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction) + { + throw new NotImplementedException("{0} does not implement uninstall".format_with(_appName)); + } + /// /// Grabs a value from the output based on the regex. /// From e2c0a594df971b158b4666b1c02a1b60de15be98 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:48:36 -0500 Subject: [PATCH 14/61] (GH-14) Container registration Add the source runners configuration --- .../registration/ContainerBinding.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index fd35fb716a..507905c2b0 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -87,6 +87,16 @@ public void RegisterComponents(Container container) }; return list.AsReadOnly(); }, Lifestyle.Singleton); + + container.Register>(() => + { + var list = new List + { + container.GetInstance(), + new WebPiService(container.GetInstance(),container.GetInstance()) + }; + return list.AsReadOnly(); + }, Lifestyle.Singleton); container.Register(Lifestyle.Singleton); EventManager.initialize_with(container.GetInstance); From 45166b2b0b2c28973fd852f0a680ce79857bf591 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:50:59 -0500 Subject: [PATCH 15/61] (GH-14) ChocolateyPackageService implement source runners ChocolateyPackageService will determine based on the ISourceRunners it has in the list on ensuring that an app/tool is installed, and then use the correct source runner to perform all applicable ISourceRunner actions. --- .../services/ChocolateyPackageService.cs | 194 ++++++++++++------ .../services/IChocolateyPackageService.cs | 7 + 2 files changed, 142 insertions(+), 59 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index 77f7b4e04d..bb69f1b248 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -33,6 +33,7 @@ public class ChocolateyPackageService : IChocolateyPackageService { private readonly INugetService _nugetService; private readonly IPowershellService _powershellService; + private readonly IEnumerable _sourceRunners; private readonly IShimGenerationService _shimgenService; private readonly IFileSystem _fileSystem; private readonly IRegistryService _registryService; @@ -42,12 +43,14 @@ public class ChocolateyPackageService : IChocolateyPackageService private readonly IXmlService _xmlService; public ChocolateyPackageService(INugetService nugetService, IPowershellService powershellService, - IShimGenerationService shimgenService, IFileSystem fileSystem, IRegistryService registryService, + IEnumerable sourceRunners, IShimGenerationService shimgenService, + IFileSystem fileSystem, IRegistryService registryService, IChocolateyPackageInformationService packageInfoService, IFilesService filesService, IAutomaticUninstallerService autoUninstallerService, IXmlService xmlService) { _nugetService = nugetService; _powershellService = powershellService; + _sourceRunners = sourceRunners; _shimgenService = shimgenService; _fileSystem = fileSystem; _registryService = registryService; @@ -57,32 +60,49 @@ public ChocolateyPackageService(INugetService nugetService, IPowershellService p _xmlService = xmlService; } - public void list_noop(ChocolateyConfiguration config) + public void ensure_source_app_installed(ChocolateyConfiguration config) + { + perform_source_runner_action(config, r => r.ensure_source_app_installed(config, (packageResult) => handle_package_result(packageResult, config, CommandNameType.install))); + } + + private void perform_source_runner_action(ChocolateyConfiguration config, Action action) { - if (config.Sources.is_equal_to(SourceType.webpi.to_string())) + var runner = _sourceRunners.FirstOrDefault(r => r.SourceType == config.SourceType); + if (runner != null && action != null) { - //todo: webpi + action.Invoke(runner); } else { - _nugetService.list_noop(config); + this.Log().Warn("No runner was found that implements source type '{0}' or action was missing".format_with(config.SourceType.to_string())); } } + private T perform_source_runner_function(ChocolateyConfiguration config, Func function) + { + var runner = _sourceRunners.FirstOrDefault(r => r.SourceType == config.SourceType); + if (runner != null && function != null) + { + return function.Invoke(runner); + } + + this.Log().Warn("No runner was found that implements source type '{0}' or function was missing.".format_with(config.SourceType.to_string())); + return default(T); + } + + public void list_noop(ChocolateyConfiguration config) + { + perform_source_runner_action(config, r => r.list_noop(config)); + } + public void list_run(ChocolateyConfiguration config, bool logResults) { this.Log().Debug(() => "Searching for package information"); - if (config.Sources.is_equal_to(SourceType.webpi.to_string())) - { - //todo: webpi - //install webpi if not installed - //run the webpi command - this.Log().Warn("Command not yet functional, stay tuned..."); - } - else + var list = perform_source_runner_function(config, r => r.list_run(config, logResults)); + + if (config.SourceType == SourceType.normal) { - var list = _nugetService.list_run(config, logResults: true); if (config.RegularOutput) { this.Log().Warn(() => @"{0} packages {1}.".format_with(list.Count, config.ListCommand.LocalOnly ? "installed" : "found")); @@ -129,21 +149,45 @@ private void report_registry_programs(ChocolateyConfiguration config, Concurrent public void pack_noop(ChocolateyConfiguration config) { + if (config.SourceType != SourceType.normal) + { + this.Log().Warn(ChocolateyLoggers.Important, "This source doesn't provide a facility for packaging."); + return; + } + _nugetService.pack_noop(config); } public void pack_run(ChocolateyConfiguration config) { + if (config.SourceType != SourceType.normal) + { + this.Log().Warn(ChocolateyLoggers.Important, "This source doesn't provide a facility for packaging."); + return; + } + _nugetService.pack_run(config); } public void push_noop(ChocolateyConfiguration config) { + if (config.SourceType != SourceType.normal) + { + this.Log().Warn(ChocolateyLoggers.Important, "This source doesn't provide a facility for pushing."); + return; + } + _nugetService.push_noop(config); } public void push_run(ChocolateyConfiguration config) { + if (config.SourceType != SourceType.normal) + { + this.Log().Warn(ChocolateyLoggers.Important, "This source doesn't provide a facility for pushing."); + return; + } + _nugetService.push_run(config); } @@ -152,7 +196,13 @@ public void install_noop(ChocolateyConfiguration config) // each package can specify its own configuration values foreach (var packageConfig in set_config_from_package_names_and_packages_config(config, new ConcurrentDictionary()).or_empty_list_if_null()) { - _nugetService.install_noop(packageConfig, (pkg) => _powershellService.install_noop(pkg)); + Action action = null; + if (packageConfig.SourceType == SourceType.normal) + { + action = (pkg) => _powershellService.install_noop(pkg); + } + + perform_source_runner_action(packageConfig, r => r.install_noop(packageConfig, action)); } } @@ -226,8 +276,6 @@ public void handle_package_result(PackageResult packageResult, ChocolateyConfigu public ConcurrentDictionary install_run(ChocolateyConfiguration config) { - //todo:are we installing from an alternate source? If so run that command instead - this.Log().Info(@"Installing the following packages:"); this.Log().Info(ChocolateyLoggers.Important, @"{0}".format_with(config.PackageNames)); this.Log().Info(@"By installing you accept licenses for the packages."); @@ -236,10 +284,13 @@ public ConcurrentDictionary install_run(ChocolateyConfigu foreach (var packageConfig in set_config_from_package_names_and_packages_config(config, packageInstalls).or_empty_list_if_null()) { - var results = _nugetService.install_run( - packageConfig, - (packageResult) => handle_package_result(packageResult, packageConfig, CommandNameType.install) - ); + Action action = null; + if (packageConfig.SourceType == SourceType.normal) + { + action = (packageResult) => handle_package_result(packageResult, packageConfig, CommandNameType.install); + } + var results = perform_source_runner_function(packageConfig, r => r.install_run(packageConfig, action)); + foreach (var result in results) { packageInstalls.GetOrAdd(result.Key, result.Value); @@ -293,6 +344,12 @@ Would have determined packages that are out of date based on what is public void outdated_run(ChocolateyConfiguration config) { + if (config.SourceType != SourceType.normal) + { + this.Log().Warn(ChocolateyLoggers.Important, "This source doesn't provide a facility for outdated."); + return; + } + this.Log().Info(ChocolateyLoggers.Important, @"Outdated Packages Output is package name | current version | available version | pinned? "); @@ -380,7 +437,13 @@ private IEnumerable get_packages_from_config(string pac public void upgrade_noop(ChocolateyConfiguration config) { - var noopUpgrades = _nugetService.upgrade_noop(config, (pkg) => _powershellService.install_noop(pkg)); + Action action = null; + if (config.SourceType == SourceType.normal) + { + action = (pkg) => _powershellService.install_noop(pkg); + } + + var noopUpgrades = perform_source_runner_function(config, r => r.upgrade_noop(config, action)); if (config.RegularOutput) { var upgradeWarnings = noopUpgrades.Count(p => p.Value.Warning); @@ -406,8 +469,6 @@ public void upgrade_noop(ChocolateyConfiguration config) public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config) { - //todo:are we upgrading an alternate source? If so run that command instead - this.Log().Info(@"Upgrading the following packages:"); this.Log().Info(ChocolateyLoggers.Important, @"{0}".format_with(config.PackageNames)); this.Log().Info(@"By upgrading you accept licenses for the packages."); @@ -417,10 +478,13 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu throw new ApplicationException("A packages.config file is only used with installs."); } - var packageUpgrades = _nugetService.upgrade_run( - config, - (packageResult) => handle_package_result(packageResult, config, CommandNameType.upgrade) - ); + Action action = null; + if (config.SourceType == SourceType.normal) + { + action = (packageResult) => handle_package_result(packageResult, config, CommandNameType.upgrade); + } + + var packageUpgrades = perform_source_runner_function(config, r => r.upgrade_run(config, action)); var upgradeFailures = packageUpgrades.Count(p => !p.Value.Success); var upgradeWarnings = packageUpgrades.Count(p => p.Value.Warning); @@ -462,7 +526,13 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu public void uninstall_noop(ChocolateyConfiguration config) { - _nugetService.uninstall_noop(config, (pkg) => _powershellService.uninstall_noop(pkg)); + Action action = null; + if (config.SourceType == SourceType.normal) + { + action = (pkg) => _powershellService.uninstall_noop(pkg); + } + + perform_source_runner_action(config, r => r.uninstall_noop(config, action)); } public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config) @@ -475,37 +545,13 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi throw new ApplicationException("A packages.config file is only used with installs."); } - var packageUninstalls = _nugetService.uninstall_run( - config, - (packageResult) => - { - if (!_fileSystem.directory_exists(packageResult.InstallLocation)) - { - packageResult.InstallLocation += ".{0}".format_with(packageResult.Package.Version.to_string()); - } - - _shimgenService.uninstall(config, packageResult); - - if (!config.SkipPackageInstallProvider) - { - _powershellService.uninstall(config, packageResult); - } - - _autoUninstallerService.run(packageResult, config); - - if (packageResult.Success) - { - //todo: v2 clean up package information store for things no longer installed (call it compact?) - uninstall_cleanup(config, packageResult); - } - else - { - this.Log().Error(ChocolateyLoggers.Important, "{0} {1} not successful.".format_with(packageResult.Name, "uninstall")); - handle_unsuccessful_operation(config, packageResult, movePackageToFailureLocation: false, attemptRollback: false); - } + Action action = null; + if (config.SourceType == SourceType.normal) + { + action = (packageResult) => handle_package_uninstall(packageResult, config); + } - //todo:prevent reboots - }); + var packageUninstalls = perform_source_runner_function(config, r => r.uninstall_run(config, action)); var uninstallFailures = packageUninstalls.Count(p => !p.Value.Success); this.Log().Warn(() => @"{0}{1} uninstalled {2}/{3} packages. {4} packages failed.{0} See the log for details ({5}).".format_with( @@ -534,6 +580,36 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi return packageUninstalls; } + public void handle_package_uninstall(PackageResult packageResult, ChocolateyConfiguration config) + { + if (!_fileSystem.directory_exists(packageResult.InstallLocation)) + { + packageResult.InstallLocation += ".{0}".format_with(packageResult.Package.Version.to_string()); + } + + _shimgenService.uninstall(config, packageResult); + + if (!config.SkipPackageInstallProvider) + { + _powershellService.uninstall(config, packageResult); + } + + _autoUninstallerService.run(packageResult, config); + + if (packageResult.Success) + { + //todo: v2 clean up package information store for things no longer installed (call it compact?) + uninstall_cleanup(config, packageResult); + } + else + { + this.Log().Error(ChocolateyLoggers.Important, "{0} {1} not successful.".format_with(packageResult.Name, "uninstall")); + handle_unsuccessful_operation(config, packageResult, movePackageToFailureLocation: false, attemptRollback: false); + } + + //todo:prevent reboots + } + private void uninstall_cleanup(ChocolateyConfiguration config, PackageResult packageResult) { _packageInfoService.remove_package_information(packageResult.Package); diff --git a/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs index 45b0e2f206..51d17671f7 100644 --- a/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs @@ -24,6 +24,13 @@ namespace chocolatey.infrastructure.app.services /// public interface IChocolateyPackageService { + + /// + /// Ensures the application that controls a source is installed + /// + /// The configuration. + void ensure_source_app_installed(ChocolateyConfiguration config); + /// /// Run list in noop mode /// From 60172999e74155930b047736808071858e8e1c79 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:52:21 -0500 Subject: [PATCH 16/61] (GH-14) ensure_source_app - called by commands For each command that has run of a command that is listed in ISourceRunners, implement a check to ensure that the application that normally runs for that tool is installed. --- .../infrastructure.app/commands/ChocolateyInstallCommand.cs | 1 + .../infrastructure.app/commands/ChocolateyListCommand.cs | 1 + .../infrastructure.app/commands/ChocolateyUninstallCommand.cs | 1 + .../infrastructure.app/commands/ChocolateyUpgradeCommand.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs index c26cbe973c..f3cf20b6df 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs @@ -171,6 +171,7 @@ public void noop(ChocolateyConfiguration configuration) public void run(ChocolateyConfiguration configuration) { + _packageService.ensure_source_app_installed(configuration); _packageService.install_run(configuration); } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs index 7d12c3cde1..fc92e3d41a 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs @@ -114,6 +114,7 @@ public void noop(ChocolateyConfiguration configuration) public void run(ChocolateyConfiguration configuration) { + _packageService.ensure_source_app_installed(configuration); _packageService.list_run(configuration, logResults: true); } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs index e1e4134a57..54b2609112 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs @@ -128,6 +128,7 @@ public void noop(ChocolateyConfiguration configuration) public void run(ChocolateyConfiguration configuration) { + _packageService.ensure_source_app_installed(configuration); _packageService.uninstall_run(configuration); } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs index 11c10bf708..a5b0763faf 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs @@ -147,6 +147,7 @@ public virtual void noop(ChocolateyConfiguration configuration) public virtual void run(ChocolateyConfiguration configuration) { + _packageService.ensure_source_app_installed(configuration); _packageService.upgrade_run(configuration); } From b02e4145050d90a4d1ee06f6cfbcf897ef21d760 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 00:53:00 -0500 Subject: [PATCH 17/61] (GH-14) Uninstall adds source When uninstalling from the special sources, those should be able to be specified. --- .../infrastructure.app/commands/ChocolateyUninstallCommand.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs index 54b2609112..a9d545efbd 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUninstallCommand.cs @@ -38,6 +38,9 @@ public ChocolateyUninstallCommand(IChocolateyPackageService packageService) public void configure_argument_parser(OptionSet optionSet, ChocolateyConfiguration configuration) { optionSet + .Add("s=|source=", + "Source - The source to find the package(s) to install. Special sources include: ruby, webpi, cygwin, windowsfeatures, and python. Defaults to default feeds.", + option => configuration.Sources = option) .Add("version=", "Version - A specific version to uninstall. Defaults to unspecified.", option => configuration.Version = option.remove_surrounding_quotes()) From dd2afd20c153b7840e9a34f32ca244e877c43f6f Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 02:04:04 -0500 Subject: [PATCH 18/61] (refactor) naming --- .../infrastructure.app/services/WebPiService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/WebPiService.cs b/src/chocolatey/infrastructure.app/services/WebPiService.cs index 88e9b6f6ea..9ae01edd40 100644 --- a/src/chocolatey/infrastructure.app/services/WebPiService.cs +++ b/src/chocolatey/infrastructure.app/services/WebPiService.cs @@ -47,14 +47,14 @@ public WebPiService(ICommandExecutor commandExecutor, INugetService nugetService /// private void set_cmd_args_dictionaries() { - set_webpi_list_dictionary(_listArguments); - set_webpi_install_dictionary(_installArguments); + set_list_dictionary(_listArguments); + set_install_dictionary(_installArguments); } /// /// Sets webpicmd list dictionary /// - private void set_webpi_list_dictionary(IDictionary args) + private void set_list_dictionary(IDictionary args) { args.Add("_action_", new ExternalCommandArgument {ArgumentOption = "/List", Required = true}); args.Add("_list_option_", new ExternalCommandArgument {ArgumentOption = "/ListOption:All", Required = true}); @@ -63,7 +63,7 @@ private void set_webpi_list_dictionary(IDictionary /// Sets webpicmd install dictionary /// - private void set_webpi_install_dictionary(IDictionary args) + private void set_install_dictionary(IDictionary args) { args.Add("_action_", new ExternalCommandArgument {ArgumentOption = "/Install", Required = true}); args.Add("_accept_eula_", new ExternalCommandArgument {ArgumentOption = "/AcceptEula", Required = true}); From 3661276fd767f35e3e9c10b741d26e0807a0a510 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 02:17:29 -0500 Subject: [PATCH 19/61] (GH-14)(refactor) move consts to WebPiService The constants for WebPI Service are only used by WebPi Service, so there is no reason to put them and all of the other special source's arguments inside of ApplicationParameters. Move those over to the actual source service. --- .../ApplicationParameters.cs | 20 -------- .../services/WebPiService.cs | 50 +++++++++++-------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/src/chocolatey/infrastructure.app/ApplicationParameters.cs b/src/chocolatey/infrastructure.app/ApplicationParameters.cs index c6d94a64f8..a546a87287 100644 --- a/src/chocolatey/infrastructure.app/ApplicationParameters.cs +++ b/src/chocolatey/infrastructure.app/ApplicationParameters.cs @@ -87,26 +87,6 @@ public static class Messages public static readonly string NugetEventActionHeader = "Nuget called an event"; } - public static class SourceRunner - { - public static readonly string WebPiName = "Web Platform Installer"; - public static readonly string WebPiPackage = "webpicmd"; - public static readonly string WebPiExe = "webpicmd.exe"; - } - - public static class OutputParser - { - public static class WebPi - { - public const string PACKAGE_NAME_GROUP = "PkgName"; - public static readonly Regex Installing =new Regex(@"Started installing:", RegexOptions.Compiled); - public static readonly Regex Installed = new Regex(@"Install completed \(Success\):", RegexOptions.Compiled); - public static readonly Regex AlreadyInstalled = new Regex(@"No products to be installed \(either not available or already installed\)", RegexOptions.Compiled); - //public static readonly Regex NotInstalled = new Regex(@"not installed", RegexOptions.Compiled); - public static readonly Regex PackageName = new Regex(@"'(?<{0}>[^']*)'".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); - } - } - private static T try_get_config(Func func, T defaultValue) { try diff --git a/src/chocolatey/infrastructure.app/services/WebPiService.cs b/src/chocolatey/infrastructure.app/services/WebPiService.cs index 9ae01edd40..02fa1cae95 100644 --- a/src/chocolatey/infrastructure.app/services/WebPiService.cs +++ b/src/chocolatey/infrastructure.app/services/WebPiService.cs @@ -30,8 +30,16 @@ public sealed class WebPiService : ISourceRunner private readonly ICommandExecutor _commandExecutor; private readonly INugetService _nugetService; private const string PACKAGE_NAME_TOKEN = "{{packagename}}"; - private readonly string _exePath = ApplicationParameters.SourceRunner.WebPiExe; - private readonly string _appName = ApplicationParameters.SourceRunner.WebPiName; + private const string EXE_PATH = "webpicmd.exe"; + private const string APP_NAME = "Web Platform Installer"; + public const string WEB_PI_PACKAGE = "webpicmd"; + public const string PACKAGE_NAME_GROUP = "PkgName"; + public static readonly Regex InstallingRegex = new Regex(@"Started installing:", RegexOptions.Compiled); + public static readonly Regex InstalledRegex = new Regex(@"Install completed \(Success\):", RegexOptions.Compiled); + public static readonly Regex AlreadyInstalledRegex = new Regex(@"No products to be installed \(either not available or already installed\)", RegexOptions.Compiled); + //public static readonly Regex NotInstalled = new Regex(@"not installed", RegexOptions.Compiled); + public static readonly Regex PackageNameRegex = new Regex(@"'(?<{0}>[^']*)'".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); + private readonly IDictionary _listArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); private readonly IDictionary _installArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); @@ -86,7 +94,7 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action

list_run(ChocolateyConfiguration config, bool logResults) @@ -128,7 +136,7 @@ public ConcurrentDictionary list_run(ChocolateyConfigurat //var recordingValues = false; Environment.ExitCode = _commandExecutor.execute( - _exePath, + EXE_PATH, args, config.CommandExecutionTimeoutSeconds, workingDirectory: ApplicationParameters.ShimsLocation, @@ -142,7 +150,7 @@ public ConcurrentDictionary list_run(ChocolateyConfigurat } else { - this.Log().Debug(() => "[{0}] {1}".format_with(_appName, logMessage)); + this.Log().Debug(() => "[{0}] {1}".format_with(APP_NAME, logMessage)); } //if (recordingValues) @@ -172,7 +180,7 @@ public void install_noop(ChocolateyConfiguration config, Action c { var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); args = args.Replace(PACKAGE_NAME_TOKEN, config.PackageNames.Replace(';', ',')); - this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + this.Log().Info("Would have run '{0} {1}'".format_with(EXE_PATH, args)); } public ConcurrentDictionary install_run(ChocolateyConfiguration configuration, Action continueAction) @@ -184,7 +192,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu { var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); var exitCode = _commandExecutor.execute( - _exePath, + EXE_PATH, argsForPackage, configuration.CommandExecutionTimeoutSeconds, ApplicationParameters.ShimsLocation, @@ -192,26 +200,24 @@ public ConcurrentDictionary install_run(ChocolateyConfigu { var logMessage = e.Data; if (string.IsNullOrWhiteSpace(logMessage)) return; - this.Log().Info(() => " [{0}] {1}".format_with(_appName, logMessage)); + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); - var packageName = get_value_from_output(logMessage, ApplicationParameters.OutputParser.WebPi.PackageName, ApplicationParameters.OutputParser.WebPi.PACKAGE_NAME_GROUP); + var packageName = get_value_from_output(logMessage, PackageNameRegex, PACKAGE_NAME_GROUP); var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); - if (ApplicationParameters.OutputParser.WebPi.AlreadyInstalled.IsMatch(logMessage)) + if (AlreadyInstalledRegex.IsMatch(logMessage)) { results.Messages.Add(new ResultMessage(ResultType.Inconclusive, packageName)); - this.Log().Warn(ChocolateyLoggers.Important, " [{0}] {1} already installed or doesn't exist. --force has no effect.".format_with(_appName, string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + this.Log().Warn(ChocolateyLoggers.Important, " [{0}] {1} already installed or doesn't exist. --force has no effect.".format_with(APP_NAME, string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); return; } - if (ApplicationParameters.OutputParser.WebPi.Installing.IsMatch(logMessage)) + if (InstallingRegex.IsMatch(logMessage)) { this.Log().Info(ChocolateyLoggers.Important, "{0}".format_with(packageName)); return; } - //if (string.IsNullOrWhiteSpace(packageName)) return; - - if (ApplicationParameters.OutputParser.WebPi.Installed.IsMatch(logMessage)) + if (InstalledRegex.IsMatch(logMessage)) { this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); } @@ -219,7 +225,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu (s, e) => { if (string.IsNullOrWhiteSpace(e.Data)) return; - this.Log().Error(() => "[{0}] {1}".format_with(_appName, e.Data)); + this.Log().Error(() => "[{0}] {1}".format_with(APP_NAME, e.Data)); }, updateProcessPath: false ); @@ -234,23 +240,23 @@ public ConcurrentDictionary install_run(ChocolateyConfigu public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) { - this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(_appName)); + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(APP_NAME)); return new ConcurrentDictionary(); } public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) { - throw new NotImplementedException("{0} does not implement upgrade".format_with(_appName)); + throw new NotImplementedException("{0} does not implement upgrade".format_with(APP_NAME)); } public void uninstall_noop(ChocolateyConfiguration config, Action continueAction) { - this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement uninstall".format_with(_appName)); + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement uninstall".format_with(APP_NAME)); } public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction) { - throw new NotImplementedException("{0} does not implement uninstall".format_with(_appName)); + throw new NotImplementedException("{0} does not implement uninstall".format_with(APP_NAME)); } ///

From 6fce571fbc97b289d9b25086fba77901fcb84d7e Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 21 May 2015 02:36:31 -0500 Subject: [PATCH 20/61] (GH-14) Ruby Gems source This implements list and install for Ruby Gems. It does not implement the ability to upgrade or uninstall applications that it installs. --- src/chocolatey/chocolatey.csproj | 1 + .../registration/ContainerBinding.cs | 3 +- .../services/RubyGemsService.cs | 274 ++++++++++++++++++ 3 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 src/chocolatey/infrastructure.app/services/RubyGemsService.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index df979ddb99..2525069c42 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -110,6 +110,7 @@ + diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index 507905c2b0..56ca6ab7ea 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -93,7 +93,8 @@ public void RegisterComponents(Container container) var list = new List { container.GetInstance(), - new WebPiService(container.GetInstance(),container.GetInstance()) + new WebPiService(container.GetInstance(),container.GetInstance()), + new RubyGemsService(container.GetInstance(),container.GetInstance()) }; return list.AsReadOnly(); }, Lifestyle.Singleton); diff --git a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs new file mode 100644 index 0000000000..0f82bb6025 --- /dev/null +++ b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs @@ -0,0 +1,274 @@ +namespace chocolatey.infrastructure.app.services +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Text.RegularExpressions; + using configuration; + using domain; + using infrastructure.commands; + using logging; + using results; + + public sealed class RubyGemsService : ISourceRunner + { + private readonly ICommandExecutor _commandExecutor; + private readonly INugetService _nugetService; + private const string PACKAGE_NAME_TOKEN = "{{packagename}}"; + private const string EXE_PATH = "cmd.exe"; + private const string APP_NAME = "Ruby Gems"; + public const string RUBY_PORTABLE_PACKAGE = "ruby.portable"; + public const string RUBY_PACKAGE = "ruby"; + public const string PACKAGE_NAME_GROUP = "PkgName"; + public static readonly Regex InstallingRegex = new Regex(@"Fetching:", RegexOptions.Compiled); + public static readonly Regex InstalledRegex = new Regex(@"Successfully installed", RegexOptions.Compiled); + public static readonly Regex ErrorNotFoundRegex = new Regex(@"ERROR: Could not find a valid gem", RegexOptions.Compiled); + public static readonly Regex PackageNameFetchingRegex = new Regex(@"Fetching: (?<{0}>.*)\-".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); + public static readonly Regex PackageNameInstalledRegex = new Regex(@"Successfully installed (?<{0}>.*)\-".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); + public static readonly Regex PackageNameErrorRegex = new Regex(@"'(?<{0}>[^']*)'".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); + + + private readonly IDictionary _listArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _installArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public RubyGemsService(ICommandExecutor commandExecutor, INugetService nugetService) + { + _commandExecutor = commandExecutor; + _nugetService = nugetService; + set_cmd_args_dictionaries(); + } + + /// + /// Set any command arguments dictionaries necessary for the service + /// + private void set_cmd_args_dictionaries() + { + set_list_dictionary(_listArguments); + set_install_dictionary(_installArguments); + } + + /// + /// Sets webpicmd list dictionary + /// + private void set_list_dictionary(IDictionary args) + { + args.Add("_cmd_c_", new ExternalCommandArgument { ArgumentOption = "/c", Required = true }); + args.Add("_gem_", new ExternalCommandArgument { ArgumentOption = "gem", Required = true }); + args.Add("_action_", new ExternalCommandArgument {ArgumentOption = "list", Required = true}); + } + + /// + /// Sets webpicmd install dictionary + /// + private void set_install_dictionary(IDictionary args) + { + args.Add("_cmd_c_", new ExternalCommandArgument {ArgumentOption = "/c", Required = true}); + args.Add("_gem_", new ExternalCommandArgument {ArgumentOption = "gem", Required = true}); + args.Add("_action_", new ExternalCommandArgument {ArgumentOption = "install", Required = true}); + args.Add("_package_name_", new ExternalCommandArgument + { + ArgumentOption = "package name ", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, + UseValueOnly = true, + Required = true + }); + + args.Add("Force", new ExternalCommandArgument + { + ArgumentOption = "-f ", + QuoteValue = false, + Required = false + }); + + args.Add("Version", new ExternalCommandArgument + { + ArgumentOption = "--version ", + QuoteValue = false, + Required = false + }); + } + + public SourceType SourceType + { + get { return SourceType.ruby; } + } + + public void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction) + { + var runnerConfig = new ChocolateyConfiguration + { + PackageNames = RUBY_PORTABLE_PACKAGE, + Sources = ApplicationParameters.PackagesLocation, + Debug = config.Debug, + Force = config.Force, + Verbose = config.Verbose, + CommandExecutionTimeoutSeconds = config.CommandExecutionTimeoutSeconds, + CacheLocation = config.CacheLocation, + RegularOutput = config.RegularOutput, + PromptForConfirmation = false, + AcceptLicense = true, + }; + runnerConfig.ListCommand.LocalOnly = true; + + var localPackages = _nugetService.list_run(runnerConfig, logResults: false); + + if (!localPackages.ContainsKey(RUBY_PACKAGE) && !localPackages.ContainsKey(RUBY_PORTABLE_PACKAGE)) + { + runnerConfig.Sources = ApplicationParameters.ChocolateyCommunityFeedSource; + + var prompt = config.PromptForConfirmation; + config.PromptForConfirmation = false; + _nugetService.install_run(runnerConfig, ensureAction.Invoke); + config.PromptForConfirmation = prompt; + } + } + + public void list_noop(ChocolateyConfiguration config) + { + var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(EXE_PATH, args)); + } + + public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults) + { + var packageResults = new ConcurrentDictionary(); + var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments); + + Environment.ExitCode = _commandExecutor.execute( + EXE_PATH, + args, + config.CommandExecutionTimeoutSeconds, + stdOutAction: (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + if (logResults) + { + this.Log().Info(e.Data); + } + else + { + this.Log().Debug(() => "[{0}] {1}".format_with(APP_NAME, logMessage)); + } + }, + stdErrAction: (s, e) => + { + if (string.IsNullOrWhiteSpace(e.Data)) return; + this.Log().Error(() => "{0}".format_with(e.Data)); + }, + updateProcessPath: false + ); + + return packageResults; + } + + public void install_noop(ChocolateyConfiguration config, Action continueAction) + { + var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); + args = args.Replace(PACKAGE_NAME_TOKEN, config.PackageNames.Replace(';', ',')); + this.Log().Info("Would have run '{0} {1}'".format_with(EXE_PATH, args)); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration configuration, Action continueAction) + { + var packageInstalls = new ConcurrentDictionary(); + var args = ExternalCommandArgsBuilder.build_arguments(configuration, _installArguments); + + foreach (var packageToInstall in configuration.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + { + var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); + var exitCode = _commandExecutor.execute( + EXE_PATH, + argsForPackage, + configuration.CommandExecutionTimeoutSeconds, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); + + if (InstallingRegex.IsMatch(logMessage)) + { + var packageName = get_value_from_output(logMessage, PackageNameFetchingRegex, PACKAGE_NAME_GROUP); + var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + this.Log().Info(ChocolateyLoggers.Important, "{0}".format_with(packageName)); + return; + } + + //if (string.IsNullOrWhiteSpace(packageName)) return; + + if (InstalledRegex.IsMatch(logMessage)) + { + var packageName = get_value_from_output(logMessage, PackageNameInstalledRegex, PACKAGE_NAME_GROUP); + var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + + var packageName = get_value_from_output(logMessage, PackageNameErrorRegex, PACKAGE_NAME_GROUP); + + if (ErrorNotFoundRegex.IsMatch(logMessage)) + { + var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Error, packageName)); + } + }, + updateProcessPath: false + ); + + if (exitCode != 0) + { + Environment.ExitCode = exitCode; + } + } + return packageInstalls; + } + + public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) + { + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(APP_NAME)); + return new ConcurrentDictionary(); + } + + public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) + { + throw new NotImplementedException("{0} does not implement upgrade".format_with(APP_NAME)); + } + + public void uninstall_noop(ChocolateyConfiguration config, Action continueAction) + { + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement uninstall".format_with(APP_NAME)); + } + + public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction) + { + throw new NotImplementedException("{0} does not implement uninstall".format_with(APP_NAME)); + } + + /// + /// Grabs a value from the output based on the regex. + /// + /// The output. + /// The regex. + /// Name of the group. + /// + private static string get_value_from_output(string output, Regex regex, string groupName) + { + var matchGroup = regex.Match(output).Groups[groupName]; + if (matchGroup != null) + { + return matchGroup.Value; + } + + return string.Empty; + } + } +} \ No newline at end of file From 95b4b90695497621f624e5a4ecdca6deb7866c2c Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 25 May 2015 21:52:38 -0500 Subject: [PATCH 21/61] (refactor) naming/comments --- .../services/RubyGemsService.cs | 23 ++++++++++--------- .../services/WebPiService.cs | 19 +++++++-------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs index 0f82bb6025..bb922cd9c0 100644 --- a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs +++ b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs @@ -48,7 +48,7 @@ private void set_cmd_args_dictionaries() } /// - /// Sets webpicmd list dictionary + /// Sets list dictionary /// private void set_list_dictionary(IDictionary args) { @@ -58,7 +58,7 @@ private void set_list_dictionary(IDictionary ar } /// - /// Sets webpicmd install dictionary + /// Sets install dictionary /// private void set_install_dictionary(IDictionary args) { @@ -170,18 +170,18 @@ public void install_noop(ChocolateyConfiguration config, Action c this.Log().Info("Would have run '{0} {1}'".format_with(EXE_PATH, args)); } - public ConcurrentDictionary install_run(ChocolateyConfiguration configuration, Action continueAction) + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) { - var packageInstalls = new ConcurrentDictionary(); - var args = ExternalCommandArgsBuilder.build_arguments(configuration, _installArguments); + var packageResults = new ConcurrentDictionary(); + var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); - foreach (var packageToInstall in configuration.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) { var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); var exitCode = _commandExecutor.execute( EXE_PATH, argsForPackage, - configuration.CommandExecutionTimeoutSeconds, + config.CommandExecutionTimeoutSeconds, (s, e) => { var logMessage = e.Data; @@ -191,7 +191,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu if (InstallingRegex.IsMatch(logMessage)) { var packageName = get_value_from_output(logMessage, PackageNameFetchingRegex, PACKAGE_NAME_GROUP); - var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); this.Log().Info(ChocolateyLoggers.Important, "{0}".format_with(packageName)); return; } @@ -201,7 +201,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu if (InstalledRegex.IsMatch(logMessage)) { var packageName = get_value_from_output(logMessage, PackageNameInstalledRegex, PACKAGE_NAME_GROUP); - var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); @@ -217,7 +217,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu if (ErrorNotFoundRegex.IsMatch(logMessage)) { - var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); results.Messages.Add(new ResultMessage(ResultType.Error, packageName)); } }, @@ -229,7 +229,8 @@ public ConcurrentDictionary install_run(ChocolateyConfigu Environment.ExitCode = exitCode; } } - return packageInstalls; + + return packageResults; } public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) diff --git a/src/chocolatey/infrastructure.app/services/WebPiService.cs b/src/chocolatey/infrastructure.app/services/WebPiService.cs index 02fa1cae95..1f880e30c4 100644 --- a/src/chocolatey/infrastructure.app/services/WebPiService.cs +++ b/src/chocolatey/infrastructure.app/services/WebPiService.cs @@ -60,7 +60,7 @@ private void set_cmd_args_dictionaries() } /// - /// Sets webpicmd list dictionary + /// Sets list dictionary /// private void set_list_dictionary(IDictionary args) { @@ -69,7 +69,7 @@ private void set_list_dictionary(IDictionary ar } /// - /// Sets webpicmd install dictionary + /// Sets install dictionary /// private void set_install_dictionary(IDictionary args) { @@ -183,18 +183,18 @@ public void install_noop(ChocolateyConfiguration config, Action c this.Log().Info("Would have run '{0} {1}'".format_with(EXE_PATH, args)); } - public ConcurrentDictionary install_run(ChocolateyConfiguration configuration, Action continueAction) + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) { - var packageInstalls = new ConcurrentDictionary(); - var args = ExternalCommandArgsBuilder.build_arguments(configuration, _installArguments); + var packageResults = new ConcurrentDictionary(); + var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); - foreach (var packageToInstall in configuration.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) { var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); var exitCode = _commandExecutor.execute( EXE_PATH, argsForPackage, - configuration.CommandExecutionTimeoutSeconds, + config.CommandExecutionTimeoutSeconds, ApplicationParameters.ShimsLocation, (s, e) => { @@ -203,7 +203,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); var packageName = get_value_from_output(logMessage, PackageNameRegex, PACKAGE_NAME_GROUP); - var results = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); if (AlreadyInstalledRegex.IsMatch(logMessage)) { results.Messages.Add(new ResultMessage(ResultType.Inconclusive, packageName)); @@ -235,7 +235,8 @@ public ConcurrentDictionary install_run(ChocolateyConfigu Environment.ExitCode = exitCode; } } - return packageInstalls; + + return packageResults; } public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) From e9b4fb2440c0e21ff68a42f6ac4b4d9e704faa30 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 25 May 2015 21:53:38 -0500 Subject: [PATCH 22/61] (GH-14) Windows Features Source - Add List - Add search by name - add install - add uninstall --- src/chocolatey/chocolatey.csproj | 1 + .../registration/ContainerBinding.cs | 1 + .../services/WindowsFeatureService.cs | 376 ++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100644 src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 2525069c42..ba82ad0c75 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -111,6 +111,7 @@ + diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index 56ca6ab7ea..25ef8839fa 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -94,6 +94,7 @@ public void RegisterComponents(Container container) { container.GetInstance(), new WebPiService(container.GetInstance(),container.GetInstance()), + new WindowsFeatureService(container.GetInstance(),container.GetInstance(),container.GetInstance()), new RubyGemsService(container.GetInstance(),container.GetInstance()) }; return list.AsReadOnly(); diff --git a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs new file mode 100644 index 0000000000..44cfc8588c --- /dev/null +++ b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs @@ -0,0 +1,376 @@ +namespace chocolatey.infrastructure.app.services +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.IO; + using System.Text.RegularExpressions; + using configuration; + using domain; + using filesystem; + using infrastructure.commands; + using logging; + using platforms; + using results; + + /// + /// Alternative Source for Enabling Windows Features + /// + /// + /// https://technet.microsoft.com/en-us/library/hh825265.aspx?f=255&MSPPError=-2147217396 + /// + public sealed class WindowsFeatureService : ISourceRunner + { + private readonly ICommandExecutor _commandExecutor; + private readonly INugetService _nugetService; + private readonly IFileSystem _fileSystem; + private const string ALL_TOKEN = "{{all}}"; + private const string PACKAGE_NAME_TOKEN = "{{packagename}}"; + private const string LOG_LEVEL_TOKEN = "{{loglevel}}"; + private const string LOG_LEVEL_INFO = "3"; + private const string LOG_LEVEL_DEBUG = "4"; + private const string FEATURES_VALUE = "/Get-Features"; + private const string FORMAT_VALUE = "/Format:Table"; + private string _exePath = string.Empty; + + private static readonly IList _exeLocations = new List + { + Environment.ExpandEnvironmentVariables("%systemroot%\\sysnative\\dism.exe"), + Environment.ExpandEnvironmentVariables("%systemroot%\\System32\\dism.exe"), + "dism.exe" + }; + + private const string APP_NAME = "Windows Features"; + public const string PACKAGE_NAME_GROUP = "PkgName"; + public static readonly Regex InstalledRegex = new Regex(@"The operation completed successfully.", RegexOptions.Compiled); + public static readonly Regex ErrorRegex = new Regex(@"Error:", RegexOptions.Compiled); + public static readonly Regex ErrorNotFoundRegex = new Regex(@"Feature name .* is unknown", RegexOptions.Compiled); + + private readonly IDictionary _listArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _installArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _uninstallArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public WindowsFeatureService(ICommandExecutor commandExecutor, INugetService nugetService, IFileSystem fileSystem) + { + _commandExecutor = commandExecutor; + _nugetService = nugetService; + _fileSystem = fileSystem; + set_cmd_args_dictionaries(); + } + + /// + /// Set any command arguments dictionaries necessary for the service + /// + private void set_cmd_args_dictionaries() + { + set_list_dictionary(_listArguments); + set_install_dictionary(_installArguments); + set_uninstall_dictionary(_uninstallArguments); + } + + /// + /// Sets list dictionary + /// + private void set_list_dictionary(IDictionary args) + { + set_common_args(args); + args.Add("_features_", new ExternalCommandArgument { ArgumentOption = FEATURES_VALUE, Required = true }); + args.Add("_format_", new ExternalCommandArgument {ArgumentOption = FORMAT_VALUE, Required = true}); + } + + /// + /// Sets install dictionary + /// + private void set_install_dictionary(IDictionary args) + { + set_common_args(args); + + args.Add("_all_", new ExternalCommandArgument { ArgumentOption = ALL_TOKEN, Required = true }); + args.Add("_feature_", new ExternalCommandArgument {ArgumentOption = "/Enable-Feature", Required = true}); + args.Add("_package_name_", new ExternalCommandArgument + { + ArgumentOption = "/FeatureName:", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, + Required = true + }); + } + + /// + /// Sets uninstall dictionary + /// + private void set_uninstall_dictionary(IDictionary args) + { + set_common_args(args); + + // uninstall feature completely in 8/2012+ - /Remove + // would need /source to bring it back + + args.Add("_feature_", new ExternalCommandArgument {ArgumentOption = "/Disable-Feature", Required = true}); + args.Add("_package_name_", new ExternalCommandArgument + { + ArgumentOption = "/FeatureName:", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, + Required = true + }); + } + + private void set_common_args(IDictionary args) + { + args.Add("_online_", new ExternalCommandArgument { ArgumentOption = "/Online", Required = true }); + args.Add("_english_", new ExternalCommandArgument { ArgumentOption = "/English", Required = true }); + args.Add("_loglevel_", new ExternalCommandArgument + { + ArgumentOption = "/LogLevel=", + ArgumentValue = LOG_LEVEL_TOKEN, + QuoteValue = false, + Required = true + }); + + args.Add("_no_restart_", new ExternalCommandArgument { ArgumentOption = "/NoRestart", Required = true }); + } + + public SourceType SourceType + { + get { return SourceType.windowsfeatures; } + } + + public void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction) + { + set_executable_path_if_not_set(); + } + + public void set_executable_path_if_not_set() + { + if (!string.IsNullOrWhiteSpace(_exePath)) return; + + foreach (var location in _exeLocations) + { + if (_fileSystem.file_exists(location)) + { + _exePath = location; + break; + } + } + + if (string.IsNullOrWhiteSpace(_exePath)) throw new FileNotFoundException("Unable to find suitable location for the executable. Searched the following locations: '{0}'".format_with(string.Join("; ", _exeLocations))); + } + + public void list_noop(ChocolateyConfiguration config) + { + set_executable_path_if_not_set(); + var args = build_args(config, _listArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + } + + public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults) + { + set_executable_path_if_not_set(); + var args = build_args(config, _listArguments); + var packageResults = new ConcurrentDictionary(); + + Environment.ExitCode = _commandExecutor.execute( + _exePath, + args, + config.CommandExecutionTimeoutSeconds, + stdOutAction: (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + if (logResults) + { + this.Log().Info(e.Data); + } + else + { + this.Log().Debug(() => "[{0}] {1}".format_with(APP_NAME, logMessage)); + } + }, + stdErrAction: (s, e) => + { + if (string.IsNullOrWhiteSpace(e.Data)) return; + this.Log().Error(() => "{0}".format_with(e.Data)); + }, + updateProcessPath: false + ); + + return packageResults; + } + + public string build_args(ChocolateyConfiguration config,IDictionary argsDictionary) + { + var args = ExternalCommandArgsBuilder.build_arguments(config, argsDictionary); + + // at least Windows 8/2012 + if (config.Information.PlatformVersion.Major >= 6 && config.Information.PlatformVersion.Minor >= 2) + { + args = args.Replace(ALL_TOKEN, "/All"); + } + else + { + args = args.Replace(ALL_TOKEN, string.Empty); + } + + if (!string.IsNullOrWhiteSpace(config.Input)) + { + args = args.Replace(FEATURES_VALUE, "/Get-FeatureInfo").Replace(FORMAT_VALUE, "/FeatureName:{0}".format_with(config.Input)); + } + + args = args.Replace(LOG_LEVEL_TOKEN, config.Debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO); + + return args; + } + + public void install_noop(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _installArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _installArguments); + var packageResults = new ConcurrentDictionary(); + + foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + { + var packageName = packageToInstall; + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageName, null, null)); + var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageName); + + //todo: detect windows feature is already enabled + /* + $checkStatement=@" + `$dismInfo=(cmd /c `"$dism /Online /Get-FeatureInfo /FeatureName:$packageName`") + if(`$dismInfo -contains 'State : Enabled') {return} + if(`$dismInfo -contains 'State : Enable Pending') {return} + "@ + */ + + var exitCode = _commandExecutor.execute( + _exePath, + argsForPackage, + config.CommandExecutionTimeoutSeconds, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + results.Messages.Add(new ResultMessage(ResultType.Error, packageName)); + } + + if (InstalledRegex.IsMatch(logMessage)) + { + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + }, + updateProcessPath: false + ); + + if (exitCode != 0) + { + Environment.ExitCode = exitCode; + } + } + + return packageResults; + } + + public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(APP_NAME)); + return new ConcurrentDictionary(); + } + + public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + throw new NotImplementedException("{0} does not implement upgrade".format_with(APP_NAME)); + } + + public void uninstall_noop(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _uninstallArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + } + + public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _uninstallArguments); + var packageResults = new ConcurrentDictionary(); + + foreach (var packageToInstall in config.PackageNames.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) + { + var packageName = packageToInstall; + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageName, null, null)); + var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageName); + + //todo: detect windows feature is already disabled + /* + $checkStatement=@" + `$dismInfo=(cmd /c `"$dism /Online /Get-FeatureInfo /FeatureName:$packageName`") + if(`$dismInfo -contains 'State : Disabled') {return} + if(`$dismInfo -contains 'State : Disable Pending') {return} + "@ + */ + + var exitCode = _commandExecutor.execute( + _exePath, + argsForPackage, + config.CommandExecutionTimeoutSeconds, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + results.Messages.Add(new ResultMessage(ResultType.Error, packageName)); + } + + if (InstalledRegex.IsMatch(logMessage)) + { + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been uninstalled successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + }, + updateProcessPath: false + ); + + if (exitCode != 0) + { + Environment.ExitCode = exitCode; + } + } + + return packageResults; + } + } +} \ No newline at end of file From 413da2b1de68c075505370fa9e15ade278b355c1 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Tue, 2 Jun 2015 14:44:31 -0500 Subject: [PATCH 23/61] (doc) WindowsFeatures - more links --- .../infrastructure.app/services/WindowsFeatureService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs index 44cfc8588c..fd77945137 100644 --- a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs +++ b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs @@ -18,6 +18,8 @@ /// /// /// https://technet.microsoft.com/en-us/library/hh825265.aspx?f=255&MSPPError=-2147217396 + /// Win 7 - https://technet.microsoft.com/en-us/library/dd744311.aspx + /// Maybe Win2003/2008 - http://www.wincert.net/forum/files/file/8-deployment-image-servicing-and-management-dism/ | http://wincert.net/leli55PK/DISM/ /// public sealed class WindowsFeatureService : ISourceRunner { From 7ae1939f734615357247c6a518c52a76a9869077 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Tue, 2 Jun 2015 15:17:54 -0500 Subject: [PATCH 24/61] (maint) formatting --- .../services/WindowsFeatureService.cs | 90 +++++++++++-------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs index fd77945137..ce91cdd8ae 100644 --- a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs +++ b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs @@ -1,4 +1,19 @@ -namespace chocolatey.infrastructure.app.services +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.services { using System; using System.Collections.Concurrent; @@ -10,16 +25,15 @@ using filesystem; using infrastructure.commands; using logging; - using platforms; using results; /// - /// Alternative Source for Enabling Windows Features + /// Alternative Source for Enabling Windows Features /// /// - /// https://technet.microsoft.com/en-us/library/hh825265.aspx?f=255&MSPPError=-2147217396 - /// Win 7 - https://technet.microsoft.com/en-us/library/dd744311.aspx - /// Maybe Win2003/2008 - http://www.wincert.net/forum/files/file/8-deployment-image-servicing-and-management-dism/ | http://wincert.net/leli55PK/DISM/ + /// https://technet.microsoft.com/en-us/library/hh825265.aspx?f=255&MSPPError=-2147217396 + /// Win 7 - https://technet.microsoft.com/en-us/library/dd744311.aspx + /// Maybe Win2003/2008 - http://www.wincert.net/forum/files/file/8-deployment-image-servicing-and-management-dism/ | http://wincert.net/leli55PK/DISM/ /// public sealed class WindowsFeatureService : ISourceRunner { @@ -76,7 +90,7 @@ private void set_cmd_args_dictionaries() private void set_list_dictionary(IDictionary args) { set_common_args(args); - args.Add("_features_", new ExternalCommandArgument { ArgumentOption = FEATURES_VALUE, Required = true }); + args.Add("_features_", new ExternalCommandArgument {ArgumentOption = FEATURES_VALUE, Required = true}); args.Add("_format_", new ExternalCommandArgument {ArgumentOption = FORMAT_VALUE, Required = true}); } @@ -85,9 +99,9 @@ private void set_list_dictionary(IDictionary ar /// private void set_install_dictionary(IDictionary args) { - set_common_args(args); + set_common_args(args); - args.Add("_all_", new ExternalCommandArgument { ArgumentOption = ALL_TOKEN, Required = true }); + args.Add("_all_", new ExternalCommandArgument {ArgumentOption = ALL_TOKEN, Required = true}); args.Add("_feature_", new ExternalCommandArgument {ArgumentOption = "/Enable-Feature", Required = true}); args.Add("_package_name_", new ExternalCommandArgument { @@ -103,7 +117,7 @@ private void set_install_dictionary(IDictionary /// private void set_uninstall_dictionary(IDictionary args) { - set_common_args(args); + set_common_args(args); // uninstall feature completely in 8/2012+ - /Remove // would need /source to bring it back @@ -120,8 +134,8 @@ private void set_uninstall_dictionary(IDictionary args) { - args.Add("_online_", new ExternalCommandArgument { ArgumentOption = "/Online", Required = true }); - args.Add("_english_", new ExternalCommandArgument { ArgumentOption = "/English", Required = true }); + args.Add("_online_", new ExternalCommandArgument {ArgumentOption = "/Online", Required = true}); + args.Add("_english_", new ExternalCommandArgument {ArgumentOption = "/English", Required = true}); args.Add("_loglevel_", new ExternalCommandArgument { ArgumentOption = "/LogLevel=", @@ -130,7 +144,7 @@ private void set_common_args(IDictionary args) Required = true }); - args.Add("_no_restart_", new ExternalCommandArgument { ArgumentOption = "/NoRestart", Required = true }); + args.Add("_no_restart_", new ExternalCommandArgument {ArgumentOption = "/NoRestart", Required = true}); } public SourceType SourceType @@ -146,7 +160,7 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action

list_run(ChocolateyConfigurat return packageResults; } - public string build_args(ChocolateyConfiguration config,IDictionary argsDictionary) + public string build_args(ChocolateyConfiguration config, IDictionary argsDictionary) { var args = ExternalCommandArgsBuilder.build_arguments(config, argsDictionary); @@ -234,9 +248,9 @@ public void install_noop(ChocolateyConfiguration config, Action c public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) { set_executable_path_if_not_set(); - var args = build_args(config, _installArguments); + var args = build_args(config, _installArguments); var packageResults = new ConcurrentDictionary(); - + foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) { var packageName = packageToInstall; @@ -319,7 +333,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi var args = build_args(config, _uninstallArguments); var packageResults = new ConcurrentDictionary(); - foreach (var packageToInstall in config.PackageNames.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) { var packageName = packageToInstall; var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageName, null, null)); @@ -339,30 +353,30 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi argsForPackage, config.CommandExecutionTimeoutSeconds, (s, e) => - { - var logMessage = e.Data; - if (string.IsNullOrWhiteSpace(logMessage)) return; - this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); - - if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) { - results.Messages.Add(new ResultMessage(ResultType.Error, packageName)); - } + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); - if (InstalledRegex.IsMatch(logMessage)) - { - results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); - this.Log().Info(ChocolateyLoggers.Important, " {0} has been uninstalled successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); - } - }, + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + results.Messages.Add(new ResultMessage(ResultType.Error, packageName)); + } + + if (InstalledRegex.IsMatch(logMessage)) + { + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been uninstalled successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, (s, e) => - { - var logMessage = e.Data; - if (string.IsNullOrWhiteSpace(logMessage)) return; - this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); - results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); - }, + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + }, updateProcessPath: false ); From 10b4f8ce799d93173d89b32eca0508bfb738dcb5 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Tue, 2 Jun 2015 16:14:18 -0500 Subject: [PATCH 25/61] (GH-14) rename registry exists to installer keys Rename the registry service `value_exists` to `installer_value_exists` to clarify what it is doing. --- .../services/AutomaticUninstallerServiceSpecs.cs | 6 +++--- .../services/AutomaticUninstallerService.cs | 4 ++-- .../infrastructure.app/services/IRegistryService.cs | 2 +- .../infrastructure.app/services/RegistryService.cs | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs index 6cfaad16a4..1cebc0d7ef 100644 --- a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs @@ -77,7 +77,7 @@ public override void Context() packageResults.GetOrAdd("regular", packageResult); fileSystem.Setup(f => f.directory_exists(registryKeys.FirstOrDefault().InstallLocation)).Returns(true); - registryService.Setup(r => r.value_exists(registryKeys.FirstOrDefault().KeyPath, ApplicationParameters.RegistryValueInstallLocation)).Returns(true); + registryService.Setup(r => r.installer_value_exists(registryKeys.FirstOrDefault().KeyPath, ApplicationParameters.RegistryValueInstallLocation)).Returns(true); fileSystem.Setup(f => f.get_full_path(expectedUninstallString)).Returns(expectedUninstallString); } } @@ -235,7 +235,7 @@ public override void Context() { base.Context(); registryService.ResetCalls(); - registryService.Setup(r => r.value_exists(registryKeys.FirstOrDefault().KeyPath, ApplicationParameters.RegistryValueInstallLocation)).Returns(false); + registryService.Setup(r => r.installer_value_exists(registryKeys.FirstOrDefault().KeyPath, ApplicationParameters.RegistryValueInstallLocation)).Returns(false); } public override void Because() @@ -264,7 +264,7 @@ public override void Context() fileSystem.ResetCalls(); fileSystem.Setup(f => f.directory_exists(registryKeys.FirstOrDefault().InstallLocation)).Returns(false); registryService.ResetCalls(); - registryService.Setup(r => r.value_exists(registryKeys.FirstOrDefault().KeyPath, ApplicationParameters.RegistryValueInstallLocation)).Returns(false); + registryService.Setup(r => r.installer_value_exists(registryKeys.FirstOrDefault().KeyPath, ApplicationParameters.RegistryValueInstallLocation)).Returns(false); } public override void Because() diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index 71997f9704..5367f46fbd 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -79,11 +79,11 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) { this.Log().Debug(() => " Preparing uninstall key '{0}'".format_with(key.UninstallString)); - if ((!string.IsNullOrWhiteSpace(key.InstallLocation) && !_fileSystem.directory_exists(key.InstallLocation)) || !_registryService.value_exists(key.KeyPath, ApplicationParameters.RegistryValueInstallLocation)) + if ((!string.IsNullOrWhiteSpace(key.InstallLocation) && !_fileSystem.directory_exists(key.InstallLocation)) || !_registryService.installer_value_exists(key.KeyPath, ApplicationParameters.RegistryValueInstallLocation)) { this.Log().Info(" Skipping auto uninstaller - The application appears to have been uninstalled already by other means."); this.Log().Debug(() => " Searched for install path '{0}' - found? {1}".format_with(key.InstallLocation.escape_curly_braces(), _fileSystem.directory_exists(key.InstallLocation))); - this.Log().Debug(() => " Searched for registry key '{0}' value '{1}' - found? {2}".format_with(key.KeyPath.escape_curly_braces(), ApplicationParameters.RegistryValueInstallLocation, _registryService.value_exists(key.KeyPath, ApplicationParameters.RegistryValueInstallLocation))); + this.Log().Debug(() => " Searched for registry key '{0}' value '{1}' - found? {2}".format_with(key.KeyPath.escape_curly_braces(), ApplicationParameters.RegistryValueInstallLocation, _registryService.installer_value_exists(key.KeyPath, ApplicationParameters.RegistryValueInstallLocation))); continue; } diff --git a/src/chocolatey/infrastructure.app/services/IRegistryService.cs b/src/chocolatey/infrastructure.app/services/IRegistryService.cs index af451bcc01..b29ed6c48f 100644 --- a/src/chocolatey/infrastructure.app/services/IRegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/IRegistryService.cs @@ -23,6 +23,6 @@ public interface IRegistryService Registry get_differences(Registry before, Registry after); void save_to_file(Registry snapshot, string filePath); Registry read_from_file(string filePath); - bool value_exists(string keyPath, string value); + bool installer_value_exists(string keyPath, string value); } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/services/RegistryService.cs b/src/chocolatey/infrastructure.app/services/RegistryService.cs index 812a751e83..51837e54f5 100644 --- a/src/chocolatey/infrastructure.app/services/RegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/RegistryService.cs @@ -202,9 +202,8 @@ public void save_to_file(Registry snapshot, string filePath) _xmlService.serialize(snapshot, filePath); } - public bool value_exists(string keyPath, string value) + public bool installer_value_exists(string keyPath, string value) { - //todo: make this check less crazy... return get_installer_keys().RegistryKeys.Any(k => k.KeyPath == keyPath); } From 58d0a394393a3492c651e90b5328dc59e8ef097c Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 11:50:59 -0500 Subject: [PATCH 26/61] (GH-14) attempt exec minimized When the process creates a window anyway, attempt to run it minimized. --- src/chocolatey/infrastructure/commands/CommandExecutor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure/commands/CommandExecutor.cs b/src/chocolatey/infrastructure/commands/CommandExecutor.cs index 56a5cce761..05f6eb39d9 100644 --- a/src/chocolatey/infrastructure/commands/CommandExecutor.cs +++ b/src/chocolatey/infrastructure/commands/CommandExecutor.cs @@ -124,7 +124,8 @@ bool updateProcessPath WorkingDirectory = workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, - CreateNoWindow = true + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Minimized }; using (var p = initialize_process()) From f4895455e9aafc063cbd574467912d39a3b00acc Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 14:43:50 -0500 Subject: [PATCH 27/61] (refactor) IFileSystem.get_current_assembly_path Move all calls to `Assembly.GetExecutingAssembly().CodeBase` path to IFileSystem instead. The code to get the curent assembly path was in quite a few locations. This moves it down to one location. --- src/chocolatey.console/Program.cs | 2 +- src/chocolatey.tests.integration/NUnitSetup.cs | 2 +- src/chocolatey.tests.integration/Scenario.cs | 2 +- .../infrastructure/cryptography/CrytpoHashProviderSpecs.cs | 2 +- src/chocolatey/infrastructure.app/ApplicationParameters.cs | 6 +++--- .../commands/ChocolateyUnpackSelfCommand.cs | 2 +- src/chocolatey/infrastructure/commands/CommandExecutor.cs | 4 ++-- .../infrastructure/commands/PowershellExecutor.cs | 2 +- .../infrastructure/filesystem/DotNetFileSystem.cs | 5 +++++ src/chocolatey/infrastructure/filesystem/IFileSystem.cs | 6 ++++++ 10 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/chocolatey.console/Program.cs b/src/chocolatey.console/Program.cs index ab64b5bf0d..77458b5a9a 100644 --- a/src/chocolatey.console/Program.cs +++ b/src/chocolatey.console/Program.cs @@ -183,7 +183,7 @@ private static void remove_old_chocolatey_exe(IFileSystem fileSystem) { try { - fileSystem.delete_file(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty) + ".old"); + fileSystem.delete_file(fileSystem.get_current_assembly_path() + ".old"); fileSystem.delete_file(fileSystem.combine_paths(AppDomain.CurrentDomain.BaseDirectory, "choco.exe.old")); } catch (Exception ex) diff --git a/src/chocolatey.tests.integration/NUnitSetup.cs b/src/chocolatey.tests.integration/NUnitSetup.cs index 4b3aa1a965..f887003ed0 100644 --- a/src/chocolatey.tests.integration/NUnitSetup.cs +++ b/src/chocolatey.tests.integration/NUnitSetup.cs @@ -57,7 +57,7 @@ private static void fix_application_parameter_variables(Container container) { var fileSystem = container.GetInstance(); - var applicationLocation = fileSystem.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)); + var applicationLocation = fileSystem.get_directory_name(fileSystem.get_current_assembly_path()); var field = typeof (ApplicationParameters).GetField("InstallLocation"); field.SetValue(null, applicationLocation); diff --git a/src/chocolatey.tests.integration/Scenario.cs b/src/chocolatey.tests.integration/Scenario.cs index 08fa4ae024..f96983da99 100644 --- a/src/chocolatey.tests.integration/Scenario.cs +++ b/src/chocolatey.tests.integration/Scenario.cs @@ -35,7 +35,7 @@ public class Scenario public static string get_top_level() { - return _fileSystem.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)); + return _fileSystem.get_directory_name(_fileSystem.get_current_assembly_path()); } public static string get_package_install_path() diff --git a/src/chocolatey.tests.integration/infrastructure/cryptography/CrytpoHashProviderSpecs.cs b/src/chocolatey.tests.integration/infrastructure/cryptography/CrytpoHashProviderSpecs.cs index 20aa0947e7..60f822664a 100644 --- a/src/chocolatey.tests.integration/infrastructure/cryptography/CrytpoHashProviderSpecs.cs +++ b/src/chocolatey.tests.integration/infrastructure/cryptography/CrytpoHashProviderSpecs.cs @@ -35,7 +35,7 @@ public override void Context() { FileSystem = new DotNetFileSystem(); Provider = new CrytpoHashProvider(FileSystem,CryptoHashProviderType.Md5); - ContextDirectory = FileSystem.combine_paths(FileSystem.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)), "context"); + ContextDirectory = FileSystem.combine_paths(FileSystem.get_directory_name(FileSystem.get_current_assembly_path()), "context"); } } diff --git a/src/chocolatey/infrastructure.app/ApplicationParameters.cs b/src/chocolatey/infrastructure.app/ApplicationParameters.cs index a546a87287..8b550c8573 100644 --- a/src/chocolatey/infrastructure.app/ApplicationParameters.cs +++ b/src/chocolatey/infrastructure.app/ApplicationParameters.cs @@ -30,12 +30,12 @@ public static class ApplicationParameters public static readonly string ChocolateyInstallEnvironmentVariableName = "ChocolateyInstall"; public static readonly string Name = "Chocolatey"; #if DEBUG - public static readonly string InstallLocation = _fileSystem.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)); + public static readonly string InstallLocation = _fileSystem.get_directory_name(_fileSystem.get_current_assembly_path()); #else - public static readonly string InstallLocation = Environment.GetEnvironmentVariable(ChocolateyInstallEnvironmentVariableName) ?? _fileSystem.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)); + public static readonly string InstallLocation = System.Environment.GetEnvironmentVariable(ChocolateyInstallEnvironmentVariableName) ?? _fileSystem.get_directory_name(_fileSystem.get_current_assembly_path()); #endif - public static readonly string CommonAppDataChocolatey = _fileSystem.combine_paths(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Name); + public static readonly string CommonAppDataChocolatey = _fileSystem.combine_paths(System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData), Name); public static readonly string LoggingLocation = _fileSystem.combine_paths(InstallLocation, "logs"); public static readonly string LoggingFile = @"chocolatey.log"; public static readonly string Log4NetConfigurationAssembly = @"chocolatey"; diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUnpackSelfCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUnpackSelfCommand.cs index 49c257fc76..5b7b9e7a23 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyUnpackSelfCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUnpackSelfCommand.cs @@ -98,7 +98,7 @@ public void run(ChocolateyConfiguration configuration) AssemblyFileExtractor.extract_all_resources_to_relative_directory( _fileSystem, assembly, - _fileSystem.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)), + _fileSystem.get_directory_name(_fileSystem.get_current_assembly_path()), folders, ApplicationParameters.ChocolateyFileResources, overwriteExisting: configuration.Force, diff --git a/src/chocolatey/infrastructure/commands/CommandExecutor.cs b/src/chocolatey/infrastructure/commands/CommandExecutor.cs index 05f6eb39d9..fd9ed43c3a 100644 --- a/src/chocolatey/infrastructure/commands/CommandExecutor.cs +++ b/src/chocolatey/infrastructure/commands/CommandExecutor.cs @@ -49,7 +49,7 @@ public static void initialize_with(Lazy file_system, Func public int execute(string process, string arguments, int waitForExitInSeconds) { - return execute(process, arguments, waitForExitInSeconds, file_system.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty))); + return execute(process, arguments, waitForExitInSeconds, file_system.get_directory_name(file_system.get_current_assembly_path())); } public int execute( @@ -64,7 +64,7 @@ public int execute( return execute(process, arguments, waitForExitInSeconds, - file_system.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)), + file_system.get_directory_name(file_system.get_current_assembly_path()), stdOutAction, stdErrAction, updateProcessPath diff --git a/src/chocolatey/infrastructure/commands/PowershellExecutor.cs b/src/chocolatey/infrastructure/commands/PowershellExecutor.cs index 1a630310aa..ef44dc84d9 100644 --- a/src/chocolatey/infrastructure/commands/PowershellExecutor.cs +++ b/src/chocolatey/infrastructure/commands/PowershellExecutor.cs @@ -50,7 +50,7 @@ Action stdErrAction _powershell, arguments, waitForExitSeconds, - workingDirectory: fileSystem.get_directory_name(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty)), + workingDirectory: fileSystem.get_directory_name(fileSystem.get_current_assembly_path()), stdOutAction: stdOutAction, stdErrAction: stdErrAction, updateProcessPath: true diff --git a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs index f2dc91eaec..f78064b550 100644 --- a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs @@ -81,6 +81,11 @@ public char get_path_directory_separator_char() return Path.DirectorySeparatorChar; } + public string get_current_assembly_path() + { + return Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty); + } + #endregion #region File diff --git a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs index 959a9205c9..971990e475 100644 --- a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs @@ -53,6 +53,12 @@ public interface IFileSystem /// char get_path_directory_separator_char(); + ///

+ /// Gets the location of the executing assembly + /// + /// The path to the executing assembly + string get_current_assembly_path(); + #endregion #region File From 4bb5d92badfaccb57999c68202b3c3a53db3d472 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 14:45:20 -0500 Subject: [PATCH 28/61] (GH-14) use case insensitive dictionaries --- .../infrastructure.app/services/NugetService.cs | 8 ++++---- .../infrastructure.app/services/RubyGemsService.cs | 6 +++--- .../infrastructure.app/services/WebPiService.cs | 6 +++--- .../infrastructure.app/services/WindowsFeatureService.cs | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 8a4477c4c8..18eb81056d 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -86,7 +86,7 @@ public void list_noop(ChocolateyConfiguration config) public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults = true) { - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var packages = NugetList.GetPackages(config, _nugetLogger).ToList(); @@ -239,7 +239,7 @@ public void install_noop(ChocolateyConfiguration config, Action c public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) { _fileSystem.create_directory_if_not_exists(ApplicationParameters.PackagesLocation); - var packageInstalls = new ConcurrentDictionary(); + var packageInstalls = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); //todo: handle all @@ -404,7 +404,7 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction, bool performAction) { _fileSystem.create_directory_if_not_exists(ApplicationParameters.PackagesLocation); - var packageInstalls = new ConcurrentDictionary(); + var packageInstalls = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); SemanticVersion version = config.Version != null ? new SemanticVersion(config.Version) : null; var packageManager = NugetCommon.GetPackageManager( @@ -754,7 +754,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction, bool performAction) { - var packageUninstalls = new ConcurrentDictionary(); + var packageUninstalls = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); SemanticVersion version = config.Version != null ? new SemanticVersion(config.Version) : null; var packageManager = NugetCommon.GetPackageManager(config, _nugetLogger, diff --git a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs index bb922cd9c0..c2bf31b382 100644 --- a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs +++ b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs @@ -132,7 +132,7 @@ public void list_noop(ChocolateyConfiguration config) public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults) { - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments); Environment.ExitCode = _commandExecutor.execute( @@ -172,7 +172,7 @@ public void install_noop(ChocolateyConfiguration config, Action c public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) { - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) @@ -236,7 +236,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) { this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(APP_NAME)); - return new ConcurrentDictionary(); + return new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); } public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) diff --git a/src/chocolatey/infrastructure.app/services/WebPiService.cs b/src/chocolatey/infrastructure.app/services/WebPiService.cs index 1f880e30c4..a04e2f027d 100644 --- a/src/chocolatey/infrastructure.app/services/WebPiService.cs +++ b/src/chocolatey/infrastructure.app/services/WebPiService.cs @@ -128,7 +128,7 @@ public void list_noop(ChocolateyConfiguration config) public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults) { - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments); //var whereToStartRecording = "---"; @@ -185,7 +185,7 @@ public void install_noop(ChocolateyConfiguration config, Action c public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) { - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) @@ -242,7 +242,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) { this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(APP_NAME)); - return new ConcurrentDictionary(); + return new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); } public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) diff --git a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs index ce91cdd8ae..46b18cafef 100644 --- a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs +++ b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs @@ -184,7 +184,7 @@ public ConcurrentDictionary list_run(ChocolateyConfigurat { set_executable_path_if_not_set(); var args = build_args(config, _listArguments); - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); Environment.ExitCode = _commandExecutor.execute( _exePath, @@ -249,7 +249,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu { set_executable_path_if_not_set(); var args = build_args(config, _installArguments); - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) { @@ -311,7 +311,7 @@ public ConcurrentDictionary upgrade_noop(ChocolateyConfig { set_executable_path_if_not_set(); this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(APP_NAME)); - return new ConcurrentDictionary(); + return new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); } public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) @@ -331,7 +331,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi { set_executable_path_if_not_set(); var args = build_args(config, _uninstallArguments); - var packageResults = new ConcurrentDictionary(); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) { From 31e3f9cdb905732a45d2436db1949bf53f65c73f Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 14:45:46 -0500 Subject: [PATCH 29/61] (doc) add returns statement --- src/chocolatey/infrastructure/filesystem/IFileSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs index 971990e475..daae73d06b 100644 --- a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs @@ -245,7 +245,7 @@ public interface IFileSystem /// /// Gets the current working directory of the application. /// - /// + /// The path to the directory string get_current_directory(); /// From 0f6bc806bd1dea1f4c4df0fe814bd1e4944db66f Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 16:30:01 -0500 Subject: [PATCH 30/61] (GH-14) Find path to executable Provide a way to determine if a command / executable is on the file system. It starts by getting the path extensions `PATHEXT` environment variable and using those extensions combined with the executable name (unless an extension is provided as part of the name) and searching for the file in all path locations, starting at current directory and moving out from there in the order things would be searched for by the system. If nothing is found, we just return the name that was passed into the method. --- .../filesystem/DotNetFileSystemSpecs.cs | 48 +++++++ .../filesystem/DotNetFileSystemSpecs.cs | 135 ++++++++++++++++-- .../ApplicationParameters.cs | 7 + .../infrastructure/adapters/Environment.cs | 5 + .../infrastructure/adapters/IEnvironment.cs | 7 + .../filesystem/DotNetFileSystem.cs | 73 +++++++++- .../infrastructure/filesystem/IFileSystem.cs | 8 ++ 7 files changed, 267 insertions(+), 16 deletions(-) diff --git a/src/chocolatey.tests.integration/infrastructure/filesystem/DotNetFileSystemSpecs.cs b/src/chocolatey.tests.integration/infrastructure/filesystem/DotNetFileSystemSpecs.cs index 653fb137c3..1fcbdc4c4c 100644 --- a/src/chocolatey.tests.integration/infrastructure/filesystem/DotNetFileSystemSpecs.cs +++ b/src/chocolatey.tests.integration/infrastructure/filesystem/DotNetFileSystemSpecs.cs @@ -21,6 +21,7 @@ namespace chocolatey.tests.integration.infrastructure.filesystem using NUnit.Framework; using Should; using chocolatey.infrastructure.filesystem; + using chocolatey.infrastructure.platforms; public class DotNetFileSystemSpecs { @@ -48,6 +49,53 @@ public override void Context() } } + [Category("Integration")] + public class when_finding_paths_to_executables_with_dotNetFileSystem : DotNetFileSystemSpecsBase + { + public override void Because() + { + } + + [Fact] + public void GetExecutablePath_should_find_existing_executable() + { + FileSystem.get_executable_path("cmd").ShouldEqual( + Platform.get_platform() == PlatformType.Windows ? + "C:\\Windows\\system32\\cmd.exe" + : "cmd" + ); + } + + [Fact] + public void GetExecutablePath_should_find_existing_executable_with_extension() + { + FileSystem.get_executable_path("cmd.exe").ShouldEqual( + Platform.get_platform() == PlatformType.Windows ? + "C:\\Windows\\system32\\cmd.exe" + : "cmd" + ); + } + + [Fact] + public void GetExecutablePath_should_return_same_value_when_executable_is_not_found() + { + FileSystem.get_executable_path("daslakjsfdasdfwea").ShouldEqual("daslakjsfdasdfwea"); + } + + [Fact] + public void GetExecutablePath_should_return_empty_string_when_value_is_null() + { + FileSystem.get_executable_path(null).ShouldEqual(string.Empty); + } + + [Fact] + public void GetExecutablePath_should_return_empty_string_when_value_is_empty_string() + { + FileSystem.get_executable_path(string.Empty).ShouldEqual(string.Empty); + } + + } + [Category("Integration")] public class when_doing_file_system_operations_with_dotNetFileSystem : DotNetFileSystemSpecsBase { diff --git a/src/chocolatey.tests/infrastructure/filesystem/DotNetFileSystemSpecs.cs b/src/chocolatey.tests/infrastructure/filesystem/DotNetFileSystemSpecs.cs index f00fb5b6d0..fd3c1b8a17 100644 --- a/src/chocolatey.tests/infrastructure/filesystem/DotNetFileSystemSpecs.cs +++ b/src/chocolatey.tests/infrastructure/filesystem/DotNetFileSystemSpecs.cs @@ -17,7 +17,10 @@ namespace chocolatey.tests.infrastructure.filesystem { using System; using System.IO; + using Moq; using Should; + using chocolatey.infrastructure.adapters; + using chocolatey.infrastructure.app; using chocolatey.infrastructure.filesystem; using chocolatey.infrastructure.platforms; @@ -73,8 +76,8 @@ public void GetExtension_should_return_the_extension_of_the_filename_even_with_a public void GetDirectoryName_should_return_the_directory_of_the_path_to_the_file() { FileSystem.get_directory_name("C:\\temp\\test.txt").ShouldEqual( - Platform.get_platform() == PlatformType.Windows ? - "C:\\temp" + Platform.get_platform() == PlatformType.Windows ? + "C:\\temp" : "C:/temp"); } @@ -82,8 +85,8 @@ public void GetDirectoryName_should_return_the_directory_of_the_path_to_the_file public void Combine_should_combine_the_file_paths_of_all_the_included_items_together() { FileSystem.combine_paths("C:\\temp", "yo", "filename.txt").ShouldEqual( - Platform.get_platform() == PlatformType.Windows ? - "C:\\temp\\yo\\filename.txt" + Platform.get_platform() == PlatformType.Windows ? + "C:\\temp\\yo\\filename.txt" : "C:/temp/yo/filename.txt"); } @@ -91,8 +94,8 @@ public void Combine_should_combine_the_file_paths_of_all_the_included_items_toge public void Combine_should_combine_when_paths_have_backslashes_in_subpaths() { FileSystem.combine_paths("C:\\temp", "yo\\timmy", "filename.txt").ShouldEqual( - Platform.get_platform() == PlatformType.Windows ? - "C:\\temp\\yo\\timmy\\filename.txt" + Platform.get_platform() == PlatformType.Windows ? + "C:\\temp\\yo\\timmy\\filename.txt" : "C:/temp/yo/timmy/filename.txt"); } @@ -100,19 +103,129 @@ public void Combine_should_combine_when_paths_have_backslashes_in_subpaths() public void Combine_should_combine_when_paths_start_with_backslashes_in_subpaths() { FileSystem.combine_paths("C:\\temp", "\\yo", "filename.txt").ShouldEqual( - Platform.get_platform() == PlatformType.Windows ? - "C:\\temp\\yo\\filename.txt" + Platform.get_platform() == PlatformType.Windows ? + "C:\\temp\\yo\\filename.txt" : "C:/temp/yo/filename.txt"); } - + [Fact] public void Combine_should_combine_when_paths_start_with_forwardslashes_in_subpaths() { FileSystem.combine_paths("C:\\temp", "/yo", "filename.txt").ShouldEqual( - Platform.get_platform() == PlatformType.Windows ? - "C:\\temp\\yo\\filename.txt" + Platform.get_platform() == PlatformType.Windows ? + "C:\\temp\\yo\\filename.txt" : "C:/temp/yo/filename.txt"); } } + + public class when_finding_paths_to_executables_with_dotNetFileSystem : DotNetFileSystemSpecsBase + { + public Mock _environment = new Mock(); + + public override void Context() + { + base.Context(); + _environment.Setup(x => x.GetEnvironmentVariable(ApplicationParameters.Environment.PathExtensions)).Returns(".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL"); + _environment.Setup(x => x.GetEnvironmentVariable(ApplicationParameters.Environment.Path)).Returns( + @"C:\ProgramData\Chocolatey\bin{0}C:\Program Files\Microsoft\Web Platform Installer\{0}C:\Users\yes\AppData\Roaming\Boxstarter{0}C:\tools\ChocolateyPackageUpdater{0}C:\Windows\system32{0}C:\Windows{0}C:\Windows\System32\Wbem{0}C:\Windows\System32\WindowsPowerShell\v1.0\{0}" + .format_with(Path.PathSeparator) + ); + FileSystem.initialize_with(new Lazy(() => _environment.Object)); + } + + public override void Because() + { + } + + private void reset() + { + _environment.ResetCalls(); + } + + [Fact] + public void GetExecutablePath_should_find_existing_executable() + { + FileSystem.get_executable_path("cmd").ShouldEqual( + Platform.get_platform() == PlatformType.Windows ? + "C:\\Windows\\system32\\cmd.exe" + : "cmd" + ); + } + + [Fact] + public void GetExecutablePath_should_find_existing_executable_with_extension() + { + FileSystem.get_executable_path("cmd.exe").ShouldEqual( + Platform.get_platform() == PlatformType.Windows ? + "C:\\Windows\\system32\\cmd.exe" + : "cmd.exe" + ); + } + + [Fact] + public void GetExecutablePath_should_return_same_value_when_executable_is_not_found() + { + FileSystem.get_executable_path("daslakjsfdasdfwea").ShouldEqual("daslakjsfdasdfwea"); + } + + [Fact] + public void GetExecutablePath_should_return_empty_string_when_value_is_null() + { + FileSystem.get_executable_path(null).ShouldEqual(string.Empty); + } + + [Fact] + public void GetExecutablePath_should_return_empty_string_when_value_is_empty_string() + { + FileSystem.get_executable_path(string.Empty).ShouldEqual(string.Empty); + } + } + + public class when_finding_paths_to_executables_with_dotNetFileSystem_with_empty_path_extensions : DotNetFileSystemSpecsBase + { + public Mock _environment = new Mock(); + + public override void Context() + { + base.Context(); + _environment.Setup(x => x.GetEnvironmentVariable(ApplicationParameters.Environment.PathExtensions)).Returns(string.Empty); + _environment.Setup(x => x.GetEnvironmentVariable(ApplicationParameters.Environment.Path)).Returns( + "/usr/local/bin{0}/usr/bin/{0}/bin{0}/usr/sbin{0}/sbin" + .format_with(Path.PathSeparator) + ); + FileSystem.initialize_with(new Lazy(() => _environment.Object)); + } + + public override void Because() + { + } + + [Fact] + public void GetExecutablePath_should_find_existing_executable() + { + FileSystem.get_executable_path("ls").ShouldEqual( + Platform.get_platform() != PlatformType.Windows ? + "/bin/ls" + : "ls"); + } + + [Fact] + public void GetExecutablePath_should_return_same_value_when_executable_is_not_found() + { + FileSystem.get_executable_path("daslakjsfdasdfwea").ShouldEqual("daslakjsfdasdfwea"); + } + + [Fact] + public void GetExecutablePath_should_return_empty_string_when_value_is_null() + { + FileSystem.get_executable_path(null).ShouldEqual(string.Empty); + } + + [Fact] + public void GetExecutablePath_should_return_empty_string_when_value_is_empty_string() + { + FileSystem.get_executable_path(string.Empty).ShouldEqual(string.Empty); + } + } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/ApplicationParameters.cs b/src/chocolatey/infrastructure.app/ApplicationParameters.cs index 8b550c8573..e635f880de 100644 --- a/src/chocolatey/infrastructure.app/ApplicationParameters.cs +++ b/src/chocolatey/infrastructure.app/ApplicationParameters.cs @@ -59,6 +59,13 @@ public static class ApplicationParameters public static readonly string RegistryValueInstallLocation = "InstallLocation"; public static readonly string AllPackages = "all"; + public static class Environment + { + public static readonly string Path = "Path"; + public static readonly string PathExtensions = "PATHEXT"; + public static readonly string PathExtensionsSeparator = ";"; + } + /// /// Default is 45 minutes /// diff --git a/src/chocolatey/infrastructure/adapters/Environment.cs b/src/chocolatey/infrastructure/adapters/Environment.cs index 5013454ccb..be5b5fca87 100644 --- a/src/chocolatey/infrastructure/adapters/Environment.cs +++ b/src/chocolatey/infrastructure/adapters/Environment.cs @@ -38,5 +38,10 @@ public string NewLine { get { return System.Environment.NewLine; } } + + public string GetEnvironmentVariable(string variable) + { + return System.Environment.GetEnvironmentVariable(variable); + } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure/adapters/IEnvironment.cs b/src/chocolatey/infrastructure/adapters/IEnvironment.cs index 476868b6cf..60549199a7 100644 --- a/src/chocolatey/infrastructure/adapters/IEnvironment.cs +++ b/src/chocolatey/infrastructure/adapters/IEnvironment.cs @@ -62,6 +62,13 @@ public interface IEnvironment /// /// 1 string NewLine { get; } + + /// + /// Gets the environment variable. + /// + /// The variable. + /// + string GetEnvironmentVariable(string variable); } // ReSharper restore InconsistentNaming diff --git a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs index f78064b550..4184662d86 100644 --- a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs @@ -17,14 +17,19 @@ namespace chocolatey.infrastructure.filesystem { using System; using System.Collections.Generic; + using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; + using adapters; + using app; using platforms; using tolerance; + using Assembly = adapters.Assembly; + using Environment = adapters.Environment; /// /// Implementation of IFileSystem for Dot Net @@ -33,14 +38,26 @@ namespace chocolatey.infrastructure.filesystem public sealed class DotNetFileSystem : IFileSystem { private readonly int TIMES_TO_TRY_OPERATION = 3; + private static Lazy environment_initializer = new Lazy(() => new Environment()); private void allow_retries(Action action) { FaultTolerance.retry( - TIMES_TO_TRY_OPERATION, - action, - waitDurationMilliseconds: 200, - increaseRetryByMilliseconds: 100); + TIMES_TO_TRY_OPERATION, + action, + waitDurationMilliseconds: 200, + increaseRetryByMilliseconds: 100); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void initialize_with(Lazy environment) + { + environment_initializer = environment; + } + + private static IEnvironment Environment + { + get { return environment_initializer.Value; } } #region Path @@ -81,6 +98,52 @@ public char get_path_directory_separator_char() return Path.DirectorySeparatorChar; } + public char get_path_separator() + { + return Path.PathSeparator; + } + + public string get_executable_path(string executableName) + { + if (string.IsNullOrWhiteSpace(executableName)) return string.Empty; + + var isWindows = Platform.get_platform() == PlatformType.Windows; + IList extensions = new List(); + + if (get_file_name_without_extension(executableName).is_equal_to(executableName) && isWindows) + { + var pathExtensions = Environment.GetEnvironmentVariable(ApplicationParameters.Environment.PathExtensions).to_string().Split(new[] {ApplicationParameters.Environment.PathExtensionsSeparator}, StringSplitOptions.RemoveEmptyEntries); + foreach (var extension in pathExtensions.or_empty_list_if_null()) + { + extensions.Add(extension.StartsWith(".") ? extension : ".{0}".format_with(extension)); + } + } + + // Always add empty, for when the executable name is enough. + extensions.Add(string.Empty); + + // Gets the path to an executable based on looking in current + // working directory, next to the running process, then among the + // derivatives of Path and Pathext variables, applied in order. + var searchPaths = new List(); + searchPaths.Add(get_current_directory()); + searchPaths.Add(get_directory_name(get_current_assembly_path())); + searchPaths.AddRange(Environment.GetEnvironmentVariable(ApplicationParameters.Environment.Path).to_string().Split(new[] { get_path_separator() }, StringSplitOptions.RemoveEmptyEntries)); + + foreach (var path in searchPaths.or_empty_list_if_null()) + { + foreach (var extension in extensions.or_empty_list_if_null()) + { + var possiblePath = combine_paths(path, "{0}{1}".format_with(executableName, extension.to_lower())); + if (file_exists(possiblePath)) return possiblePath; + } + } + + // If not found, return the same as passed in - it may work, + // but possibly not. + return executableName; + } + public string get_current_assembly_path() { return Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty); @@ -130,7 +193,7 @@ public FileInfo get_file_info_for(string filePath) return new FileInfo(filePath); } - public DateTime get_file_modified_date(string filePath) + public System.DateTime get_file_modified_date(string filePath) { return new FileInfo(filePath).LastWriteTime; } diff --git a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs index daae73d06b..f7b5613481 100644 --- a/src/chocolatey/infrastructure/filesystem/IFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/IFileSystem.cs @@ -53,6 +53,14 @@ public interface IFileSystem /// char get_path_directory_separator_char(); + /// + /// Gets the path to an executable based on looking in current directory, next to the running process, then among the derivatives of Path and Pathext variables + /// + /// Name of the executable. + /// Based loosely on http://stackoverflow.com/a/5471032/18475 + /// + string get_executable_path(string executableName); + /// /// Gets the location of the executing assembly /// From b09644e00c925d6c17ad01a578fb726d07ccb4a4 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 16:38:58 -0500 Subject: [PATCH 31/61] (maint) formatting --- .../infrastructure.app/registration/ContainerBinding.cs | 2 +- src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index 25ef8839fa..bf47635cf3 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -87,7 +87,7 @@ public void RegisterComponents(Container container) }; return list.AsReadOnly(); }, Lifestyle.Singleton); - + container.Register>(() => { var list = new List diff --git a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs index 4184662d86..8d636b4822 100644 --- a/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs +++ b/src/chocolatey/infrastructure/filesystem/DotNetFileSystem.cs @@ -157,11 +157,11 @@ public IEnumerable get_files(string directoryPath, string pattern = "*.* { return Directory.EnumerateFiles(directoryPath, pattern, option); } - + public IEnumerable get_files(string directoryPath, string[] extensions, SearchOption option = SearchOption.TopDirectoryOnly) { return Directory.EnumerateFiles(directoryPath, "*.*", option) - .Where(f => extensions.Any(x => f.EndsWith(x,StringComparison.OrdinalIgnoreCase))); + .Where(f => extensions.Any(x => f.EndsWith(x, StringComparison.OrdinalIgnoreCase))); } public bool file_exists(string filePath) From 00d35b19d7dd458b5bedd872ef5206195f4b7156 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 16:52:25 -0500 Subject: [PATCH 32/61] (GH-14) RegistryService.get_key() Get registry keys and values from the registry. --- src/chocolatey.tests/chocolatey.tests.csproj | 1 + .../services/RegistryServiceSpecs.cs | 150 ++++++++++++++++++ .../services/IRegistryService.cs | 4 +- .../services/RegistryService.cs | 25 +++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs diff --git a/src/chocolatey.tests/chocolatey.tests.csproj b/src/chocolatey.tests/chocolatey.tests.csproj index 9033d98df5..2d2e98c5ef 100644 --- a/src/chocolatey.tests/chocolatey.tests.csproj +++ b/src/chocolatey.tests/chocolatey.tests.csproj @@ -80,6 +80,7 @@ + diff --git a/src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs new file mode 100644 index 0000000000..5d4bdda5a1 --- /dev/null +++ b/src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs @@ -0,0 +1,150 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.tests.infrastructure.app.services +{ + using System; + using Microsoft.Win32; + using Moq; + using Should; + using chocolatey.infrastructure.app.services; + using chocolatey.infrastructure.filesystem; + using chocolatey.infrastructure.services; + using Registry = chocolatey.infrastructure.app.domain.Registry; + + public class RegistryServiceSpecs + { + public abstract class RegistryServiceSpecsBase : TinySpec + { + protected RegistryService Service; + protected Mock FileSystem = new Mock(); + protected Mock XmlService = new Mock(); + + public override void Context() + { + reset(); + Service = new RegistryService(XmlService.Object, FileSystem.Object); + } + + protected void reset() + { + FileSystem.ResetCalls(); + XmlService.ResetCalls(); + MockLogger.reset(); + } + } + + + + public class when_RegistryService_get_installer_keys_is_called : RegistryServiceSpecsBase + { + private Registry _result; + + public override void Context() + { + base.Context(); + } + + public override void Because() + { + _result = Service.get_installer_keys(); + } + + + [Fact] + public void should_not_be_null() + { + _result.ShouldNotBeNull(); + } + + } + + public class when_RegistryService_get_key_is_called_for_a_value_that_exists : RegistryServiceSpecsBase + { + //todo does any of this test correctly on *nix? + + private RegistryKey _result; + private RegistryHive _hive = RegistryHive.CurrentUser; + private string _subkeyPath = "Console"; + + public override void Context() + { + base.Context(); + } + + public override void Because() + { + _result = Service.get_key(_hive, _subkeyPath); + } + + [Fact] + public void should_return_a_non_null_value() + { + _result.ShouldNotBeNull(); + } + + [Fact] + public void should_return_a_value_of_type_RegistryKey() + { + _result.ShouldBeType(); + } + + [Fact] + public void should_contain_keys() + { + _result.GetSubKeyNames().ShouldNotBeEmpty(); + } + + [Fact] + public void should_contain_values() + { + _result.GetValueNames().ShouldNotBeEmpty(); + } + } + + public class when_RegistryService_get_key_is_called_for_a_value_that_does_not_exist : RegistryServiceSpecsBase + { + //todo does any of this test correctly on *nix? + + private RegistryKey _result; + private RegistryHive _hive = RegistryHive.CurrentUser; + private string _subkeyPath = "Software\\alsdjfalskjfaasdfasdf"; + + public override void Context() + { + base.Context(); + } + + public override void Because() + { + _result = Service.get_key(_hive, _subkeyPath); + } + + [Fact] + public void should_not_error() + { + //nothing to see here + } + + [Fact] + public void should_return_null_key() + { + _result.ShouldBeNull(); + } + } + + //todo a subkey that exists only in 32 bit mode (must create it to test it though) + } +} \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/services/IRegistryService.cs b/src/chocolatey/infrastructure.app/services/IRegistryService.cs index b29ed6c48f..47adab543a 100644 --- a/src/chocolatey/infrastructure.app/services/IRegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/IRegistryService.cs @@ -15,7 +15,8 @@ namespace chocolatey.infrastructure.app.services { - using domain; + using Microsoft.Win32; + using Registry = domain.Registry; public interface IRegistryService { @@ -24,5 +25,6 @@ public interface IRegistryService void save_to_file(Registry snapshot, string filePath); Registry read_from_file(string filePath); bool installer_value_exists(string keyPath, string value); + RegistryKey get_key(RegistryHive hive, string subKeyPath); } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/services/RegistryService.cs b/src/chocolatey/infrastructure.app/services/RegistryService.cs index 51837e54f5..b49c1db7ec 100644 --- a/src/chocolatey/infrastructure.app/services/RegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/RegistryService.cs @@ -216,5 +216,30 @@ public Registry read_from_file(string filePath) return _xmlService.deserialize(filePath); } + + public RegistryKey get_key(RegistryHive hive, string subKeyPath) + { + IList keyLocations = new List(); + if (Environment.Is64BitOperatingSystem) + { + keyLocations.Add(RegistryKey.OpenBaseKey(hive, RegistryView.Registry64)); + } + + keyLocations.Add(RegistryKey.OpenBaseKey(hive, RegistryView.Registry32)); + + foreach (var topLevelRegistryKey in keyLocations) + { + using (topLevelRegistryKey) + { + var key = topLevelRegistryKey.OpenSubKey(subKeyPath, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey); + if (key != null) + { + return key; + } + } + } + + return null; + } } } \ No newline at end of file From c133229c709cfcb400e1f3c0071c92302448ab00 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 3 Jun 2015 16:56:30 -0500 Subject: [PATCH 33/61] (GH-14) Cygwin Alternate source Provide the ability to install with cygwin, but not much else. The executable unfortunately doesn't also execute without popping a window, which is very unfortunate. I will probably look at using the cyg-get wrapper instead. --- src/chocolatey/chocolatey.csproj | 1 + .../registration/ContainerBinding.cs | 7 +- .../services/CygwinService.cs | 290 ++++++++++++++++++ 3 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 src/chocolatey/infrastructure.app/services/CygwinService.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index ba82ad0c75..52069d46ad 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -107,6 +107,7 @@ + diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index bf47635cf3..a5cdb31b06 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -93,9 +93,10 @@ public void RegisterComponents(Container container) var list = new List { container.GetInstance(), - new WebPiService(container.GetInstance(),container.GetInstance()), - new WindowsFeatureService(container.GetInstance(),container.GetInstance(),container.GetInstance()), - new RubyGemsService(container.GetInstance(),container.GetInstance()) + new WebPiService(container.GetInstance(), container.GetInstance()), + new WindowsFeatureService(container.GetInstance(), container.GetInstance(), container.GetInstance()), + new CygwinService(container.GetInstance(), container.GetInstance(), container.GetInstance(), container.GetInstance()), + new RubyGemsService(container.GetInstance(), container.GetInstance()) }; return list.AsReadOnly(); }, Lifestyle.Singleton); diff --git a/src/chocolatey/infrastructure.app/services/CygwinService.cs b/src/chocolatey/infrastructure.app/services/CygwinService.cs new file mode 100644 index 0000000000..bb393519c0 --- /dev/null +++ b/src/chocolatey/infrastructure.app/services/CygwinService.cs @@ -0,0 +1,290 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.services +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Text.RegularExpressions; + using Microsoft.Win32; + using configuration; + using domain; + using filesystem; + using infrastructure.commands; + using logging; + using results; + + /// + /// Alternative Source for Cygwin + /// + /// + /// https://cygwin.com/faq/faq.html#faq.setup.cli + /// + public sealed class CygwinService : ISourceRunner + { + private readonly ICommandExecutor _commandExecutor; + private readonly INugetService _nugetService; + private readonly IFileSystem _fileSystem; + private readonly IRegistryService _registryService; + private const string PACKAGE_NAME_TOKEN = "{{packagename}}"; + private const string INSTALL_ROOT_TOKEN = "{{installroot}}"; + public const string CYGWIN_PACKAGE = "cygwin"; + private string _rootDirectory = string.Empty; + + private const string APP_NAME = "Cygwin"; + public const string PACKAGE_NAME_GROUP = "PkgName"; + public static readonly Regex InstalledRegex = new Regex(@"Extracting from file", RegexOptions.Compiled); + public static readonly Regex PackageNameRegex = new Regex(@"/(?<{0}>[^/]*).tar.".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); + + private readonly IDictionary _installArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public CygwinService(ICommandExecutor commandExecutor, INugetService nugetService, IFileSystem fileSystem, IRegistryService registryService) + { + _commandExecutor = commandExecutor; + _nugetService = nugetService; + _fileSystem = fileSystem; + _registryService = registryService; + set_cmd_args_dictionaries(); + } + + /// + /// Set any command arguments dictionaries necessary for the service + /// + private void set_cmd_args_dictionaries() + { + set_install_dictionary(_installArguments); + } + + /// + /// Sets install dictionary + /// + private void set_install_dictionary(IDictionary args) + { + //args.Add("_cmd_c_", new ExternalCommandArgument { ArgumentOption = "/c", Required = true }); + //args.Add("_app_", new ExternalCommandArgument + //{ + // ArgumentOption = "", + // ArgumentValue = _fileSystem.combine_paths(INSTALL_ROOT_TOKEN, "cygwinsetup.exe"), + // QuoteValue = false, + // UseValueOnly = true, + // Required = true + //}); + args.Add("_quiet_", new ExternalCommandArgument {ArgumentOption = "--quiet-mode", Required = true}); + args.Add("_no_desktop_", new ExternalCommandArgument {ArgumentOption = "--no-desktop", Required = true}); + args.Add("_no_startmenu_", new ExternalCommandArgument {ArgumentOption = "--no-startmenu", Required = true}); + args.Add("_root_", new ExternalCommandArgument + { + ArgumentOption = "--root ", + ArgumentValue = INSTALL_ROOT_TOKEN, + QuoteValue = false, + Required = true + }); + args.Add("_local_pkgs_dir_", new ExternalCommandArgument + { + ArgumentOption = "--local-package-dir ", + ArgumentValue = "{0}\\packages".format_with(INSTALL_ROOT_TOKEN), + QuoteValue = false, + Required = true + }); + + args.Add("_site_", new ExternalCommandArgument + { + ArgumentOption = "--site ", + ArgumentValue = "http://mirrors.kernel.org/sourceware/cygwin/", + QuoteValue = false, + Required = true + }); + + args.Add("_package_name_", new ExternalCommandArgument + { + ArgumentOption = "--packages ", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, + Required = true + }); + } + + public SourceType SourceType + { + get { return SourceType.cygwin; } + } + + public void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction) + { + var runnerConfig = new ChocolateyConfiguration + { + Sources = ApplicationParameters.PackagesLocation, + Debug = config.Debug, + Force = config.Force, + Verbose = config.Verbose, + CommandExecutionTimeoutSeconds = config.CommandExecutionTimeoutSeconds, + CacheLocation = config.CacheLocation, + RegularOutput = config.RegularOutput, + PromptForConfirmation = false, + AcceptLicense = true, + }; + runnerConfig.ListCommand.LocalOnly = true; + + var localPackages = _nugetService.list_run(runnerConfig, logResults: false); + + if (!localPackages.ContainsKey(CYGWIN_PACKAGE)) + { + runnerConfig.PackageNames = CYGWIN_PACKAGE; + runnerConfig.Sources = ApplicationParameters.ChocolateyCommunityFeedSource; + + var prompt = config.PromptForConfirmation; + config.PromptForConfirmation = false; + _nugetService.install_run(runnerConfig, ensureAction.Invoke); + config.PromptForConfirmation = prompt; + } + + set_root_dir_if_not_set(); + } + + public void set_root_dir_if_not_set() + { + if (!string.IsNullOrWhiteSpace(_rootDirectory)) return; + + var setupKey = _registryService.get_key(RegistryHive.LocalMachine, "SOFTWARE\\Cygwin\\setup"); + if (setupKey != null) + { + _rootDirectory = setupKey.GetValue("rootdir", string.Empty).to_string(); + } + + if (string.IsNullOrWhiteSpace(_rootDirectory)) + { + var binRoot = Environment.GetEnvironmentVariable("ChocolateyBinRoot"); + if (string.IsNullOrWhiteSpace(binRoot)) binRoot = "c:\\tools"; + + _rootDirectory = _fileSystem.combine_paths(binRoot,"cygwin"); + } + } + + public string get_exe(string rootpath) + { + return _fileSystem.combine_paths(rootpath, "cygwinsetup.exe"); + } + + public void list_noop(ChocolateyConfiguration config) + { + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement list".format_with(APP_NAME)); + } + + public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults) + { + throw new NotImplementedException("{0} does not implement list".format_with(APP_NAME)); + } + + public string build_args(ChocolateyConfiguration config, IDictionary argsDictionary) + { + var args = ExternalCommandArgsBuilder.build_arguments(config, argsDictionary); + + args = args.Replace(INSTALL_ROOT_TOKEN, _rootDirectory); + + return args; + } + + public void install_noop(ChocolateyConfiguration config, Action continueAction) + { + var args = build_args(config, _installArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(get_exe(_rootDirectory), args)); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) + { + var args = build_args(config, _installArguments); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + + foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + { + var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); + + var exitCode = _commandExecutor.execute( + get_exe(_rootDirectory), + argsForPackage, + config.CommandExecutionTimeoutSeconds, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); + + if (InstalledRegex.IsMatch(logMessage)) + { + var packageName = get_value_from_output(logMessage, PackageNameRegex, PACKAGE_NAME_GROUP); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + }, + updateProcessPath: false + ); + + if (exitCode != 0) + { + Environment.ExitCode = exitCode; + } + } + + return packageResults; + } + + public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) + { + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement upgrade".format_with(APP_NAME)); + return new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + } + + public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) + { + throw new NotImplementedException("{0} does not implement upgrade".format_with(APP_NAME)); + } + + public void uninstall_noop(ChocolateyConfiguration config, Action continueAction) + { + this.Log().Warn(ChocolateyLoggers.Important, "{0} does not implement uninstall".format_with(APP_NAME)); + } + + public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction) + { + throw new NotImplementedException("{0} does not implement upgrade".format_with(APP_NAME)); + } + + /// + /// Grabs a value from the output based on the regex. + /// + /// The output. + /// The regex. + /// Name of the group. + /// + private static string get_value_from_output(string output, Regex regex, string groupName) + { + var matchGroup = regex.Match(output).Groups[groupName]; + if (matchGroup != null) + { + return matchGroup.Value; + } + + return string.Empty; + } + } +} \ No newline at end of file From 66e98ea4679e3079694b3e0fd8329c01cc786e02 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 12:23:20 -0500 Subject: [PATCH 34/61] (maint) add registry service evaluation --- .../services/RegistryService.cs | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/RegistryService.cs b/src/chocolatey/infrastructure.app/services/RegistryService.cs index b49c1db7ec..5dee9bb8c8 100644 --- a/src/chocolatey/infrastructure.app/services/RegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/RegistryService.cs @@ -34,6 +34,7 @@ public sealed class RegistryService : IRegistryService { private readonly IXmlService _xmlService; private readonly IFileSystem _fileSystem; + private readonly bool _logOutput = false; public RegistryService(IXmlService xmlService, IFileSystem fileSystem) { @@ -69,13 +70,16 @@ public Registry get_installer_keys() registryKey.Dispose(); } - //Console.WriteLine(""); - //Console.WriteLine("A total of {0} unrecognized apps".format_with(snapshot.RegistryKeys.Where((p) => p.InstallerType == InstallerType.Unknown).Count())); - //Console.WriteLine(""); + if (_logOutput) + { + Console.WriteLine(""); + Console.WriteLine("A total of {0} unrecognized apps".format_with(snapshot.RegistryKeys.Where((p) => p.InstallerType == InstallerType.Unknown && p.is_in_programs_and_features()).Count())); + Console.WriteLine(""); - //Console.WriteLine(""); - //Console.WriteLine("A total of {0} of {1} are programs and features apps".format_with(snapshot.RegistryKeys.Where((p) => p.is_in_programs_and_features()).Count(), snapshot.RegistryKeys.Count)); - //Console.WriteLine(""); + Console.WriteLine(""); + Console.WriteLine("A total of {0} of {1} are programs and features apps".format_with(snapshot.RegistryKeys.Where((p) => p.is_in_programs_and_features()).Count(), snapshot.RegistryKeys.Count)); + Console.WriteLine(""); + } return snapshot; } @@ -168,21 +172,23 @@ public void evaluate_keys(RegistryKey key, Registry snapshot) appKey.InstallerType = InstallerType.Custom; } - //if (appKey.InstallerType == InstallerType.Msi) - //{ - //Console.WriteLine(""); - //if (!string.IsNullOrWhiteSpace(appKey.UninstallString)) - //{ - // Console.WriteLine(appKey.UninstallString.to_string().Split(new[] { " /", " -" }, StringSplitOptions.RemoveEmptyEntries)[0]); - // key.UninstallString.to_string().Split(new[] { " /", " -" }, StringSplitOptions.RemoveEmptyEntries); - //} - //foreach (var name in key.GetValueNames()) - //{ - // var kind = key.GetValueKind(name); - // var value = key.GetValue(name); - // Console.WriteLine("key - {0}, name - {1}, kind - {2}, value - {3}".format_with(key.Name, name, kind, value.to_string())); - //} - //} + if (_logOutput) + { + if (appKey.is_in_programs_and_features() && appKey.InstallerType == InstallerType.Unknown) + { + foreach (var name in key.GetValueNames()) + { + //var kind = key.GetValueKind(name); + var value = key.GetValue(name); + if (name.is_equal_to("QuietUninstallString") || name.is_equal_to("UninstallString")) + { + Console.WriteLine("key - {0}|{1}={2}|Type detected={3}".format_with(key.Name, name, value.to_string(), appKey.InstallerType.to_string())); + } + + //Console.WriteLine("key - {0}, name - {1}, kind - {2}, value - {3}".format_with(key.Name, name, kind, value.to_string())); + } + } + } snapshot.RegistryKeys.Add(appKey); } From 60c99cb9ac6532455f49112fd6780e8bfcced3de Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 16:32:27 -0500 Subject: [PATCH 35/61] (GH-14) use the packagenames separator --- src/chocolatey/infrastructure.app/services/CygwinService.cs | 2 +- src/chocolatey/infrastructure.app/services/RubyGemsService.cs | 2 +- src/chocolatey/infrastructure.app/services/WebPiService.cs | 2 +- .../infrastructure.app/services/WindowsFeatureService.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/CygwinService.cs b/src/chocolatey/infrastructure.app/services/CygwinService.cs index bb393519c0..2b20a8bc6a 100644 --- a/src/chocolatey/infrastructure.app/services/CygwinService.cs +++ b/src/chocolatey/infrastructure.app/services/CygwinService.cs @@ -208,7 +208,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu var args = build_args(config, _installArguments); var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) { var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); diff --git a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs index c2bf31b382..7f07c1008d 100644 --- a/src/chocolatey/infrastructure.app/services/RubyGemsService.cs +++ b/src/chocolatey/infrastructure.app/services/RubyGemsService.cs @@ -175,7 +175,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); - foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) { var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); var exitCode = _commandExecutor.execute( diff --git a/src/chocolatey/infrastructure.app/services/WebPiService.cs b/src/chocolatey/infrastructure.app/services/WebPiService.cs index a04e2f027d..61b56f341f 100644 --- a/src/chocolatey/infrastructure.app/services/WebPiService.cs +++ b/src/chocolatey/infrastructure.app/services/WebPiService.cs @@ -188,7 +188,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); var args = ExternalCommandArgsBuilder.build_arguments(config, _installArguments); - foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) { var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); var exitCode = _commandExecutor.execute( diff --git a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs index 46b18cafef..6e5b79f679 100644 --- a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs +++ b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs @@ -251,7 +251,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu var args = build_args(config, _installArguments); var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) { var packageName = packageToInstall; var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageName, null, null)); @@ -333,7 +333,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi var args = build_args(config, _uninstallArguments); var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (var packageToInstall in config.PackageNames.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) { var packageName = packageToInstall; var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageName, null, null)); From 45ed0657ade7f2d9c3ec6eb81257c95942409fb7 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 17:35:02 -0500 Subject: [PATCH 36/61] (GH-14) Python Alternate Source - Able to find pip, starting by finding python, then going to the registry to find python install location. - Able to install python if not found - Able to list packages - Able to install pacakges - Able to upgrade packages - Able to uninstall packages --- src/chocolatey/chocolatey.csproj | 1 + .../registration/ContainerBinding.cs | 1 + .../services/PythonService.cs | 531 ++++++++++++++++++ 3 files changed, 533 insertions(+) create mode 100644 src/chocolatey/infrastructure.app/services/PythonService.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 52069d46ad..1b113a04fb 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -111,6 +111,7 @@ + diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index a5cdb31b06..e1acad8400 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -96,6 +96,7 @@ public void RegisterComponents(Container container) new WebPiService(container.GetInstance(), container.GetInstance()), new WindowsFeatureService(container.GetInstance(), container.GetInstance(), container.GetInstance()), new CygwinService(container.GetInstance(), container.GetInstance(), container.GetInstance(), container.GetInstance()), + new PythonService(container.GetInstance(), container.GetInstance(), container.GetInstance(), container.GetInstance()), new RubyGemsService(container.GetInstance(), container.GetInstance()) }; return list.AsReadOnly(); diff --git a/src/chocolatey/infrastructure.app/services/PythonService.cs b/src/chocolatey/infrastructure.app/services/PythonService.cs new file mode 100644 index 0000000000..963ec5c39a --- /dev/null +++ b/src/chocolatey/infrastructure.app/services/PythonService.cs @@ -0,0 +1,531 @@ +namespace chocolatey.infrastructure.app.services +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.IO; + using System.Text.RegularExpressions; + using Microsoft.Win32; + using configuration; + using domain; + using filesystem; + using infrastructure.commands; + using logging; + using results; + + /// + /// Alternative Source for Installing Python packages + /// + public sealed class PythonService : ISourceRunner + { + private readonly ICommandExecutor _commandExecutor; + private readonly INugetService _nugetService; + private readonly IFileSystem _fileSystem; + private readonly IRegistryService _registryService; + private const string PACKAGE_NAME_TOKEN = "{{packagename}}"; + private const string LOG_LEVEL_TOKEN = "{{loglevel}}"; + private const string FORCE_TOKEN = "{{force}}"; + public const string PYTHON_PACKAGE = "python"; + private string _exePath = string.Empty; + + private const string APP_NAME = "Python"; + public const string PACKAGE_NAME_GROUP = "PkgName"; + public static readonly Regex InstalledRegex = new Regex(@"Successfully installed", RegexOptions.Compiled); + public static readonly Regex UninstalledRegex = new Regex(@"Successfully uninstalled", RegexOptions.Compiled); + public static readonly Regex PackageNameRegex = new Regex(@"\s(?<{0}>[^-\s]*)-".format_with(PACKAGE_NAME_GROUP), RegexOptions.Compiled); + public static readonly Regex ErrorRegex = new Regex(@"Error:", RegexOptions.Compiled); + public static readonly Regex ErrorNotFoundRegex = new Regex(@"Could not find any downloads that", RegexOptions.Compiled); + + private readonly IDictionary _listArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _installArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _upgradeArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _uninstallArguments = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public PythonService(ICommandExecutor commandExecutor, INugetService nugetService, IFileSystem fileSystem, IRegistryService registryService) + { + _commandExecutor = commandExecutor; + _nugetService = nugetService; + _fileSystem = fileSystem; + _registryService = registryService; + set_cmd_args_dictionaries(); + } + + /// + /// Set any command arguments dictionaries necessary for the service + /// + private void set_cmd_args_dictionaries() + { + set_list_dictionary(_listArguments); + set_install_dictionary(_installArguments); + set_upgrade_dictionary(_upgradeArguments); + set_uninstall_dictionary(_uninstallArguments); + } + + /// + /// Sets list dictionary + /// + private void set_list_dictionary(IDictionary args) + { + set_common_args(args); + args.Add("_command_", new ExternalCommandArgument { ArgumentOption = "list", Required = true }); + } + + /// + /// Sets install dictionary + /// + private void set_install_dictionary(IDictionary args) + { + set_common_args(args); + + args.Add("_command_", new ExternalCommandArgument { ArgumentOption = "install", Required = true }); + args.Add("_package_name_", new ExternalCommandArgument + { + ArgumentOption = "", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, + UseValueOnly = true, + Required = true + }); + } + + /// + /// Sets install dictionary + /// + private void set_upgrade_dictionary(IDictionary args) + { + set_common_args(args); + + args.Add("_command_", new ExternalCommandArgument { ArgumentOption = "install", Required = true }); + args.Add("_upgrade_", new ExternalCommandArgument { ArgumentOption = "--upgrade", Required = true }); + args.Add("_package_name_", new ExternalCommandArgument + { + ArgumentOption = "", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, + UseValueOnly = true, + Required = true + }); + } + + /// + /// Sets uninstall dictionary + /// + private void set_uninstall_dictionary(IDictionary args) + { + set_common_args(args); + + args.Add("_command_", new ExternalCommandArgument { ArgumentOption = "uninstall", Required = true }); + args.Add("_confirm_", new ExternalCommandArgument { ArgumentOption = "-y", Required = true }); + args.Add("_package_name_", new ExternalCommandArgument + { + ArgumentOption = "", + ArgumentValue = PACKAGE_NAME_TOKEN, + QuoteValue = false, + UseValueOnly = true, + Required = true + }); + } + + private void set_common_args(IDictionary args) + { + args.Add("_loglevel_", new ExternalCommandArgument + { + ArgumentOption = "", + ArgumentValue = LOG_LEVEL_TOKEN, + QuoteValue = false, + UseValueOnly = true, + Required = true + }); + + args.Add("_force_", new ExternalCommandArgument + { + ArgumentOption = "", + ArgumentValue = FORCE_TOKEN, + QuoteValue = false, + UseValueOnly = true, + Required = true + }); + + + } + + public SourceType SourceType + { + get { return SourceType.python; } + } + + public void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction) + { + //ensure at least python 2.7.9 is installed + var python = _fileSystem.get_executable_path("python"); + //python -V + + if (python.is_equal_to("python")) + { + var runnerConfig = new ChocolateyConfiguration + { + Sources = ApplicationParameters.PackagesLocation, + Debug = config.Debug, + Force = config.Force, + Verbose = config.Verbose, + CommandExecutionTimeoutSeconds = config.CommandExecutionTimeoutSeconds, + CacheLocation = config.CacheLocation, + RegularOutput = config.RegularOutput, + PromptForConfirmation = false, + AcceptLicense = true, + }; + runnerConfig.ListCommand.LocalOnly = true; + + var localPackages = _nugetService.list_run(runnerConfig, logResults: false); + + if (!localPackages.ContainsKey(PYTHON_PACKAGE)) + { + runnerConfig.PackageNames = PYTHON_PACKAGE; + runnerConfig.Sources = ApplicationParameters.ChocolateyCommunityFeedSource; + + var prompt = config.PromptForConfirmation; + config.PromptForConfirmation = false; + _nugetService.install_run(runnerConfig, ensureAction.Invoke); + config.PromptForConfirmation = prompt; + } + } + } + + public void set_executable_path_if_not_set() + { + if (!string.IsNullOrWhiteSpace(_exePath)) return; + + var python = _fileSystem.get_executable_path("python"); + + var pipPath = string.Empty; + if (!python.is_equal_to("python")) + { + pipPath = _fileSystem.combine_paths(_fileSystem.get_directory_name(python), "Scripts", "pip.exe"); + if (_fileSystem.file_exists(pipPath)) + { + _exePath = pipPath; + return; + } + } + + var topLevelPath = string.Empty; + var python34PathKey = _registryService.get_key(RegistryHive.LocalMachine, "SOFTWARE\\Python\\PythonCore\\3.4\\InstallPath"); + if (python34PathKey != null) + { + topLevelPath = python34PathKey.GetValue("", string.Empty).to_string(); + } + if (string.IsNullOrWhiteSpace(topLevelPath)) + { + var python27PathKey = _registryService.get_key(RegistryHive.LocalMachine, "SOFTWARE\\Python\\PythonCore\\2.7\\InstallPath"); + if (python27PathKey != null) + { + topLevelPath = python27PathKey.GetValue("", string.Empty).to_string(); + } + } + + if (string.IsNullOrWhiteSpace(topLevelPath)) + { + var binRoot = Environment.GetEnvironmentVariable("ChocolateyBinRoot"); + if (string.IsNullOrWhiteSpace(binRoot)) binRoot = "c:\\tools"; + + topLevelPath = _fileSystem.combine_paths(binRoot, "python"); + } + + pipPath = _fileSystem.combine_paths(_fileSystem.get_directory_name(topLevelPath), "Scripts", "pip.exe"); + if (_fileSystem.file_exists(pipPath)) + { + _exePath = pipPath; + } + + if (string.IsNullOrWhiteSpace(_exePath)) throw new FileNotFoundException("Unable to find pip"); + } + + public string build_args(ChocolateyConfiguration config, IDictionary argsDictionary) + { + var args = ExternalCommandArgsBuilder.build_arguments(config, argsDictionary); + + args = args.Replace(LOG_LEVEL_TOKEN, config.Debug ? "-vvv" : ""); + + if (config.CommandName.is_equal_to("intall")) + { + args = args.Replace(FORCE_TOKEN, config.Force ? "--ignore-installed" : ""); + } + else if (config.CommandName.is_equal_to("upgrade")) + { + args = args.Replace(FORCE_TOKEN, config.Force ? "--force-reinstall" : ""); + } + else + { + args = args.Replace(FORCE_TOKEN, ""); + } + + return args; + } + + public void list_noop(ChocolateyConfiguration config) + { + set_executable_path_if_not_set(); + var args = build_args(config, _listArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + } + + public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults) + { + set_executable_path_if_not_set(); + var args = build_args(config, _listArguments); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + + Environment.ExitCode = _commandExecutor.execute( + _exePath, + args, + config.CommandExecutionTimeoutSeconds, + stdOutAction: (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + if (logResults) + { + this.Log().Info(e.Data); + } + else + { + this.Log().Debug(() => "[{0}] {1}".format_with(APP_NAME, logMessage)); + } + }, + stdErrAction: (s, e) => + { + if (string.IsNullOrWhiteSpace(e.Data)) return; + this.Log().Error(() => "{0}".format_with(e.Data)); + }, + updateProcessPath: false + ); + + return packageResults; + } + + public void install_noop(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _installArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + } + + public ConcurrentDictionary install_run(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _installArguments); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) + { + var pkgName = packageToInstall; + if (!string.IsNullOrWhiteSpace(config.Version)) + { + pkgName = "{0}=={1}".format_with(packageToInstall, config.Version); + } + var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, pkgName); + + var exitCode = _commandExecutor.execute( + _exePath, + argsForPackage, + config.CommandExecutionTimeoutSeconds, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageToInstall, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + } + + if (InstalledRegex.IsMatch(logMessage)) + { + var packageName = get_value_from_output(logMessage, PackageNameRegex, PACKAGE_NAME_GROUP); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageToInstall, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + } + }, + updateProcessPath: false + ); + + if (exitCode != 0) + { + Environment.ExitCode = exitCode; + } + } + + return packageResults; + } + + public ConcurrentDictionary upgrade_noop(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _upgradeArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + return new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + } + + public ConcurrentDictionary upgrade_run(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _upgradeArguments); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) + { + var pkgName = packageToInstall; + if (!string.IsNullOrWhiteSpace(config.Version)) + { + pkgName = "{0}=={1}".format_with(packageToInstall, config.Version); + } + + var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, pkgName); + + var exitCode = _commandExecutor.execute( + _exePath, + argsForPackage, + config.CommandExecutionTimeoutSeconds, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageToInstall, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + } + + if (InstalledRegex.IsMatch(logMessage)) + { + var packageName = get_value_from_output(logMessage, PackageNameRegex, PACKAGE_NAME_GROUP); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been installed successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageToInstall, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + } + }, + updateProcessPath: false + ); + + if (exitCode != 0) + { + Environment.ExitCode = exitCode; + } + } + + return packageResults; + } + + public void uninstall_noop(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _uninstallArguments); + this.Log().Info("Would have run '{0} {1}'".format_with(_exePath, args)); + } + + public ConcurrentDictionary uninstall_run(ChocolateyConfiguration config, Action continueAction) + { + set_executable_path_if_not_set(); + var args = build_args(config, _uninstallArguments); + var packageResults = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + + foreach (var packageToInstall in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries)) + { + var argsForPackage = args.Replace(PACKAGE_NAME_TOKEN, packageToInstall); + + var exitCode = _commandExecutor.execute( + _exePath, + argsForPackage, + config.CommandExecutionTimeoutSeconds, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Info(() => " [{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageToInstall, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Error, packageToInstall)); + } + + if (UninstalledRegex.IsMatch(logMessage)) + { + var packageName = get_value_from_output(logMessage, PackageNameRegex, PACKAGE_NAME_GROUP); + var results = packageResults.GetOrAdd(packageName, new PackageResult(packageName, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Note, packageName)); + this.Log().Info(ChocolateyLoggers.Important, " {0} has been uninstalled successfully.".format_with(string.IsNullOrWhiteSpace(packageName) ? packageToInstall : packageName)); + } + }, + (s, e) => + { + var logMessage = e.Data; + if (string.IsNullOrWhiteSpace(logMessage)) return; + this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); + + if (ErrorRegex.IsMatch(logMessage) || ErrorNotFoundRegex.IsMatch(logMessage)) + { + var results = packageResults.GetOrAdd(packageToInstall, new PackageResult(packageToInstall, null, null)); + results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); + } + }, + updateProcessPath: false + ); + + if (exitCode != 0) + { + Environment.ExitCode = exitCode; + } + } + + return packageResults; + } + + /// + /// Grabs a value from the output based on the regex. + /// + /// The output. + /// The regex. + /// Name of the group. + /// + private static string get_value_from_output(string output, Regex regex, string groupName) + { + var matchGroup = regex.Match(output).Groups[groupName]; + if (matchGroup != null) + { + return matchGroup.Value; + } + + return string.Empty; + } + } +} \ No newline at end of file From d9087bab8ab5122f1aa5c16275cbf55a0feda2b5 Mon Sep 17 00:00:00 2001 From: Edward Wilde Date: Tue, 16 Jun 2015 09:12:48 +0100 Subject: [PATCH 37/61] (GH-332) Custom headers support for downloads Adds the ability for packages that use Get-ChocolateyWebFile.ps1 or Get-WebFile to specify customer headers during the request. See #332 or powershell documentation for example usage --- .../functions/Get-ChocolateyWebFile.ps1 | 23 +++++++++++++++++-- .../helpers/functions/Get-WebFile.ps1 | 18 ++++++++++++++- .../functions/Install-ChocolateyPackage.ps1 | 22 ++++++++++++++++-- .../Install-ChocolateyZipPackage.ps1 | 23 +++++++++++++++++-- 4 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/chocolatey.resources/helpers/functions/Get-ChocolateyWebFile.ps1 b/src/chocolatey.resources/helpers/functions/Get-ChocolateyWebFile.ps1 index ad83d67d51..aedca5c4f7 100644 --- a/src/chocolatey.resources/helpers/functions/Get-ChocolateyWebFile.ps1 +++ b/src/chocolatey.resources/helpers/functions/Get-ChocolateyWebFile.ps1 @@ -45,6 +45,24 @@ OPTIONAL (Right now) - 'md5' or 'sha1' - defaults to 'md5' .PARAMETER ChecksumType64 OPTIONAL (Right now) - 'md5' or 'sha1' - defaults to ChecksumType + +.PARAMETER options +OPTIONAL - Specify custom headers + +Example: +-------- + $options = + @{ + Headers = @{ + Accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; + 'Accept-Charset' = 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'; + 'Accept-Language' = 'en-GB,en-US;q=0.8,en;q=0.6'; + Cookie = 'products.download.email=ewilde@gmail.com'; + Referer = 'http://submain.com/download/ghostdoc/'; + } + } + + Get-ChocolateyWebFile 'ghostdoc' 'http://submain.com/download/GhostDoc_v4.0.zip' -options $options .EXAMPLE Get-ChocolateyWebFile '__NAME__' 'C:\somepath\somename.exe' 'URL' '64BIT_URL_DELETE_IF_NO_64BIT' @@ -64,7 +82,8 @@ param( [string] $checksum = '', [string] $checksumType = '', [string] $checksum64 = '', - [string] $checksumType64 = $checksumType + [string] $checksumType64 = $checksumType, + [hashtable] $options = @{Headers=@{}} ) Write-Debug "Running 'Get-ChocolateyWebFile' for $packageName with url:`'$url`', fileFullPath:`'$fileFullPath`', url64bit:`'$url64bit`', checksum: `'$checksum`', checksumType: `'$checksumType`', checksum64: `'$checksum64`', checksumType64: `'$checksumType64`'"; @@ -138,7 +157,7 @@ param( if ($needsDownload) { Write-Host "Downloading $packageName $bitPackage bit from `'$url`'" - Get-WebFile $url $fileFullPath + Get-WebFile $url $fileFullPath -options $options } } elseif ($url.StartsWith('ftp')) { Write-Host "Ftp-ing $packageName diff --git a/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 b/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 index 324a766ad4..7275a083da 100644 --- a/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 +++ b/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 @@ -20,7 +20,8 @@ param( $fileName = $null, $userAgent = 'chocolatey command line', [switch]$Passthru, - [switch]$quiet + [switch]$quiet, + [hashtable] $options = @{Headers=@{}} ) Write-Debug "Running 'Get-WebFile' for $fileName with url:`'$url`', userAgent: `'$userAgent`' "; #if ($url -eq '' return) @@ -63,6 +64,21 @@ param( $req.UserAgent = $userAgent } + if ($options.Headers.Count -gt 0) { + Write-Debug "Setting custom headers" + foreach ($item in $options.Headers.GetEnumerator()) { + $uri = (new-object system.uri $url) + Write-Debug($item.Key + ':' + $item.Value) + switch ($item.Key) { + 'Accept' {$req.Accept = $item.Value} + 'Cookie' {$req.CookieContainer.SetCookies($uri, $item.Value)} + 'Referer' {$req.Referer = $item.Value} + 'User-Agent' {$req.UserAgent = $item.Value} + Default {$req.Headers.Add($item.Key, $item.Value)} + } + } + } + $res = $req.GetResponse(); if($fileName -and !(Split-Path $fileName)) { diff --git a/src/chocolatey.resources/helpers/functions/Install-ChocolateyPackage.ps1 b/src/chocolatey.resources/helpers/functions/Install-ChocolateyPackage.ps1 index c17fd1ab9a..be2641e35c 100644 --- a/src/chocolatey.resources/helpers/functions/Install-ChocolateyPackage.ps1 +++ b/src/chocolatey.resources/helpers/functions/Install-ChocolateyPackage.ps1 @@ -53,6 +53,23 @@ OPTIONAL (Right now) - 'md5' or 'sha1' - defaults to 'md5' .PARAMETER ChecksumType64 OPTIONAL (Right now) - 'md5' or 'sha1' - defaults to ChecksumType +.PARAMETER options +OPTIONAL - Specify custom headers + +Example: +-------- + $options = + @{ + Headers = @{ + Accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; + 'Accept-Charset' = 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'; + 'Accept-Language' = 'en-GB,en-US;q=0.8,en;q=0.6'; + Cookie = 'products.download.email=ewilde@gmail.com'; + Referer = 'http://submain.com/download/ghostdoc/'; + } + } + + Get-ChocolateyWebFile 'ghostdoc' 'http://submain.com/download/GhostDoc_v4.0.zip' -options $options .EXAMPLE Install-ChocolateyPackage '__NAME__' 'EXE_OR_MSI' 'SILENT_ARGS' 'URL' '64BIT_URL_DELETE_IF_NO_64BIT' @@ -78,7 +95,8 @@ param( [string] $checksum = '', [string] $checksumType = '', [string] $checksum64 = '', - [string] $checksumType64 = '' + [string] $checksumType64 = '', + [hashtable] $options = @{Headers=@{}} ) Write-Debug "Running 'Install-ChocolateyPackage' for $packageName with url:`'$url`', args: `'$silentArgs`', fileType: `'$fileType`', url64bit: `'$url64bit`', checksum: `'$checksum`', checksumType: `'$checksumType`', checksum64: `'$checksum64`', checksumType64: `'$checksumType64`', validExitCodes: `'$validExitCodes`' "; @@ -89,6 +107,6 @@ param( if (![System.IO.Directory]::Exists($tempDir)) { [System.IO.Directory]::CreateDirectory($tempDir) | Out-Null } $file = Join-Path $tempDir "$($packageName)Install.$fileType" - Get-ChocolateyWebFile $packageName $file $url $url64bit -checksum $checksum -checksumType $checksumType -checksum64 $checksum64 -checksumType64 $checksumType64 + Get-ChocolateyWebFile $packageName $file $url $url64bit -checksum $checksum -checksumType $checksumType -checksum64 $checksum64 -checksumType64 $checksumType64 -options $options Install-ChocolateyInstallPackage $packageName $fileType $silentArgs $file -validExitCodes $validExitCodes } diff --git a/src/chocolatey.resources/helpers/functions/Install-ChocolateyZipPackage.ps1 b/src/chocolatey.resources/helpers/functions/Install-ChocolateyZipPackage.ps1 index 6b7e2caf58..7c8a36d8d6 100644 --- a/src/chocolatey.resources/helpers/functions/Install-ChocolateyZipPackage.ps1 +++ b/src/chocolatey.resources/helpers/functions/Install-ChocolateyZipPackage.ps1 @@ -45,6 +45,24 @@ OPTIONAL (Right now) - 'md5' or 'sha1' - defaults to 'md5' .PARAMETER ChecksumType64 OPTIONAL (Right now) - 'md5' or 'sha1' - defaults to ChecksumType +.PARAMETER options +OPTIONAL - Specify custom headers + +Example: +-------- + $options = + @{ + Headers = @{ + Accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; + 'Accept-Charset' = 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'; + 'Accept-Language' = 'en-GB,en-US;q=0.8,en;q=0.6'; + Cookie = 'products.download.email=ewilde@gmail.com'; + Referer = 'http://submain.com/download/ghostdoc/'; + } + } + + Get-ChocolateyWebFile 'ghostdoc' 'http://submain.com/download/GhostDoc_v4.0.zip' -options $options + .EXAMPLE Install-ChocolateyZipPackage '__NAME__' 'URL' "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" @@ -68,7 +86,8 @@ param( [string] $checksum = '', [string] $checksumType = '', [string] $checksum64 = '', - [string] $checksumType64 = '' + [string] $checksumType64 = '', + [hashtable] $options = @{Headers=@{}} ) Write-Debug "Running 'Install-ChocolateyZipPackage' for $packageName with url:`'$url`', unzipLocation: `'$unzipLocation`', url64bit: `'$url64bit`', specificFolder: `'$specificFolder`', checksum: `'$checksum`', checksumType: `'$checksumType`', checksum64: `'$checksum64`', checksumType64: `'$checksumType64`' "; @@ -79,6 +98,6 @@ param( if (![System.IO.Directory]::Exists($tempDir)) {[System.IO.Directory]::CreateDirectory($tempDir) | Out-Null} $file = Join-Path $tempDir "$($packageName)Install.$fileType" - Get-ChocolateyWebFile $packageName $file $url $url64bit -checkSum $checkSum -checksumType $checksumType -checkSum64 $checkSum64 -checksumType64 $checksumType64 + Get-ChocolateyWebFile $packageName $file $url $url64bit -checkSum $checkSum -checksumType $checksumType -checkSum64 $checkSum64 -checksumType64 $checksumType64 -options $options Get-ChocolateyUnzip "$file" $unzipLocation $specificFolder $packageName } From 77a023c84bb0da4667333d1bff295ae78cead473 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Sat, 20 Jun 2015 08:01:24 -0500 Subject: [PATCH 38/61] (doc) update changelog/nuspec --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++ nuget/chocolatey/chocolatey.nuspec | 39 ++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a86179b86f..ff79307c0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,42 @@ +## [0.9.9.7](https://github.com/chocolatey/choco/issues?q=milestone%3A0.9.9.7+is%3Aclosed) (unreleased) + +"Fix Everything. Fix All The Things" - There have been some things bugging us for a long time related to limitations with NuGet, so we decided to fix that. Like [nuspec enhancements](https://github.com/chocolatey/choco/issues/205), that crazy [content folder restriction](https://github.com/chocolatey/choco/issues/290) has been removed (I know, right?!), and we're working around [badly](https://github.com/chocolatey/choco/issues/316) [behaved](https://github.com/chocolatey/choco/issues/326) packages quite a bit more to bring you more feature parity. + +Let's talk about a couple of big, like really big, BIG features just added with this release. No more packages rebooting Windows. We fixed ([#304](https://github.com/chocolatey/choco/issues/304) / [#323](https://github.com/chocolatey/choco/issues/323)) and [enhanced](https://github.com/chocolatey/choco/issues/305) up the Auto Uninstaller Service quite a bit to ensure things are working like you would expect (It goes on by default in 0.9.10 - we'll start documenting more about it soon). But wait, there's more! I haven't even told you about the big features yet + +The first big feature is enhancing the nuspec. I mentioned this I know, but *now* you can use `packageSourceUrl` in the nuspec to tell folks where you are storing the source for the package! We also added `projectSourceUrl`, `docsUrl`, `mailingListUrl`, and `bugTrackerUrl`. What's even better is that the community feed has already been enhanced to look for these values. So have the templates from `choco new`. And it's backwards compatible, meaning you can still install packages that have these added nuspec enhancements without issue (but we will need to provide a fix for Nuget Package Explorer). + +The second is Xml Document Transformations (XDT), which I think many folks are aware of but may not realize what it can provide. [NuGet has allowed transformations for quite awhile](https://docs.nuget.org/Create/Configuration-File-and-Source-Code-Transformations) to allow you to make changes to an `app.config`/`web.config` on install/uninstall. We are following in similar footsteps to allow you to do similar when installing/upgrading packages. We will look for `*.install.xdt` files in the package (doesn't matter where) and they will apply to configuration files with the same name in the package. This means that during upgrades we won't overwrite configuration files during upgrades that have opted into this feature. It allows you to give users a better experience during upgrades because they won't need to keep making the same changes to the xml config files each time they upgrade your package. + +### FEATURES + + * Allow XDT Configuration Transforms - see [#331](https://github.com/chocolatey/choco/issues/331) + * Prevent reboots - see [#316](https://github.com/chocolatey/choco/issues/316) + * Enhance the nuspec - first wave - see [#205](https://github.com/chocolatey/choco/issues/205) + * Uninstaller Service Enhancements - see [#305](https://github.com/chocolatey/choco/issues/305) + +### BUG FIXES + + * When uninstall fails, do not continue removing files - see [#315](https://github.com/chocolatey/choco/issues/315) + * Do not run autouninstaller if the package result is already a failure - see [#323](https://github.com/chocolatey/choco/issues/323) + * Fix - Auto Uninstaller can fail if chocolateyUninstall.ps1 uninstalls prior to it running - see [#304](https://github.com/chocolatey/choco/issues/304) + * Fix - Packages with content folders cannot have a dependency without also having a content folder - see [#290](https://github.com/chocolatey/choco/issues/290) + * Remove ShimGen director files on upgrade/uninstall - see [#326](https://github.com/chocolatey/choco/issues/326) + * If feature doesn't exist, throw an error - see [#317](https://github.com/chocolatey/choco/issues/317) + * Fix - The operation completed successfully on stderr - see [#249](https://github.com/chocolatey/choco/issues/249) + * Fix - When specific nuget version is needed by a package it is the chocolatey version that is used - see [#194](https://github.com/chocolatey/choco/issues/194) + * When installing with *.nupkg, need to get package name from package, not file name - see [#90](https://github.com/chocolatey/choco/issues/90) + * Fix - Choco pin list is not returning a list - see [#302](https://github.com/chocolatey/choco/issues/302) + * Fix - A pin is not created for existing installations (prior to new choco) - see [#60](https://github.com/chocolatey/choco/issues/60) + +### IMPROVEMENTS + + * Allow upgrade to always install missing packages - see [#300](https://github.com/chocolatey/choco/issues/300) + * Enhance Templates - see [#296](https://github.com/chocolatey/choco/issues/296) + * Always log debug output to the log file - see [#319](https://github.com/chocolatey/choco/issues/319) + * Warn when unable to snapshot locked files - see [#313](https://github.com/chocolatey/choco/issues/313) + * Use %systemroot% in place of %windir%. PATH exceed 2048 breaks choco - see [#252](https://github.com/chocolatey/choco/issues/252) + ## [0.9.9.6](https://github.com/chocolatey/choco/issues?q=milestone%3A0.9.9.6+is%3Aclosed) (May 16, 2015) Some really large fixes this release, especially removing all files that are installed to the package directory if they haven't changed, including ensuring that the nupkg file is always removed on successful uninstalls. The really big add some folks are going to like is the new outdated command. Some more variables that were misused have been brought back, which allows some packages (like Atom) to be installed again without issue. If you can believe some people never read these, we decided to add a note to the installer prompt to let people know about -y. diff --git a/nuget/chocolatey/chocolatey.nuspec b/nuget/chocolatey/chocolatey.nuspec index daf354ef74..f9c21ca0ab 100644 --- a/nuget/chocolatey/chocolatey.nuspec +++ b/nuget/chocolatey/chocolatey.nuspec @@ -40,6 +40,45 @@ In that mess there is a link to the [Helper Reference](https://github.com/chocol See all - https://github.com/chocolatey/choco/blob/master/CHANGELOG.md +## 0.9.9.7 + +"Fix Everything. Fix All The Things" - There have been some things bugging us for a long time related to limitations with NuGet, so we decided to fix that. Like [nuspec enhancements](https://github.com/chocolatey/choco/issues/205), that crazy [content folder restriction](https://github.com/chocolatey/choco/issues/290) has been removed (I know, right?!), and we're working around [badly](https://github.com/chocolatey/choco/issues/316) [behaved](https://github.com/chocolatey/choco/issues/326) packages quite a bit more to bring you more feature parity. + +Let's talk about a couple of big, like really big, BIG features just added with this release. No more packages rebooting Windows. We fixed ([#304](https://github.com/chocolatey/choco/issues/304) / [#323](https://github.com/chocolatey/choco/issues/323)) and [enhanced](https://github.com/chocolatey/choco/issues/305) up the Auto Uninstaller Service quite a bit to ensure things are working like you would expect (It goes on by default in 0.9.10 - we'll start documenting more about it soon). But wait, there's more! I haven't even told you about the big features yet + +The first big feature is enhancing the nuspec. I mentioned this I know, but *now* you can use `packageSourceUrl` in the nuspec to tell folks where you are storing the source for the package! We also added `projectSourceUrl`, `docsUrl`, `mailingListUrl`, and `bugTrackerUrl`. What's even better is that the community feed has already been enhanced to look for these values. So have the templates from `choco new`. And it's backwards compatible, meaning you can still install packages that have these added nuspec enhancements without issue (but we will need to provide a fix for Nuget Package Explorer). + +The second is Xml Document Transformations (XDT), which I think many folks are aware of but may not realize what it can provide. [NuGet has allowed transformations for quite awhile](https://docs.nuget.org/Create/Configuration-File-and-Source-Code-Transformations) to allow you to make changes to an `app.config`/`web.config` on install/uninstall. We are following in similar footsteps to allow you to do similar when installing/upgrading packages. We will look for `*.install.xdt` files in the package (doesn't matter where) and they will apply to configuration files with the same name in the package. This means that during upgrades we won't overwrite configuration files during upgrades that have opted into this feature. It allows you to give users a better experience during upgrades because they won't need to keep making the same changes to the xml config files each time they upgrade your package. + +### FEATURES + + * Allow XDT Configuration Transforms - see [#331](https://github.com/chocolatey/choco/issues/331) + * Prevent reboots - see [#316](https://github.com/chocolatey/choco/issues/316) + * Enhance the nuspec - first wave - see [#205](https://github.com/chocolatey/choco/issues/205) + * Uninstaller Service Enhancements - see [#305](https://github.com/chocolatey/choco/issues/305) + +### BUG FIXES + + * When uninstall fails, do not continue removing files - see [#315](https://github.com/chocolatey/choco/issues/315) + * Do not run autouninstaller if the package result is already a failure - see [#323](https://github.com/chocolatey/choco/issues/323) + * Fix - Auto Uninstaller can fail if chocolateyUninstall.ps1 uninstalls prior to it running - see [#304](https://github.com/chocolatey/choco/issues/304) + * Fix - Packages with content folders cannot have a dependency without also having a content folder - see [#290](https://github.com/chocolatey/choco/issues/290) + * Remove ShimGen director files on upgrade/uninstall - see [#326](https://github.com/chocolatey/choco/issues/326) + * If feature doesn't exist, throw an error - see [#317](https://github.com/chocolatey/choco/issues/317) + * Fix - The operation completed successfully on stderr - see [#249](https://github.com/chocolatey/choco/issues/249) + * Fix - When specific nuget version is needed by a package it is the chocolatey version that is used - see [#194](https://github.com/chocolatey/choco/issues/194) + * When installing with *.nupkg, need to get package name from package, not file name - see [#90](https://github.com/chocolatey/choco/issues/90) + * Fix - Choco pin list is not returning a list - see [#302](https://github.com/chocolatey/choco/issues/302) + * Fix - A pin is not created for existing installations (prior to new choco) - see [#60](https://github.com/chocolatey/choco/issues/60) + +### IMPROVEMENTS + + * Allow upgrade to always install missing packages - see [#300](https://github.com/chocolatey/choco/issues/300) + * Enhance Templates - see [#296](https://github.com/chocolatey/choco/issues/296) + * Always log debug output to the log file - see [#319](https://github.com/chocolatey/choco/issues/319) + * Warn when unable to snapshot locked files - see [#313](https://github.com/chocolatey/choco/issues/313) + * Use %systemroot% in place of %windir%. PATH exceed 2048 breaks choco - see [#252](https://github.com/chocolatey/choco/issues/252) + ## 0.9.9.6 Some really large fixes this release, especially removing all files that are installed to the package directory if they haven't changed, including ensuring that the nupkg file is always removed on successful uninstalls. The really big add some folks are going to like is the new outdated command. Some more variables that were misused have been brought back, which allows some packages (like Atom) to be installed again without issue. If you can believe some people never read these, we decided to add a note to the installer prompt to let people know about -y. From 888b9033f22332b616b306cd2713fd7417112a60 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Sat, 20 Jun 2015 09:41:22 -0500 Subject: [PATCH 39/61] (GH-305) Fix: uninstaller confirm if no silent When the user selects `Yes`, proceed with the uninstaller. Otherwise skip. --- .../infrastructure.app/services/AutomaticUninstallerService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index aa951bcd6f..1dcb52d745 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -131,6 +131,7 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) } var logLocation = _fileSystem.combine_paths(_fileSystem.get_full_path(config.CacheLocation), "chocolatey", pkgInfo.Package.Id, pkgInfo.Package.Version.to_string()); + this.Log().Debug(()=>" Setting up uninstall logging directory at {0}".format_with(logLocation)); _fileSystem.create_directory_if_not_exists(_fileSystem.get_directory_name(logLocation)); uninstallArgs = uninstallArgs.Replace(InstallTokens.PACKAGE_LOCATION, logLocation); @@ -142,7 +143,7 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) if (config.PromptForConfirmation) { var selection = InteractivePrompt.prompt_for_confirmation("Uninstall may not be silent (could not detect). Proceed?", new[] {"yes", "no"}, defaultChoice: null, requireAnswer: true); - if (selection.is_equal_to("no")) skipUninstaller = false; + if (selection.is_equal_to("yes")) skipUninstaller = false; } if (skipUninstaller) From f04877207118b83b6fcd0e5c933ecb154ac9daa8 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Sat, 20 Jun 2015 09:43:28 -0500 Subject: [PATCH 40/61] (GH-337) Add fault tolerance to registry keys Sometimes there may be issues accessing a key in the registry, don't allow it to stop capturing a snapshot. `Requested registry access is not allowed` is the error seen with GH-335. --- .../services/RegistryService.cs | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/RegistryService.cs b/src/chocolatey/infrastructure.app/services/RegistryService.cs index 5dee9bb8c8..fc19d2ad99 100644 --- a/src/chocolatey/infrastructure.app/services/RegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/RegistryService.cs @@ -25,6 +25,7 @@ namespace chocolatey.infrastructure.app.services using domain; using filesystem; using infrastructure.services; + using tolerance; using Registry = domain.Registry; /// @@ -42,6 +43,14 @@ public RegistryService(IXmlService xmlService, IFileSystem fileSystem) _fileSystem = fileSystem; } + private void add_key(IList keys, RegistryHive hive, RegistryView view) + { + FaultTolerance.try_catch_with_logging_exception( + () => keys.Add(RegistryKey.OpenBaseKey(hive, view)), + "Could not open registry hive '{0}' for view '{1}'".format_with(hive.to_string(), view.to_string()), + logWarningInsteadOfError: true); + } + public Registry get_installer_keys() { var snapshot = new Registry(); @@ -51,16 +60,20 @@ public Registry get_installer_keys() IList keys = new List(); if (Environment.Is64BitOperatingSystem) { - keys.Add(RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64)); - keys.Add(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)); + add_key(keys, RegistryHive.CurrentUser, RegistryView.Registry64); + add_key(keys, RegistryHive.LocalMachine, RegistryView.Registry64); } - keys.Add(RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32)); - keys.Add(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)); + add_key(keys, RegistryHive.CurrentUser, RegistryView.Registry32); + add_key(keys, RegistryHive.LocalMachine, RegistryView.Registry32); foreach (var registryKey in keys) { - var uninstallKey = registryKey.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey); + var uninstallKey = FaultTolerance.try_catch_with_logging_exception( + () => registryKey.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey), + "Could not open uninstall subkey for key '{0}'".format_with(registryKey.Name), + logWarningInsteadOfError: true); + if (uninstallKey != null) { //Console.WriteLine("Evaluating {0} of {1}".format_with(uninstallKey.View, uninstallKey.Name)); @@ -91,10 +104,21 @@ public Registry get_installer_keys() /// The snapshot. public void evaluate_keys(RegistryKey key, Registry snapshot) { - foreach (var subKeyName in key.GetSubKeyNames()) - { - evaluate_keys(key.OpenSubKey(subKeyName), snapshot); - } + if (key == null) return; + + FaultTolerance.try_catch_with_logging_exception( + () => + { + foreach (var subKeyName in key.GetSubKeyNames()) + { + FaultTolerance.try_catch_with_logging_exception( + () => evaluate_keys(key.OpenSubKey(subKeyName, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey), snapshot), + "Failed to open subkey named '{0}' for '{1}', likely due to permissions".format_with(subKeyName, key.Name), + logWarningInsteadOfError: true); + } + }, + "Failed to open subkeys for '{0}', likely due to permissions".format_with(key.Name), + logWarningInsteadOfError: true); var appKey = new RegistryApplicationKey { From 78ddf645f10526e71b7e38f709aca0242248ee04 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Sat, 20 Jun 2015 09:47:45 -0500 Subject: [PATCH 41/61] (version) 0.9.9.7 --- .uppercut | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.uppercut b/.uppercut index 87aa45f0e7..d9686134b0 100644 --- a/.uppercut +++ b/.uppercut @@ -18,7 +18,7 @@ - + From 7fa13ab44247de35ba0970da209006ad374c2e28 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Fri, 26 Jun 2015 23:17:55 -0500 Subject: [PATCH 42/61] (GH-14) Allow alternative sources to interact Allow alternative sources to interact with the console. --- .../infrastructure.app/services/CygwinService.cs | 4 +++- .../infrastructure.app/services/PythonService.cs | 16 ++++++++++++---- .../services/WindowsFeatureService.cs | 12 +++++++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/CygwinService.cs b/src/chocolatey/infrastructure.app/services/CygwinService.cs index 2b20a8bc6a..986ce8ede5 100644 --- a/src/chocolatey/infrastructure.app/services/CygwinService.cs +++ b/src/chocolatey/infrastructure.app/services/CygwinService.cs @@ -216,6 +216,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu get_exe(_rootDirectory), argsForPackage, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), (s, e) => { var logMessage = e.Data; @@ -236,7 +237,8 @@ public ConcurrentDictionary install_run(ChocolateyConfigu if (string.IsNullOrWhiteSpace(logMessage)) return; this.Log().Error("[{0}] {1}".format_with(APP_NAME, logMessage)); }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); if (exitCode != 0) diff --git a/src/chocolatey/infrastructure.app/services/PythonService.cs b/src/chocolatey/infrastructure.app/services/PythonService.cs index 963ec5c39a..bfe501ad7d 100644 --- a/src/chocolatey/infrastructure.app/services/PythonService.cs +++ b/src/chocolatey/infrastructure.app/services/PythonService.cs @@ -279,6 +279,7 @@ public ConcurrentDictionary list_run(ChocolateyConfigurat _exePath, args, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), stdOutAction: (s, e) => { var logMessage = e.Data; @@ -297,7 +298,8 @@ public ConcurrentDictionary list_run(ChocolateyConfigurat if (string.IsNullOrWhiteSpace(e.Data)) return; this.Log().Error(() => "{0}".format_with(e.Data)); }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); return packageResults; @@ -329,6 +331,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu _exePath, argsForPackage, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), (s, e) => { var logMessage = e.Data; @@ -361,7 +364,8 @@ public ConcurrentDictionary install_run(ChocolateyConfigu results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); } }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); if (exitCode != 0) @@ -401,6 +405,7 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu _exePath, argsForPackage, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), (s, e) => { var logMessage = e.Data; @@ -433,7 +438,8 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); } }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); if (exitCode != 0) @@ -466,6 +472,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi _exePath, argsForPackage, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), (s, e) => { var logMessage = e.Data; @@ -498,7 +505,8 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); } }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); if (exitCode != 0) diff --git a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs index 6e5b79f679..0bf41002cb 100644 --- a/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs +++ b/src/chocolatey/infrastructure.app/services/WindowsFeatureService.cs @@ -190,6 +190,7 @@ public ConcurrentDictionary list_run(ChocolateyConfigurat _exePath, args, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), stdOutAction: (s, e) => { var logMessage = e.Data; @@ -208,7 +209,8 @@ public ConcurrentDictionary list_run(ChocolateyConfigurat if (string.IsNullOrWhiteSpace(e.Data)) return; this.Log().Error(() => "{0}".format_with(e.Data)); }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); return packageResults; @@ -270,6 +272,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu _exePath, argsForPackage, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), (s, e) => { var logMessage = e.Data; @@ -295,7 +298,8 @@ public ConcurrentDictionary install_run(ChocolateyConfigu results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); if (exitCode != 0) @@ -352,6 +356,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi _exePath, argsForPackage, config.CommandExecutionTimeoutSeconds, + _fileSystem.get_current_directory(), (s, e) => { var logMessage = e.Data; @@ -377,7 +382,8 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi results.Messages.Add(new ResultMessage(ResultType.Error, logMessage)); }, - updateProcessPath: false + updateProcessPath: false, + allowUseWindow: true ); if (exitCode != 0) From beb404dfec89ff75865bec86a9d8856437e62ce8 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Fri, 26 Jun 2015 23:18:20 -0500 Subject: [PATCH 43/61] (maint) removing unused variable --- src/chocolatey.console/Program.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/chocolatey.console/Program.cs b/src/chocolatey.console/Program.cs index 82b2d21cfc..68b441834a 100644 --- a/src/chocolatey.console/Program.cs +++ b/src/chocolatey.console/Program.cs @@ -172,8 +172,6 @@ private static void report_version_and_exit_if_requested(string[] args, Chocolat } } - static EventHandler _handler; - private static void trap_exit_scenarios(ChocolateyConfiguration config) { ExitScenarioHandler.SetHandler(); From 37047fa070cffe7e0afb796c1e0945e70e93a0a3 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 29 Jun 2015 10:04:29 -0500 Subject: [PATCH 44/61] (GH-14) Log source type evaluated --- src/chocolatey/infrastructure.app/runners/GenericRunner.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs index 6ba73330ec..74ff83d398 100644 --- a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs +++ b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs @@ -113,11 +113,13 @@ now be in a bad state. Only official builds are to be trusted. return command; } - private static void set_source_type(ChocolateyConfiguration config) + private void set_source_type(ChocolateyConfiguration config) { var sourceType = SourceType.normal; Enum.TryParse(config.Sources, true, out sourceType); config.SourceType = sourceType; + + this.Log().Debug(()=> "The source '{0}' evaluated to a '{1}' source type".format_with(config.Sources,sourceType.to_string())); } public void run(ChocolateyConfiguration config, Container container, bool isConsole, Action parseArgs) From 563debbccc23b44f23117d32e830f8a11a73a316 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 29 Jun 2015 10:04:55 -0500 Subject: [PATCH 45/61] (GH-14) Throw when windowsfeature is not plural --- src/chocolatey/infrastructure.app/domain/SourceType.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/chocolatey/infrastructure.app/domain/SourceType.cs b/src/chocolatey/infrastructure.app/domain/SourceType.cs index 6c193ef063..1b7c156d31 100644 --- a/src/chocolatey/infrastructure.app/domain/SourceType.cs +++ b/src/chocolatey/infrastructure.app/domain/SourceType.cs @@ -25,6 +25,7 @@ public enum SourceType webpi, ruby, python, + windowsfeature, windowsfeatures, cygwin, } From 35c8a30416eb72b495973985432f09fd2d97685f Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Sat, 25 Jul 2015 12:23:52 -0500 Subject: [PATCH 46/61] (version) 0.9.10-alpha series --- .uppercut | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.uppercut b/.uppercut index f97da0a6b2..a9da054bf3 100644 --- a/.uppercut +++ b/.uppercut @@ -17,10 +17,10 @@ - - - - + + + + From 34089b7fc86393112535389d99bbb5abd91acffa Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Sat, 25 Jul 2015 16:43:40 -0500 Subject: [PATCH 47/61] (doc) update changelog Start adding resolved 0.9.10 series features. --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a88f7a6800..37d55109ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## [0.9.10](https://github.com/chocolatey/choco/issues?q=milestone%3A0.9.10+is%3Aclosed) (unreleased) + +Alternative sources (webpi, windowsfeature, cygwin, etc) are back (finally, right?)! + +### FEATURES + + * Alternative sources - see [#14](https://github.com/chocolatey/choco/issues/14) + * Support for custom headers - see [#332](https://github.com/chocolatey/choco/issues/332) + +### BUG FIXES + + * n/a currently + +### IMPROVEMENTS + + * AutoUninstaller is on by default - see [#308](https://github.com/chocolatey/choco/issues/308) + ## [0.9.9.9](https://github.com/chocolatey/choco/issues?q=milestone%3A0.9.9.9+is%3Aclosed) (unreleased) ### BUG FIXES From 52b12acbdedcab73d5606c0485c5f28ff05a69a3 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 30 Jul 2015 09:35:18 -0500 Subject: [PATCH 48/61] (maint) define windows only tests Based on http://stackoverflow.com/a/329072/18475 there is always a `__MonoCS__` compiler definition. This allows us to handily ignore some tests completely. --- src/chocolatey.tests/TinySpec.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/chocolatey.tests/TinySpec.cs b/src/chocolatey.tests/TinySpec.cs index 71b049464b..c2ce986c81 100644 --- a/src/chocolatey.tests/TinySpec.cs +++ b/src/chocolatey.tests/TinySpec.cs @@ -145,6 +145,19 @@ public PendingAttribute(string reason) } } +#if __MonoCS__ + public class WindowsOnlyAttribute : IgnoreAttribute + { + public WindowsOnlyAttribute() : base("This is a Windows only test") + { + } + } +#else + public class WindowsOnlyAttribute : Attribute + { + } +#endif + public class IntegrationAttribute : CategoryAttribute { public IntegrationAttribute() From eb6528ef3abee1c8cff26a85cb07bec1c54196c9 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 30 Jul 2015 10:07:36 -0500 Subject: [PATCH 49/61] (maint) registry specs are Windows only --- .../infrastructure.app/services/RegistryServiceSpecs.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs index 5d4bdda5a1..f2ec5feca2 100644 --- a/src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/services/RegistryServiceSpecs.cs @@ -46,8 +46,7 @@ protected void reset() } } - - + [WindowsOnly] public class when_RegistryService_get_installer_keys_is_called : RegistryServiceSpecsBase { private Registry _result; @@ -71,10 +70,9 @@ public void should_not_be_null() } + [WindowsOnly] public class when_RegistryService_get_key_is_called_for_a_value_that_exists : RegistryServiceSpecsBase { - //todo does any of this test correctly on *nix? - private RegistryKey _result; private RegistryHive _hive = RegistryHive.CurrentUser; private string _subkeyPath = "Console"; @@ -114,10 +112,9 @@ public void should_contain_values() } } + [WindowsOnly] public class when_RegistryService_get_key_is_called_for_a_value_that_does_not_exist : RegistryServiceSpecsBase { - //todo does any of this test correctly on *nix? - private RegistryKey _result; private RegistryHive _hive = RegistryHive.CurrentUser; private string _subkeyPath = "Software\\alsdjfalskjfaasdfasdf"; From 9598a79fc8e24d79a397c76a651c3676d6dafafd Mon Sep 17 00:00:00 2001 From: Matt Wrock Date: Sat, 22 Aug 2015 23:59:22 -0700 Subject: [PATCH 50/61] (GH-390) Fix log4net dependency version in nuspec This corrects the version of log4net declared in the chocolatey.lib nuspec. changing it to 2.0.3 which maps to assembly version 1.2.13. --- nuget/chocolatey.lib/chocolatey.lib.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuget/chocolatey.lib/chocolatey.lib.nuspec b/nuget/chocolatey.lib/chocolatey.lib.nuspec index 126062ab19..7357b7e3be 100644 --- a/nuget/chocolatey.lib/chocolatey.lib.nuspec +++ b/nuget/chocolatey.lib/chocolatey.lib.nuspec @@ -22,7 +22,7 @@ This is the core package which allows Chocolatey to be embedded in your applicat apt-get yum machine repository chocolatey https://github.com/chocolatey/chocolatey/raw/master/docs/logo/chocolateyicon.gif - + From fe8edc5237eb699373a89e23f820dc90174569f2 Mon Sep 17 00:00:00 2001 From: Matt Wrock Date: Sun, 23 Aug 2015 01:30:41 -0700 Subject: [PATCH 51/61] (GH-392) pass 4.0 reference assemblies to ilmerge Makes chocolatey builds more portable and allows it to be built on .net 4.5+ machines while maintaining compatibility with .net 4.0 clients. --- .build.custom/ilmerge.replace.build | 8 ++++---- .build.custom/ilmergeDLL.build | 4 ++-- .build/ilmerge.build | 8 ++++---- lib/NuGet-Chocolatey/strongname.cmd | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.build.custom/ilmerge.replace.build b/.build.custom/ilmerge.replace.build index 9dcb927ad5..11786707ad 100644 --- a/.build.custom/ilmerge.replace.build +++ b/.build.custom/ilmerge.replace.build @@ -25,8 +25,8 @@ - - + + @@ -39,8 +39,8 @@ - - + + diff --git a/.build.custom/ilmergeDLL.build b/.build.custom/ilmergeDLL.build index e12511e3b9..14a52be43a 100644 --- a/.build.custom/ilmergeDLL.build +++ b/.build.custom/ilmergeDLL.build @@ -26,8 +26,8 @@ - - + + - - + + @@ -38,8 +38,8 @@ - - + + diff --git a/lib/NuGet-Chocolatey/strongname.cmd b/lib/NuGet-Chocolatey/strongname.cmd index c902585d6a..a96c23d52b 100644 --- a/lib/NuGet-Chocolatey/strongname.cmd +++ b/lib/NuGet-Chocolatey/strongname.cmd @@ -7,4 +7,4 @@ SET DIR=%~d0%~p0% echo Make sure you have webtransform here for the merge. Continue? pause -%DIR%..\ILMerge\ILMerge.exe NuGet.Core.dll /keyfile:%DIR%..\..\chocolatey.snk /out:%DIR%NuGet.Core.dll /targetplatform:v4 /log:%DIR%ILMerge.DELETE.log /ndebug /allowDup \ No newline at end of file +%DIR%..\ILMerge\ILMerge.exe NuGet.Core.dll /keyfile:%DIR%..\..\chocolatey.snk /out:%DIR%NuGet.Core.dll /targetplatform:v4,"%ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0" /log:%DIR%ILMerge.DELETE.log /ndebug /allowDup \ No newline at end of file From 8c94c71bbc8b4cd2bbf8ed438928b4a60c7f3be3 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria Date: Sat, 5 Sep 2015 20:59:45 +0530 Subject: [PATCH 52/61] (GH-363) Show human-readable file sizes when downloading When downloading files, show the download progress like 'Saving 8.5 MB of 8.82 MB (8911368/9252864)' instead of 'Saving 8911368 of 9252864'. Adds Format-FileSize helper function to format the file sizes like mentioned above. --- .../chocolatey.resources.csproj | 1 + .../helpers/functions/Format-FileSize.ps1 | 25 +++++++++++++++++++ .../helpers/functions/Get-FtpFile.ps1 | 4 ++- .../helpers/functions/Get-WebFile.ps1 | 4 ++- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/chocolatey.resources/helpers/functions/Format-FileSize.ps1 diff --git a/src/chocolatey.resources/chocolatey.resources.csproj b/src/chocolatey.resources/chocolatey.resources.csproj index bbd951ecd9..306a783fae 100644 --- a/src/chocolatey.resources/chocolatey.resources.csproj +++ b/src/chocolatey.resources/chocolatey.resources.csproj @@ -43,6 +43,7 @@ + diff --git a/src/chocolatey.resources/helpers/functions/Format-FileSize.ps1 b/src/chocolatey.resources/helpers/functions/Format-FileSize.ps1 new file mode 100644 index 0000000000..c1be440737 --- /dev/null +++ b/src/chocolatey.resources/helpers/functions/Format-FileSize.ps1 @@ -0,0 +1,25 @@ +# Copyright © 2011 - Present RealDimensions Software, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Function Format-FileSize() { + Param ([double]$size) + Foreach ($unit in @('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB')) { + If ($size -lt 1024) { + return [string]::Format("{0:0.##} {1}", $size, $unit) + } + $size /= 1024 + } + return [string]::Format("{0:0.##} YB", $size) +} \ No newline at end of file diff --git a/src/chocolatey.resources/helpers/functions/Get-FtpFile.ps1 b/src/chocolatey.resources/helpers/functions/Get-FtpFile.ps1 index 9f7973b353..56e962c0f2 100644 --- a/src/chocolatey.resources/helpers/functions/Get-FtpFile.ps1 +++ b/src/chocolatey.resources/helpers/functions/Get-FtpFile.ps1 @@ -27,6 +27,7 @@ param( # send the ftp request to the server $ftpresponse = $ftprequest.GetResponse() [int]$goal = $ftpresponse.ContentLength + $goalFormatted = Format-FileSize $goal # get a download stream from the server response $reader = $ftpresponse.GetResponseStream() @@ -42,8 +43,9 @@ param( $writer.Write($buffer, 0, $count); if(!$quiet) { $total += $count + $totalFormatted = Format-FileSize $total if($goal -gt 0) { - Write-Progress "Downloading $url to $fileName" "Saving $total of $goal" -id 0 -percentComplete (($total/$goal)*100) + Write-Progress "Downloading $url to $fileName" "Saving $totalFormatted of $goalFormatted ($total/$goal)" -id 0 -percentComplete (($total/$goal)*100) } else { Write-Progress "Downloading $url to $fileName" "Saving $total bytes..." -id 0 -Completed } diff --git a/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 b/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 index 76b4298907..893dcef705 100644 --- a/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 +++ b/src/chocolatey.resources/helpers/functions/Get-WebFile.ps1 @@ -108,6 +108,7 @@ param( if($res.StatusCode -eq 200) { [long]$goal = $res.ContentLength + $goalFormatted = Format-FileSize $goal $reader = $res.GetResponseStream() if ($fileName) { @@ -140,8 +141,9 @@ param( $output += $encoding.GetString($buffer,0,$count) } elseif(!$quiet) { $total += $count + $totalFormatted = Format-FileSize $total if($goal -gt 0 -and ++$iterLoop%10 -eq 0) { - Write-Progress "Downloading $url to $fileName" "Saving $total of $goal" -id 0 -percentComplete (($total/$goal)*100) + Write-Progress "Downloading $url to $fileName" "Saving $totalFormatted of $goalFormatted ($total/$goal)" -id 0 -percentComplete (($total/$goal)*100) } if ($total -eq $goal) { From 8ade74d001a7d803b3245768d291f0909f2ce234 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 21 Sep 2015 11:43:07 -0500 Subject: [PATCH 53/61] (GH-416) Named arguments issue after merge Did not verify build after merge conflicts were resolved. Fix the named arguments issue. --- .../infrastructure.app/builders/ConfigurationBuilder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs index fba140317c..8f8b659d49 100644 --- a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs +++ b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs @@ -179,10 +179,10 @@ private static string set_config_item(string configName, ConfigFileSettings conf private static void set_feature_flags(ChocolateyConfiguration config, ConfigFileSettings configFileSettings) { - config.Features.CheckSumFiles = set_feature_flag(ApplicationParameters.Features.CheckSumFiles, configFileSettings, defaultEnabled: true, "Checksum files when pulled in from internet (based on package)."); - config.Features.AutoUninstaller = set_feature_flag(ApplicationParameters.Features.AutoUninstaller, configFileSettings, defaultEnabled: true, "Uninstall from programs and features without requiring an explicit uninstall script."); - config.Features.FailOnAutoUninstaller = set_feature_flag(ApplicationParameters.Features.FailOnAutoUninstaller, configFileSettings, defaultEnabled: false, "Fail if automatic uninstaller fails."); - config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false, "Prompt for confirmation in scripts or bypass."); + config.Features.CheckSumFiles = set_feature_flag(ApplicationParameters.Features.CheckSumFiles, configFileSettings, defaultEnabled: true, description: "Checksum files when pulled in from internet (based on package)."); + config.Features.AutoUninstaller = set_feature_flag(ApplicationParameters.Features.AutoUninstaller, configFileSettings, defaultEnabled: true, description: "Uninstall from programs and features without requiring an explicit uninstall script."); + config.Features.FailOnAutoUninstaller = set_feature_flag(ApplicationParameters.Features.FailOnAutoUninstaller, configFileSettings, defaultEnabled: false, description: "Fail if automatic uninstaller fails."); + config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false, description: "Prompt for confirmation in scripts or bypass."); } private static bool set_feature_flag(string featureName, ConfigFileSettings configFileSettings, bool defaultEnabled, string description) From de18ba81432a252f21d72994e650d4df3e220688 Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Thu, 24 Sep 2015 17:35:16 -0500 Subject: [PATCH 54/61] (GH-71) Add priority property to ChocolateySource --- .../infrastructure.app/configuration/ChocolateySource.cs | 5 +++++ .../services/ChocolateyConfigSettingsService.cs | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs index 5c163e98c6..a9eb754a16 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateySource.cs @@ -18,8 +18,13 @@ namespace chocolatey.infrastructure.app.configuration public class ChocolateySource { public string Id { get; set; } + public string Value { get; set; } + public bool Disabled { get; set; } + public bool Authenticated { get; set; } + + public int Priority { get; set; } } } diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs index 9093ec0d55..6942bdb443 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs @@ -61,8 +61,9 @@ public IEnumerable source_list(ChocolateyConfiguration configu list.Add(new ChocolateySource { Id = source.Id, Value = source.Value, - Disabled = source.Disabled, - Authenticated = string.IsNullOrWhiteSpace(source.Password) + Disabled = source.Disabled, + Authenticated = string.IsNullOrWhiteSpace(source.Password), + Priority = source.Priority }); } return list; From 4c550a87c04813e4ce9044a247cb0a164fcbfe89 Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Thu, 24 Sep 2015 17:54:25 -0500 Subject: [PATCH 55/61] (GH-427) Add paging to ListCommand --- .../commands/ChocolateyListCommand.cs | 18 ++++++++++++++++++ .../configuration/ChocolateyConfiguration.cs | 7 +++++++ .../infrastructure.app/nuget/NugetList.cs | 5 +++++ 3 files changed, 30 insertions(+) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs index 70ce382e1c..1855be78ec 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs @@ -15,6 +15,7 @@ namespace chocolatey.infrastructure.app.commands { + using System; using System.Collections.Generic; using System.Linq; using attributes; @@ -61,6 +62,22 @@ public void configure_argument_parser(OptionSet optionSet, ChocolateyConfigurati .Add("p=|password=", "Password - the user's password to the source. Defaults to empty.", option => configuration.SourceCommand.Password = option.remove_surrounding_quotes()) + .Add("page=", + "Page - the 'page' of results to return. Defaults to return all results.", option => + { + int page; + if (int.TryParse(option, out page)) + { + configuration.ListCommand.Page = page; + } + else + { + configuration.ListCommand.Page = null; + } + }) + .Add("page-size=", + "Page Size - the amount of package results to return per page. Defaults to 25.", + option => configuration.ListCommand.PageSize = int.Parse(option)) ; //todo exact name } @@ -94,6 +111,7 @@ choco list [] choco list --local-only choco list -li choco list -lai + choco list --page=0 --page-size=25 choco search git choco search git -s ""https://somewhere/out/there"" choco search bob -s ""https://somewhere/protected"" -u user -p pass diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index de49a57d4e..4012f90726 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -342,9 +342,16 @@ public sealed class FeaturesConfiguration [Serializable] public sealed class ListCommandConfiguration { + public ListCommandConfiguration() + { + PageSize = 25; + } + // list public bool LocalOnly { get; set; } public bool IncludeRegistryPrograms { get; set; } + public int? Page { get; set; } + public int PageSize { get; set; } } [Serializable] diff --git a/src/chocolatey/infrastructure.app/nuget/NugetList.cs b/src/chocolatey/infrastructure.app/nuget/NugetList.cs index 58ab1dc6a8..88c573311e 100644 --- a/src/chocolatey/infrastructure.app/nuget/NugetList.cs +++ b/src/chocolatey/infrastructure.app/nuget/NugetList.cs @@ -43,6 +43,11 @@ public static IEnumerable GetPackages(ChocolateyConfiguration configur results = results.Where(p => p.IsLatestVersion); } + if (configuration.ListCommand.Page.HasValue) + { + results = results.Skip(configuration.ListCommand.PageSize * configuration.ListCommand.Page.Value).Take(configuration.ListCommand.PageSize); + } + return results.OrderBy(p => p.Id) .AsEnumerable() .Where(PackageExtensions.IsListed) From baf9666662f979f0da29fbfd63bb5eaa48315bd5 Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Thu, 24 Sep 2015 19:04:57 -0500 Subject: [PATCH 56/61] (GH-433) Externalize some more package information. Externalizes more package information that was available through the API as properties, but were marked internal. Allows full access to all Package information. --- .build.custom/ilmerge.internalize.ignore.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.build.custom/ilmerge.internalize.ignore.txt b/.build.custom/ilmerge.internalize.ignore.txt index 8c1842244d..120f0425b2 100644 --- a/.build.custom/ilmerge.internalize.ignore.txt +++ b/.build.custom/ilmerge.internalize.ignore.txt @@ -1,3 +1,6 @@ chocolatey.* NuGet.Manifest -NuGet.IPackage \ No newline at end of file +NuGet.IPackage +NuGet.PackageDependency +NuGet.IServerPackageMetadata +NuGet.SemanticVersion \ No newline at end of file From 9a0ea94a0f80963cb4954e92f1f2248d8736e548 Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Mon, 28 Sep 2015 11:18:30 -0500 Subject: [PATCH 57/61] (GH-431) Update Nuget List to use IQueryable as far down as makes sense. Updated Nuget List to attempt to use IQueryable all the way down for queries executed against service based repositories. This allows chocolatey to defer filtering, sorting, and paging to the server rather than the client. Reverts back to the old logic, though, for everything else. --- .../infrastructure.app/nuget/NugetList.cs | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/chocolatey/infrastructure.app/nuget/NugetList.cs b/src/chocolatey/infrastructure.app/nuget/NugetList.cs index 88c573311e..b11458b96e 100644 --- a/src/chocolatey/infrastructure.app/nuget/NugetList.cs +++ b/src/chocolatey/infrastructure.app/nuget/NugetList.cs @@ -17,6 +17,7 @@ namespace chocolatey.infrastructure.app.nuget { using System.Collections.Generic; using System.Linq; + using NuGet; using configuration; @@ -25,13 +26,42 @@ namespace chocolatey.infrastructure.app.nuget public static class NugetList { public static IEnumerable GetPackages(ChocolateyConfiguration configuration, ILogger nugetLogger) + { + return execute_package_search(configuration, nugetLogger); + } + + private static IQueryable execute_package_search(ChocolateyConfiguration configuration, ILogger nugetLogger) { var packageRepository = NugetCommon.GetRemoteRepository(configuration, nugetLogger); + + // Whether or not the package is remote determines two things: + // 1. Does the repository have a notion of "listed"? + // 2. Does it support prerelease in a straight-forward way? + // Choco previously dealt with this by taking the path of least resistance and manually filtering out and sort unwanted packages + // This result in blocking operations that didn't let service based repositories, like OData, take care of heavy lifting on the server. + bool isRemote; + var aggregateRepo = packageRepository as AggregateRepository; + if (aggregateRepo != null) + { + isRemote = aggregateRepo.Repositories.All(repo => repo is IServiceBasedRepository); + } + else + { + isRemote = packageRepository is IServiceBasedRepository; + } + IQueryable results = packageRepository.Search(configuration.Input, configuration.Prerelease); if (configuration.AllVersions) { - return results.Where(PackageExtensions.IsListed).OrderBy(p => p.Id); + if (isRemote) + { + return results.OrderBy(p => p.Id); + } + else + { + return results.Where(PackageExtensions.IsListed).OrderBy(p => p.Id).AsQueryable(); + } } if (configuration.Prerelease && packageRepository.SupportsPrereleasePackages) @@ -43,17 +73,25 @@ public static IEnumerable GetPackages(ChocolateyConfiguration configur results = results.Where(p => p.IsLatestVersion); } + if (!isRemote) + { + results = + results + .Where(PackageExtensions.IsListed) + .Where(p => configuration.Prerelease || p.IsReleaseVersion()) + .distinct_last(PackageEqualityComparer.Id, PackageComparer.Version) + .AsQueryable(); + } + if (configuration.ListCommand.Page.HasValue) { results = results.Skip(configuration.ListCommand.PageSize * configuration.ListCommand.Page.Value).Take(configuration.ListCommand.PageSize); } - return results.OrderBy(p => p.Id) - .AsEnumerable() - .Where(PackageExtensions.IsListed) - .Where(p => configuration.Prerelease || p.IsReleaseVersion()) - .distinct_last(PackageEqualityComparer.Id, PackageComparer.Version); - } + return results.OrderBy(p => p.Id); + } + + } // ReSharper restore InconsistentNaming From 3bd5d64dd93bc28184f930c9c738dee7c9eced4b Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Mon, 28 Sep 2015 11:19:33 -0500 Subject: [PATCH 58/61] (GH-431) Add Count to IListCommand Uses the IQueryable changes to add an efficient count for retrieving the number of results that would be returned by a list, usually in a much faster, more efficient way. --- src/chocolatey/GetChocolatey.cs | 9 ++++++++ .../commands/ChocolateyListCommand.cs | 6 ++++++ .../commands/ChocolateySourceCommand.cs | 5 +++++ .../infrastructure.app/nuget/NugetList.cs | 5 +++++ .../runners/GenericRunner.cs | 18 ++++++++++++++++ .../services/ChocolateyPackageService.cs | 5 +++++ .../services/CygwinService.cs | 5 +++++ .../services/IChocolateyPackageService.cs | 7 +++++++ .../services/ISourceRunner.cs | 7 +++++++ .../services/NugetService.cs | 21 +++++++++++++++++++ .../services/PythonService.cs | 5 +++++ .../services/RubyGemsService.cs | 5 +++++ .../services/WebPiService.cs | 5 +++++ .../services/WindowsFeatureService.cs | 5 +++++ .../infrastructure/commands/IListCommand.cs | 7 ++++++- 15 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/chocolatey/GetChocolatey.cs b/src/chocolatey/GetChocolatey.cs index c8883f2192..7c58d2fd99 100644 --- a/src/chocolatey/GetChocolatey.cs +++ b/src/chocolatey/GetChocolatey.cs @@ -222,6 +222,15 @@ public IEnumerable List() var runner = new GenericRunner(); return runner.list(configuration, _container, isConsole: false, parseArgs: null); } + + public int Count() + { + extract_resources(); + var configuration = create_configuration(new List()); + configuration.RegularOutput = true; + var runner = new GenericRunner(); + return runner.count(configuration, _container, isConsole: false, parseArgs: null); + } } // ReSharper restore InconsistentNaming diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs index 1855be78ec..32cce2b02b 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs @@ -140,6 +140,12 @@ public IEnumerable list(ChocolateyConfiguration configuration) return _packageService.list_run(configuration); } + public int count(ChocolateyConfiguration config) + { + config.QuietOutput = true; + return _packageService.count_run(config); + } + public bool may_require_admin_access() { return false; diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs index d82876812a..c13331b055 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs @@ -148,6 +148,11 @@ public IEnumerable list(ChocolateyConfiguration configuration) return _configSettingsService.source_list(configuration); } + public int count(ChocolateyConfiguration config) + { + return list(config).Count(); + } + public bool may_require_admin_access() { return true; diff --git a/src/chocolatey/infrastructure.app/nuget/NugetList.cs b/src/chocolatey/infrastructure.app/nuget/NugetList.cs index b11458b96e..b3125d0da3 100644 --- a/src/chocolatey/infrastructure.app/nuget/NugetList.cs +++ b/src/chocolatey/infrastructure.app/nuget/NugetList.cs @@ -30,6 +30,11 @@ public static IEnumerable GetPackages(ChocolateyConfiguration configur return execute_package_search(configuration, nugetLogger); } + public static int GetCount(ChocolateyConfiguration configuration, ILogger nugetLogger) + { + return execute_package_search(configuration, nugetLogger).Count(); + } + private static IQueryable execute_package_search(ChocolateyConfiguration configuration, ILogger nugetLogger) { var packageRepository = NugetCommon.GetRemoteRepository(configuration, nugetLogger); diff --git a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs index 5347cec8c3..43f78e6150 100644 --- a/src/chocolatey/infrastructure.app/runners/GenericRunner.cs +++ b/src/chocolatey/infrastructure.app/runners/GenericRunner.cs @@ -150,6 +150,24 @@ public IEnumerable list(ChocolateyConfiguration config, Container containe } } + public int count(ChocolateyConfiguration config, Container container, bool isConsole, Action parseArgs) + { + var command = find_command(config, container, isConsole, parseArgs) as IListCommand; + if (command == null) + { + if (!string.IsNullOrWhiteSpace(config.CommandName)) + { + throw new Exception("The implementation of '{0}' does not support listing.".format_with(config.CommandName)); + } + return 0; + } + else + { + this.Log().Debug("_ {0}:{1} - Normal Count Mode _".format_with(ApplicationParameters.Name, command.GetType().Name)); + return command.count(config); + } + } + public void warn_when_admin_needs_elevation(ChocolateyConfiguration config) { var shouldWarn = (!config.Information.IsProcessElevated && config.Information.IsUserAdministrator); diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index 2d5fe060ce..356bc57480 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -70,6 +70,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config) perform_source_runner_action(config, r => r.ensure_source_app_installed(config, (packageResult) => handle_package_result(packageResult, config, CommandNameType.install))); } + public int count_run(ChocolateyConfiguration config) + { + return perform_source_runner_function(config, r => r.count_run(config)); + } + private void perform_source_runner_action(ChocolateyConfiguration config, Action action) { var runner = _sourceRunners.FirstOrDefault(r => r.SourceType == config.SourceType); diff --git a/src/chocolatey/infrastructure.app/services/CygwinService.cs b/src/chocolatey/infrastructure.app/services/CygwinService.cs index c94681202b..911f87b896 100644 --- a/src/chocolatey/infrastructure.app/services/CygwinService.cs +++ b/src/chocolatey/infrastructure.app/services/CygwinService.cs @@ -156,6 +156,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action

The configuration. void ensure_source_app_installed(ChocolateyConfiguration config); + ///

+ /// Retrieves the count of items that meet the search criteria. + /// + /// + /// + int count_run(ChocolateyConfiguration config); + /// /// Run list in noop mode /// diff --git a/src/chocolatey/infrastructure.app/services/ISourceRunner.cs b/src/chocolatey/infrastructure.app/services/ISourceRunner.cs index ac9826ddfd..c881f10a64 100644 --- a/src/chocolatey/infrastructure.app/services/ISourceRunner.cs +++ b/src/chocolatey/infrastructure.app/services/ISourceRunner.cs @@ -39,6 +39,13 @@ public interface ISourceRunner /// The action to continue with as part of the install void ensure_source_app_installed(ChocolateyConfiguration config, Action ensureAction); + /// + /// Retrieve the listed packages from the source feed cout + /// + /// The configuration. + /// Packages count + int count_run(ChocolateyConfiguration config); + /// /// Run list in noop mode /// diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index c6defdd7cd..d217a8626e 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -75,6 +75,27 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action

: ICommand + public interface IListCommand : ICommand + { + int count(ChocolateyConfiguration config); + } + + public interface IListCommand : IListCommand { IEnumerable list(ChocolateyConfiguration config); } From 981d1c5595953afb336e5da6c47af2bc29816cb7 Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Tue, 29 Sep 2015 09:56:37 -0500 Subject: [PATCH 59/61] (GH-431) Changed Count to ListCount. Added xml docs to List and ListCount. --- src/chocolatey/GetChocolatey.cs | 48 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/chocolatey/GetChocolatey.cs b/src/chocolatey/GetChocolatey.cs index 7c58d2fd99..d35529b113 100644 --- a/src/chocolatey/GetChocolatey.cs +++ b/src/chocolatey/GetChocolatey.cs @@ -185,6 +185,36 @@ public void RunConsole(string[] args) runner.run(args, configuration, _container); } + ///

+ /// Run chocolatey after setting the options, and list the results. + /// + /// The typer of results you're expecting back. + public IEnumerable List() + { + extract_resources(); + var configuration = create_configuration(new List()); + configuration.RegularOutput = true; + var runner = new GenericRunner(); + return runner.list(configuration, _container, isConsole: false, parseArgs: null); + } + + /// + /// Run chocolatey after setting the options, + /// and get the count of items that would be returned if you listed the results. + /// + /// + /// Is intended to be more efficient then simply calling List and then Count() on the returned list. + /// It also returns the full count as is ignores paging. + /// + public int ListCount() + { + extract_resources(); + var configuration = create_configuration(new List()); + configuration.RegularOutput = true; + var runner = new GenericRunner(); + return runner.count(configuration, _container, isConsole: false, parseArgs: null); + } + private ChocolateyConfiguration create_configuration(IList args) { var configuration = new ChocolateyConfiguration(); @@ -213,24 +243,6 @@ private void extract_resources() AssemblyFileExtractor.extract_all_resources_to_relative_directory(_container.GetInstance(), Assembly.GetAssembly(typeof(ChocolateyResourcesAssembly)), ApplicationParameters.InstallLocation, folders, ApplicationParameters.ChocolateyFileResources); } - - public IEnumerable List() - { - extract_resources(); - var configuration = create_configuration(new List()); - configuration.RegularOutput = true; - var runner = new GenericRunner(); - return runner.list(configuration, _container, isConsole: false, parseArgs: null); - } - - public int Count() - { - extract_resources(); - var configuration = create_configuration(new List()); - configuration.RegularOutput = true; - var runner = new GenericRunner(); - return runner.count(configuration, _container, isConsole: false, parseArgs: null); - } } // ReSharper restore InconsistentNaming From 7579937ad736ed6aa4192540a5556f3c24a328c1 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 7 Oct 2015 09:58:40 -0500 Subject: [PATCH 60/61] (doc) update release notes/nuspec for 0.9.10 --- CHANGELOG.md | 7 +- nuget/chocolatey/chocolatey.nuspec | 315 ++--------------------------- 2 files changed, 20 insertions(+), 302 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce9a80ad17..6a7dc496fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,16 @@ Alternative sources (webpi, windowsfeature, cygwin, etc) are back (finally, righ ### BUG FIXES - * n/a currently + * Fix - Merging assemblies on a machine running .net 4.5 or higher produces binaries incompatible with .net 4 - see [#392](https://github.com/chocolatey/choco/issues/392) + * Fix - API - Incorrect log4net version in chocolatey.lib dependencies - see [#390](https://github.com/chocolatey/choco/issues/390) ### IMPROVEMENTS * AutoUninstaller is on by default - see [#308](https://github.com/chocolatey/choco/issues/308) + * Show human-readable file sizes when downloading - see [#363](https://github.com/chocolatey/choco/issues/363) + * API - Add the ability to retrieve package count for a Source - see [#431](https://github.com/chocolatey/choco/issues/431) + * API - Chocolatey Lib still marks vital package information as internal - see [#433](https://github.com/chocolatey/choco/issues/433) + * API - Add paging to list command - see [#427](https://github.com/chocolatey/choco/issues/427) ## [0.9.9.11](https://github.com/chocolatey/choco/issues?q=milestone%3A0.9.9.11+is%3Aclosed) (October 6, 2015) diff --git a/nuget/chocolatey/chocolatey.nuspec b/nuget/chocolatey/chocolatey.nuspec index 469751c349..0bc3ec1f72 100644 --- a/nuget/chocolatey/chocolatey.nuspec +++ b/nuget/chocolatey/chocolatey.nuspec @@ -17,14 +17,16 @@ You can host your own sources and add them to Chocolatey, you can extend Chocola ### Commands There are quite a few commands you can call - you should check out the [command reference](https://github.com/chocolatey/choco/wiki/CommandsReference). Here are the most common: + * Help - choco -? or choco command -? * Search - choco search something * List - choco list -lo + * Config - choco config list * Install - choco install baretail * Pin - choco pin windirstat * Upgrade - choco upgrade baretail * Uninstall - choco uninstall baretail - Alternative installation sources (TEMPORARILY DISABLED IN 0.9.9 series): + Alternative installation sources: * Install ruby gem - choco install compass -source ruby * Install python egg - choco install sphynx -source python * Install windows feature - choco install IIS -source windowsfeatures @@ -40,317 +42,28 @@ In that mess there is a link to the [Helper Reference](https://github.com/chocol See all - https://github.com/chocolatey/choco/blob/master/CHANGELOG.md -## 0.9.9.11 +## 0.9.10 -### BUG FIXES - - * Fix - Pin list is broken - see [#452](https://github.com/chocolatey/choco/issues/452) - -## 0.9.9.10 - -Not to be confused with 0.9.10 (this is not that version). This fixes a small but extremely significant issue with relation to configuration managers and other tools that use choco. - -### BUG FIXES - - * Fix - List output for other tools messed up in 0.9.9.9 (pipe separator missing) - see [#450](https://github.com/chocolatey/choco/issues/450) - * Fix - accidentally escaped characters in "new" -help - see [#447](https://github.com/chocolatey/choco/issues/447) - -## 0.9.9.9 - -With this release you can completely configure choco from the command line (including the priority of sources). Choco now allows you to create [custom package templates](https://github.com/chocolatey/choco/issues/76). Choco has [proper proxy support](https://github.com/chocolatey/choco/issues/243) now. We also squashed up some bugs, like the infinite download loop that happens if the connection is lost. We've also improved the installation experience of Chocolatey itself, [unpacking all of the required setup files in the chocolatey package](https://github.com/chocolatey/choco/issues/347) and improving the messaging output during the bootstrapping process. Chocolatey also [doesn't try to write config updates every command](https://github.com/chocolatey/choco/issues/364), unless something actually changes in the config file. And last but not least for mentions, the issue of [choco not recognizing itself as needing upgraded after being installed by the bootstrapper](https://github.com/chocolatey/choco/issues/414) is now fixed. - -### FEATURES - - * Config Command - see [#417](https://github.com/chocolatey/choco/issues/417) - * Create Custom Package Templates - see [#76](https://github.com/chocolatey/choco/issues/76) - * Proxy Support - see [#243](https://github.com/chocolatey/choco/issues/243) - -### BUG FIXES - - * Fix - [Security] Remove rollback should validate it exists in choco install backup directory - see [#387](https://github.com/chocolatey/choco/issues/387) - * Fix - Ensure chocolatey is installed into the lib folder during initial install - see [#414](https://github.com/chocolatey/choco/issues/414) - * Fix - Infinite loop downloading files if connection is lost - see [#285](https://github.com/chocolatey/choco/issues/285) - * Fix - list / search results blocking until completion instead of streaming output - see [#143](https://github.com/chocolatey/choco/issues/143) - * Fix - default template install script for MSI silentArgs are bad - see [#354](https://github.com/chocolatey/choco/issues/354) - * Fix - Deleting read-only files fails - see [#338](https://github.com/chocolatey/choco/issues/338) and [#263](https://github.com/chocolatey/choco/issues/263) - * Fix - If the package uses $packageParameters instead of $env:PackageParameters, quotes are removed - see [#406](https://github.com/chocolatey/choco/issues/406) - * Fix - Choco upgrade not downloading new installer if current installer is the same size - see [#405](https://github.com/chocolatey/choco/issues/405) - * Fix - Exit with non-zero code if install/upgrade version and a newer version is installed - see [#365](https://github.com/chocolatey/choco/issues/365) - * Fix - Chocolately can permanently corrupt the config file if an operation is interrupted - see [#355](https://github.com/chocolatey/choco/issues/355) - * Fix - Handle PowerShell's `InitializeDefaultDrives` Error (that should just be a warning) - see [#349](https://github.com/chocolatey/choco/issues/349) - * Fix - Checksumming can not be turned off by the feature flag - see [#33](https://github.com/chocolatey/choco/issues/33) - * Fix - Process with an id of is not running errors on 0.9.9.8 - see [#346](https://github.com/chocolatey/choco/issues/346) - * Fix - Export cmdlets for automation scripts - see [#422](https://github.com/chocolatey/choco/issues/422) - -### IMPROVEMENTS - - * [Security] Add SHA-2 (sha256 / sha512) to checksum - see [#113](https://github.com/chocolatey/choco/issues/113) - * Sources should have explicit priority order- see [#71](https://github.com/chocolatey/choco/issues/71) - * Unpack the powershell files just before packaging up the nupkg (Installing chocolatey meta) - see [#347](https://github.com/chocolatey/choco/issues/347) - * API - List --localonly not working by default - see [#223](https://github.com/chocolatey/choco/issues/223) - * API - Expose package results - see [#132](https://github.com/chocolatey/choco/issues/132) - * API - Externalize IPackage and its interfaces - see [#353](https://github.com/chocolatey/choco/issues/353) - * Enhance "Access to path is denied" message on no admin rights - see [#177](https://github.com/chocolatey/choco/issues/177) - * Only update chocolatey.config if there are changes - see [#364](https://github.com/chocolatey/choco/issues/364) - * Modify source when attempting to add a source with same name but different URL - see [#88](https://github.com/chocolatey/choco/issues/88) - * Features should contain description - see [#416](https://github.com/chocolatey/choco/issues/416) - * Chocolatey Installer - removing modules not loaded - see [#442](https://github.com/chocolatey/choco/issues/442) - * Chocolatey Installer - Don't use Write-Host - see [#444](https://github.com/chocolatey/choco/issues/444) - * Set environment variables once configuration is complete - see [#420](https://github.com/chocolatey/choco/issues/420) - * Enhance Package Template for 0.9.9.9 - see [#366](https://github.com/chocolatey/choco/issues/366) - -## 0.9.9.8 - -### BUG FIXES - - * Fix: choco install -y C: deletes all files - see [#341](https://github.com/chocolatey/choco/issues/341) - * Fix: Read-Host halts scripts rather than prompt for input - see [#219](https://github.com/chocolatey/choco/issues/219) - -### IMPROVEMENTS - - * Download Progress Bar is Missing - see [#56](https://github.com/chocolatey/choco/issues/56) - -## 0.9.9.7 - -"Fix Everything. Fix All The Things" - There have been some things bugging us for a long time related to limitations with NuGet, so we decided to fix that. Like [nuspec enhancements](https://github.com/chocolatey/choco/issues/205), that crazy [content folder restriction](https://github.com/chocolatey/choco/issues/290) has been removed (I know, right?!), and we're working around [badly](https://github.com/chocolatey/choco/issues/316) [behaved](https://github.com/chocolatey/choco/issues/326) packages quite a bit more to bring you more feature parity. - -Let's talk about a couple of big, like really big, BIG features just added with this release. No more packages rebooting Windows. We fixed ([#304](https://github.com/chocolatey/choco/issues/304) / [#323](https://github.com/chocolatey/choco/issues/323)) and [enhanced](https://github.com/chocolatey/choco/issues/305) up the Auto Uninstaller Service quite a bit to ensure things are working like you would expect (It goes on by default in 0.9.10 - we'll start documenting more about it soon). But wait, there's more! I haven't even told you about the big features yet - -The first big feature is enhancing the nuspec. I mentioned this I know, but *now* you can use `packageSourceUrl` in the nuspec to tell folks where you are storing the source for the package! We also added `projectSourceUrl`, `docsUrl`, `mailingListUrl`, and `bugTrackerUrl`. What's even better is that the community feed has already been enhanced to look for these values. So have the templates from `choco new`. And it's backwards compatible, meaning you can still install packages that have these added nuspec enhancements without issue (but we will need to provide a fix for Nuget Package Explorer). - -The second is Xml Document Transformations (XDT), which I think many folks are aware of but may not realize what it can provide. [NuGet has allowed transformations for quite awhile](https://docs.nuget.org/Create/Configuration-File-and-Source-Code-Transformations) to allow you to make changes to an `app.config`/`web.config` on install/uninstall. We are following in similar footsteps to allow you to do similar when installing/upgrading packages. We will look for `*.install.xdt` files in the package (doesn't matter where) and they will apply to configuration files with the same name in the package. This means that during upgrades we won't overwrite configuration files during upgrades that have opted into this feature. It allows you to give users a better experience during upgrades because they won't need to keep making the same changes to the xml config files each time they upgrade your package. - -### FEATURES - - * Allow XDT Configuration Transforms - see [#331](https://github.com/chocolatey/choco/issues/331) - * Prevent reboots - see [#316](https://github.com/chocolatey/choco/issues/316) - * Enhance the nuspec - first wave - see [#205](https://github.com/chocolatey/choco/issues/205) - * Uninstaller Service Enhancements - see [#305](https://github.com/chocolatey/choco/issues/305) - -### BUG FIXES - - * When uninstall fails, do not continue removing files - see [#315](https://github.com/chocolatey/choco/issues/315) - * Do not run autouninstaller if the package result is already a failure - see [#323](https://github.com/chocolatey/choco/issues/323) - * Fix - Auto Uninstaller can fail if chocolateyUninstall.ps1 uninstalls prior to it running - see [#304](https://github.com/chocolatey/choco/issues/304) - * Fix - Packages with content folders cannot have a dependency without also having a content folder - see [#290](https://github.com/chocolatey/choco/issues/290) - * Remove ShimGen director files on upgrade/uninstall - see [#326](https://github.com/chocolatey/choco/issues/326) - * If feature doesn't exist, throw an error - see [#317](https://github.com/chocolatey/choco/issues/317) - * Fix - The operation completed successfully on stderr - see [#249](https://github.com/chocolatey/choco/issues/249) - * Fix - When specific nuget version is needed by a package it is the chocolatey version that is used - see [#194](https://github.com/chocolatey/choco/issues/194) - * When installing with *.nupkg, need to get package name from package, not file name - see [#90](https://github.com/chocolatey/choco/issues/90) - * Fix - Choco pin list is not returning a list - see [#302](https://github.com/chocolatey/choco/issues/302) - * Fix - A pin is not created for existing installations (prior to new choco) - see [#60](https://github.com/chocolatey/choco/issues/60) - -### IMPROVEMENTS - - * Allow upgrade to always install missing packages - see [#300](https://github.com/chocolatey/choco/issues/300) - * Enhance Templates - see [#296](https://github.com/chocolatey/choco/issues/296) - * Always log debug output to the log file - see [#319](https://github.com/chocolatey/choco/issues/319) - * Warn when unable to snapshot locked files - see [#313](https://github.com/chocolatey/choco/issues/313) - * Use %systemroot% in place of %windir%. PATH exceed 2048 breaks choco - see [#252](https://github.com/chocolatey/choco/issues/252) - -## 0.9.9.6 - -Some really large fixes this release, especially removing all files that are installed to the package directory if they haven't changed, including ensuring that the nupkg file is always removed on successful uninstalls. The really big add some folks are going to like is the new outdated command. Some more variables that were misused have been brought back, which allows some packages (like Atom) to be installed again without issue. If you can believe some people never read these, we decided to add a note to the installer prompt to let people know about -y. - -### FEATURES - - * Outdated Command - Use `choco outdated` to see outdated packages - see [#170](https://github.com/chocolatey/choco/issues/170) - -### BUG FIXES - - * Fix - NotSilent Switch Not Working - see [#281](https://github.com/chocolatey/choco/issues/281) - * Fix - Silent installation of choco without admin is not possible - see [#274](https://github.com/chocolatey/choco/issues/274) - * Fix - Package resolves to latest version from any source - see [#279](https://github.com/chocolatey/choco/issues/279) - * Fix - Install fails when shortcut creation fails - see [#264](https://github.com/chocolatey/choco/issues/264) - * Fix - Error deserializing response of type Registry - see [#257](https://github.com/chocolatey/choco/issues/257) - * Fix - Auto uninstaller should not depend on optional InstallLocation value - see [#255](https://github.com/chocolatey/choco/issues/255) - * Fix - Nupkg is left but reported as successfully uninstalled by NuGet - see [#254](https://github.com/chocolatey/choco/issues/254) - * Fix - SHA1 checksum compared as MD5 for Install-ChocolateyZipPackage - see [#253](https://github.com/chocolatey/choco/issues/253) - * Fix - Auto uninstaller strips off "/" and "-" in arguments - see [#212](https://github.com/chocolatey/choco/issues/212) -### IMPROVEMENTS - - * Uninstall removes all installed files if unchanged - see [#121](https://github.com/chocolatey/choco/issues/121) - * Auto uninstaller should convert /I to /X for Msi Uninstalls - see [#271](https://github.com/chocolatey/choco/issues/271) - * Bring back more variables for feature parity - see [#267](https://github.com/chocolatey/choco/issues/267) - * Mention -y in the prompt - see [#265](https://github.com/chocolatey/choco/issues/265) - -## 0.9.9.5 - -### BREAKING CHANGES - - * Renamed short option `p` to `i` for list --include-programs so that `p` could be ubiquitous for password across commands that optionally can pass a password - see [#240](https://github.com/chocolatey/choco/issues/240) - -### BUG FIXES - - * Fix - Secure Sources Not Working - see [#240](https://github.com/chocolatey/choco/issues/240) - * Fix - Generate-BinFile / Remove-BinFile - see [#230](https://github.com/chocolatey/choco/issues/230) - * Fix - cpack should only include files from nuspec - see [#232](https://github.com/chocolatey/choco/issues/232) - * Fix - cpack should leave nupkg in current directory - see [#231](https://github.com/chocolatey/choco/issues/231) - * Fix - Install-PowerShellCommand uses incorrect path - see [#241](https://github.com/chocolatey/choco/issues/241) - * Fix - choco list source with redirects does not resolve - see [#171](https://github.com/chocolatey/choco/issues/171) - * Fix - choco tried to resolve disabled repo - see [#169](https://github.com/chocolatey/choco/issues/169) - * Fix - cpack nuspec results in "The path is not of a legal form" - see [#164](https://github.com/chocolatey/choco/issues/164) - * Fix - cpack hangs on security related issue - see [#160](https://github.com/chocolatey/choco/issues/160) - * Fix - spelling error in "package has been upgradeed successfully" - see [#64](https://github.com/chocolatey/choco/issues/64) - -### IMPROVEMENTS - - * Api Key and Source matching could be more intuitive - see [#228](https://github.com/chocolatey/choco/issues/238) - * Remove warning about allowGlobalConfirmation being enabled - see [#237](https://github.com/chocolatey/choco/issues/237) - * Include log file path when saying 'See the log for details' - see [#187](https://github.com/chocolatey/choco/issues/187) - * Uninstall prompts for version when there is only one installed - see [#186](https://github.com/chocolatey/choco/issues/186) - * Do not offer a default option when prompting for a user choice - see [#185](https://github.com/chocolatey/choco/issues/185) - * Remove the warning note about skipping, and instead show the warning when selecting skip - see [#183](https://github.com/chocolatey/choco/issues/183) - * Do not print PowerShell install/update scripts by default - see [#182](https://github.com/chocolatey/choco/issues/182) - -## 0.9.9.4 - -### BUG FIXES - - * Fix - The term 'false' is not recognized as the name of a cmdlet - see [#215](https://github.com/chocolatey/choco/issues/215) - -### IMPROVEMENTS - - * Some packages use non-API variables like $installArguments - see [#207](https://github.com/chocolatey/choco/issues/207) - -## 0.9.9.3 - -### BUG FIXES - - * Fix - Install .NET Framework immediately during install - see [#168](https://github.com/chocolatey/choco/issues/168) - * Fix - Do not error on Set-Acl during install/upgrade - see [#163](https://github.com/chocolatey/choco/issues/163) - * Fix - Do not escape curly braces in powershell script - see [#208](https://github.com/chocolatey/choco/issues/208) - * Fix - Formatting issues on --noop command logging - see [#202](https://github.com/chocolatey/choco/issues/202) - * Fix - Uninstaller check doesn't find 32-bit registry keys - see [#197](https://github.com/chocolatey/choco/issues/197) - * Fix - Uninstaller errors on short path to msiexec - see [#211](https://github.com/chocolatey/choco/issues/211) - -### IMPROVEMENTS - - * Some packages use non-API variables like $installArguments - see [#207](https://github.com/chocolatey/choco/issues/207) - * Add Generate-BinFile to Helpers (widely used but never part of API) - see [#145](https://github.com/chocolatey/choco/issues/145) - * Add Remove-BinFile to Helpers - see [#195](https://github.com/chocolatey/choco/issues/195) - * Get-ChocolateyWebFile should create path if it doesn't exist - see [#167](https://github.com/chocolatey/choco/issues/167) - -## 0.9.9.2 - -### BUG FIXES - - * Fix - Allow passing install arguments again (regression in 0.9.9 series) - see [#150](https://github.com/chocolatey/choco/issues/150) - * Fix - Allow apostrophes to be used as quotes - quoting style that worked with previous client - see [#141](https://github.com/chocolatey/choco/issues/141) - * Fix - Shims write errors to stderr - see [#142](https://github.com/chocolatey/choco/issues/142) and [ShimGen #14](https://github.com/chocolatey/shimgen/issues/14) - -### IMPROVEMENTS - - * Upgrade `-r` should always return a value - see [#153](https://github.com/chocolatey/choco/issues/153) - -## 0.9.9.1 - -### BUG FIXES - - * Fix - Get-BinRoot broken - see [#144](https://github.com/chocolatey/choco/issues/144) - -##0.9.9 - -This also includes issues that were being tracked in the old Chocolatey repository: [Chocolatey 0.9.9](https://github.com/chocolatey/chocolatey/issues?q=is%3Aclosed+label%3Av0.9.9). - -The two links above will not capture everything that has changed, since this is a complete rewrite. We broke everything. If this were a v1+, it would be a major release. But we are less than v1, so 0.9.9 it is! ;) - -Okay, so we didn't really break everything. We have maintained nearly full compatibility with how you pass options into choco, although the output may be a bit different (but better, we hope) and in at least one case, additional switches (or a feature setting) is/are required - we limited this to security related changes only. - -We also fixed and improved a bunch of things, so we feel the trade off is well worth the changes. - -We'll try to capture everything here that you should know about. Please call `choco -?` or `choco.exe -h` to get started. - -### KNOWN ISSUES - - * [Known Issues](https://github.com/chocolatey/choco/labels/Bug) - * TEMPORARY `install all` is missing - this is expected to be back in 0.9.10 - see [#23](https://github.com/chocolatey/choco/issues/23) - * Alternative sources (`webpi`,`ruby`,`python`,`cygwin`, `windowsfeature`) do not work yet. This is expected to be fixed in 0.9.10 - see [#14](https://github.com/chocolatey/choco/issues/14) - * Progress bar is missing when downloading until we are using internal posh components for Packages - see [#56](https://github.com/chocolatey/choco/issues/56) - * See [Feature Parity](https://github.com/chocolatey/choco/labels/FeatureParity) for items not yet reimplemented from older PowerShell Chocolatey client (v0.9.8.32 and below). - -### BREAKING CHANGES - - * [Security] **Prompt for confirmation**: For security reasons, we now stop for confirmation before changing the state of the system on most commands. You can pass `-y` to confirm any prompts or set a value in the config that will globally confirm - see [#52](https://github.com/chocolatey/choco/issues/52) (**NOTE**: This is one of those additional switches we were talking about) - * [Security] If your default installation is still at `c:\Chocolatey`, this version will force a move to ProgramData and update the environment settings - see [#7](https://github.com/chocolatey/choco/issues/7) - * **Configuration Breaking Changes:** - 1. You now have one config file to interact with in %ChocolateyInstall%\config - your user config is no longer valid and can be removed once you migrate settings to the config. - 2. The config will no longer be overwritten on upgrade. - 3. Choco no longer interacts with NuGet's config file at all. You will need to reset all of your apiKeys (see features for `apikey`). On the plus side, the keys will work for all users of the machine, unlike NuGet's apiKeys (only work for the user that sets them). - 4. This also means you can no longer use `useNugetForSources`. It has been removed as a config setting. - * **Packaging Changes:** - 1. Choco now installs packages without version numbers on folders. This means quite a few things... - 2. Upgrading packages doesn't install a new version next to an old version, it actually upgrades. - 3. Dependencies resolve at highest available version, not the minimum version as before - see [Chocolatey #415](https://github.com/chocolatey/chocolatey/issues/415) - * **Package Maintenance Changes**: - 1. Read the above about apikey changes - 2. Read above about dependency resolution changes. - * **Deprecated/Removed Commands:** - 1. `installmissing` has been removed. It was deprecated awhile ago, so this should not be a surprise. - 2. `choco version` has been deprecated and will be removed in v1. Use `choco upgrade pkgName --noop` or `choco upgrade pkgName -whatif` instead. - 3. `Write-ChocolateySuccess`, `Write-ChocolateyFailure` have been deprecated. - 4. `update` is now `upgrade`. `update` has been deprecated and will be removed/replaced in v1. Update will be reincarnated later for a different purpose. **Hint**: It rhymes with smackage pindexes. +Alternative sources (webpi, windowsfeature, cygwin, etc) are back (finally, right?)! ### FEATURES - * In app documentation! Use `choco -?`, `choco -h` or `choco commandName -?` to learn about each command, complete with examples! - * WhatIf/Noop mode for all commands (`--noop` can also be specified as `-whatif`) - see [Chocolatey #263](https://github.com/chocolatey/chocolatey/issues/263) and [Default Options and Switches](https://github.com/chocolatey/choco/wiki/CommandsReference#default-options-and-switches) - * Performs like a package manager, expect to see queries failing because of unmet dependency issues. - * **New Commands:** - 1. `pin` - Suppress upgrades. This allows you to 'pin' an install to a particular version - see [#1](https://github.com/chocolatey/choco/issues/1), [Chocolatey #5](https://github.com/chocolatey/chocolatey/issues/5) and [Pin Command](https://github.com/chocolatey/choco/wiki/CommandsPin) - 2. `apikey` - see [ApiKey Command](https://github.com/chocolatey/choco/wiki/CommandsApiKey) - 3. `new` - see [New Command](https://github.com/chocolatey/choco/wiki/CommandsNew) and [Chocolatey #157](https://github.com/chocolatey/chocolatey/issues/157) - * New ways to pass arguments! See [How to Pass Options/Switches](https://github.com/chocolatey/choco/wiki/CommandsReference#how-to-pass-options--switches) - * Did we mention there is a help menu that is actually helpful now? Shiny! - * AutoUninstaller!!!! But it is not enabled by default this version. See [#15](https://github.com/chocolatey/choco/issues/15), [#9](https://github.com/chocolatey/choco/issues/9) and [Chocolatey #6](https://github.com/chocolatey/chocolatey/issues/6) - * **New Helpers:** - 1. `Install-ChocolateyShortcut` - see [Chocolatey #238](https://github.com/chocolatey/chocolatey/pull/238), [Chocolatey #235](https://github.com/chocolatey/chocolatey/issues/235) and [Chocolatey #218](https://github.com/chocolatey/chocolatey/issues/218) + * Alternative sources - see [#14](https://github.com/chocolatey/choco/issues/14) + * Support for custom headers - see [#332](https://github.com/chocolatey/choco/issues/332) ### BUG FIXES -Probably a lot of bug fixes that may not make it here, but here are the ones we know about. - - * Fix - Cannot upgrade from prerelease to same version released - see [Chocolatey #122](https://github.com/chocolatey/chocolatey/issues/122) - * Fix - install `--force` should not use cache - see [Chocolatey #199](https://github.com/chocolatey/chocolatey/issues/199) - * Fix - force dependencies as well - see [--force-dependencies](https://github.com/chocolatey/choco/wiki/CommandsInstall) and [Chocolatey #199](https://github.com/chocolatey/chocolatey/issues/199) - * Fix - Chocolatey should not stop on error - see [Chocolatey #192](https://github.com/chocolatey/chocolatey/issues/192) - * Fix - Upgrading does not remove previous version - see [Chocolatey #259](https://github.com/chocolatey/chocolatey/issues/259) - * Fix - Non-elevated shell message spills errors - see [Chocolatey #540](https://github.com/chocolatey/chocolatey/issues/540) - * Fix - Package names are case sensitive for some sources - see [Chocolatey #589](https://github.com/chocolatey/chocolatey/issues/589) - * Fix - Install-ChocolateyVsixPackage doesn't check for correct VS 2012 path - see [Chocolatey #601](https://github.com/chocolatey/chocolatey/issues/601) - * Fix - Chocolatey behaves strangely after ctrl+c - see [Chocolatey #608](https://github.com/chocolatey/chocolatey/issues/608) - * Fix - Uninstall doesn't respect version setting - see [Chocolatey #612](https://github.com/chocolatey/chocolatey/issues/612) - * Fix - No update after download error - see [Chocolatey #637](https://github.com/chocolatey/chocolatey/issues/637) - * Fix - cup ends silently on error - see [Chocolatey #312](https://github.com/chocolatey/chocolatey/issues/312) - * Fix - cpack silently fails when dependency .NET 4.0+ is not met - see [Chocolatey #270](https://github.com/chocolatey/chocolatey/issues/270) - * Fix - Regression in cver all in 0.9.8.27 - see [Chocolatey #530](https://github.com/chocolatey/chocolatey/issues/530) - * Fix - Certain installs and updates fail with a "process with an Id of xxxx is not running" error - see [Chocolatey #603](https://github.com/chocolatey/chocolatey/issues/603) + * Fix - Merging assemblies on a machine running .net 4.5 or higher produces binaries incompatible with .net 4 - see [#392](https://github.com/chocolatey/choco/issues/392) + * Fix - API - Incorrect log4net version in chocolatey.lib dependencies - see [#390](https://github.com/chocolatey/choco/issues/390) ### IMPROVEMENTS - * [Security] Allow keeping `c:\chocolatey` install directory with environment variable - see [#17](https://github.com/chocolatey/choco/issues/17) - * [Security] Require switch on unofficial build - see [#36](https://github.com/chocolatey/choco/issues/36) - * Install script updates - see [#7](https://github.com/chocolatey/choco/issues/7) - * Ensure Chocolatey pkg is installed properly in lib folder - This means you can take a dependency on a minimum version of Chocolatey (we didn't like that before) - see [#19](https://github.com/chocolatey/choco/issues/19) - * Uninstall - allow abort - see [#43](https://github.com/chocolatey/choco/issues/43) - * Support for HTTPS basic authorization - see [Chocolatey #128](https://github.com/chocolatey/chocolatey/issues/128) - * Smooth out success/failure logging - see [Chocolatey #154](https://github.com/chocolatey/chocolatey/issues/154) - * Add $env:CHOCOLATEY_VERSION - see [Chocolatey #251](https://github.com/chocolatey/chocolatey/issues/251) - * Replace ascii cue with visual cues - see [Chocolatey #376](https://github.com/chocolatey/chocolatey/pull/376) - * Uninstall all versions of an app - see [Chocolatey #389](https://github.com/chocolatey/chocolatey/issues/389) - * Add parameters in packages.config files - see [Packages.config](https://github.com/chocolatey/choco/wiki/CommandsInstall#packagesconfig), [Chocolatey #472](https://github.com/chocolatey/chocolatey/issues/472), and [#10](https://github.com/chocolatey/choco/issues/10) - * Choco pack should support `-version` - see [Chocolatey #526](https://github.com/chocolatey/chocolatey/issues/526) - * Enhancements to Start-ChocolateyProcessAsAdmin - see [Chocolatey #564](https://github.com/chocolatey/chocolatey/pull/564) - * Install-ChocolateyFileAssociation - add label to new file types - see [Chocolatey #564](https://github.com/chocolatey/chocolatey/pull/564) - * Clean up the verobsity of Chocolatey - see [Chocolatey #374](https://github.com/chocolatey/chocolatey/issues/374) - * Compact choco upgrade --noop option - see [Chocolatey #414](https://github.com/chocolatey/chocolatey/issues/414) - * Remove references to the Chocolatey gods - see [Chocolatey #669](https://github.com/chocolatey/chocolatey/issues/669) - * Shims now have noop (`--shimgen-noop`) and help (`--shimgen-help`) switches - see [ShimGen #8](https://github.com/chocolatey/shimgen/issues/8) and [ShimGen #10](https://github.com/chocolatey/shimgen/issues/10) - * Shims will terminate underlying process on termination signal - see [ShimGen #11](https://github.com/chocolatey/shimgen/issues/11) - * Shims now have gui (`--shimgen-gui`) and exit (`--shimgen-exit`) switches - see [ShimGen #13](https://github.com/chocolatey/shimgen/issues/13) and [ShimGen #12](https://github.com/chocolatey/shimgen/issues/12) - * Dat help menu tho. I mean srsly guise - see [Chocolatey #641](https://github.com/chocolatey/chocolatey/issues/641) + * AutoUninstaller is on by default - see [#308](https://github.com/chocolatey/choco/issues/308) + * Show human-readable file sizes when downloading - see [#363](https://github.com/chocolatey/choco/issues/363) + * API - Add the ability to retrieve package count for a Source - see [#431](https://github.com/chocolatey/choco/issues/431) + * API - Chocolatey Lib still marks vital package information as internal - see [#433](https://github.com/chocolatey/choco/issues/433) + * API - Add paging to list command - see [#427](https://github.com/chocolatey/choco/issues/427) https://github.com/chocolatey/choco From 2fbecf671dd6a2bfc54ee2c6a97bb152649d340c Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Wed, 7 Oct 2015 12:04:43 -0500 Subject: [PATCH 61/61] (version) 0.9.10-beta1 --- .uppercut | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.uppercut b/.uppercut index a9da054bf3..d2fbe2f473 100644 --- a/.uppercut +++ b/.uppercut @@ -19,7 +19,7 @@ - +