diff --git a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs index b6936d68b5..5603cb241b 100644 --- a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs @@ -233,7 +233,7 @@ public override void Because() [Fact] public void should_call_service_list_run() { - packageService.Verify(c => c.list_run(configuration, true), Times.Once); + packageService.Verify(c => c.list_run(configuration), Times.Once); } } } diff --git a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPinCommandSpecs.cs b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPinCommandSpecs.cs index 95ae4330cd..e9248924b2 100644 --- a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPinCommandSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPinCommandSpecs.cs @@ -342,7 +342,7 @@ public override void Context() new PackageResult(package.Object, null), new PackageResult(pinnedPackage.Object, null) }; - nugetService.Setup(n => n.list_run(It.IsAny(), true)).Returns(packageResults); + nugetService.Setup(n => n.list_run(It.IsAny())).Returns(packageResults); configuration.PinCommand.Command = PinCommandType.list; } @@ -414,7 +414,7 @@ public void should_call_nuget_service_list_run_when_command_is_list() configuration.PinCommand.Command = PinCommandType.list; command.run(configuration); - nugetService.Verify(n => n.list_run(It.IsAny(), true), Times.Once); + nugetService.Verify(n => n.list_run(It.IsAny()), Times.Once); } [Pending("NuGet is killing me with extension methods. Need to find proper item to mock out to return the package object.")] diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 7ffb13a536..8eee6d9aeb 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -170,6 +170,7 @@ + diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs index 7d12c3cde1..0d805f1b56 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs @@ -16,17 +16,19 @@ namespace chocolatey.infrastructure.app.commands { using System.Collections.Generic; + using System.Linq; using attributes; using commandline; using configuration; using domain; using infrastructure.commands; using logging; + using results; using services; [CommandFor(CommandNameType.list)] [CommandFor(CommandNameType.search)] - public sealed class ChocolateyListCommand : ICommand + public sealed class ChocolateyListCommand : IListCommand { private readonly IChocolateyPackageService _packageService; @@ -66,12 +68,6 @@ public void configure_argument_parser(OptionSet optionSet, ChocolateyConfigurati public void handle_additional_argument_parsing(IList unparsedArguments, ChocolateyConfiguration configuration) { configuration.Input = string.Join(" ", unparsedArguments); - - if (configuration.ListCommand.LocalOnly) - { - configuration.Sources = ApplicationParameters.PackagesLocation; - configuration.Prerelease = true; - } } public void handle_validation(ChocolateyConfiguration configuration) @@ -114,7 +110,15 @@ public void noop(ChocolateyConfiguration configuration) public void run(ChocolateyConfiguration configuration) { - _packageService.list_run(configuration, logResults: true); + // you must leave the .ToList() here or else the method won't be evaluated! + _packageService.list_run(configuration).ToList(); + } + + public IEnumerable list(ChocolateyConfiguration configuration) + { + configuration.QuietOutput = true; + // here it's up to the caller to enumerate the results + return _packageService.list_run(configuration); } public bool may_require_admin_access() diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs index 11ab93045f..e82b88e58c 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs @@ -139,7 +139,7 @@ public void run(ChocolateyConfiguration configuration) public void list_pins(IPackageManager packageManager, ChocolateyConfiguration config) { - foreach (var pkg in _nugetService.list_run(config, logResults: true)) + foreach (var pkg in _nugetService.list_run(config)) { var pkgInfo = _packageInfoService.get_package_information(pkg.Package); if (pkgInfo != null && pkgInfo.IsPinned) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyVersionCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyVersionCommand.cs index 02f285fccc..a3fd4de673 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyVersionCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyVersionCommand.cs @@ -115,7 +115,7 @@ public override void run(ChocolateyConfiguration configuration) { if (configuration.ListCommand.LocalOnly) { - _packageService.list_run(configuration,logResults:true); + _packageService.list_run(configuration); } else { diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index 7b636e80bf..2c15cfe117 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -151,7 +151,22 @@ private void append_output(StringBuilder propertyValues, string append) public bool Force { get; set; } public bool Noop { get; set; } public bool HelpRequested { get; set; } + + // TODO: Should look into using mutually exclusive output levels - Debug, Info (Regular), Error (Quiet) + // Verbose and Important are not part of the levels at all + /// + /// Gets or sets a value indicating whether output should be limited. + /// This supports the --limit-output parameter. + /// + /// true for regular output; false for limited output. public bool RegularOutput { get; set; } + /// + /// Gets or sets a value indicating whether console logging should be supressed. + /// This is for use by API calls which surface results in alternate forms. + /// + /// true for no output; false for regular or limited output. + /// This has only been implemented for NuGet List + public bool QuietOutput { get; set; } public bool PromptForConfirmation { get; set; } public bool AcceptLicense { get; set; } public bool AllowUnofficialBuild { get; set; } diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index 1c781faac0..a457a11428 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -22,15 +22,16 @@ namespace chocolatey.infrastructure.app.services using commandline; using configuration; using domain; - using filesystem; using infrastructure.commands; using infrastructure.services; using logging; + using NuGet; using platforms; using results; using tolerance; + using IFileSystem = filesystem.IFileSystem; - public class ChocolateyPackageService : IChocolateyPackageService + public class ChocolateyPackageService : IChocolateyPackageService { private readonly INugetService _nugetService; private readonly IPowershellService _powershellService; @@ -73,7 +74,7 @@ public void list_noop(ChocolateyConfiguration config) } } - public void list_run(ChocolateyConfiguration config, bool logResults) + public IEnumerable list_run(ChocolateyConfiguration config) { this.Log().Debug(() => "Searching for package information"); @@ -83,51 +84,68 @@ public void list_run(ChocolateyConfiguration config, bool logResults) //install webpi if not installed //run the webpi command this.Log().Warn("Command not yet functional, stay tuned..."); + yield break; } else { - var list = _nugetService.list_run(config, logResults: true); - if (config.RegularOutput) + var packages = new List(); + + if (config.ListCommand.LocalOnly) + { + config.Sources = ApplicationParameters.PackagesLocation; + config.Prerelease = true; + } + foreach (var package in _nugetService.list_run(config)) { - this.Log().Warn(() => @"{0} packages {1}.".format_with(list.Count(), config.ListCommand.LocalOnly ? "installed" : "found")); + if (!config.ListCommand.LocalOnly || !config.ListCommand.IncludeRegistryPrograms) + { + yield return package; + } - if (config.ListCommand.LocalOnly && config.ListCommand.IncludeRegistryPrograms) + if (config.ListCommand.LocalOnly && config.ListCommand.IncludeRegistryPrograms && package.Package != null) { - report_registry_programs(config, list); + packages.Add(package.Package); } } - } - } - private void report_registry_programs(ChocolateyConfiguration config, IEnumerable list) - { - var itemsToRemoveFromMachine = new List(); - foreach (var packageResult in list) - { - if (packageResult != null && packageResult.Package != null) + if (config.ListCommand.LocalOnly && config.ListCommand.IncludeRegistryPrograms) { - var pkginfo = _packageInfoService.get_package_information(packageResult.Package); - if (pkginfo.RegistrySnapshot == null) + foreach (var installed in report_registry_programs(config, packages)) { - continue; - } - var key = pkginfo.RegistrySnapshot.RegistryKeys.FirstOrDefault(); - if (key != null) - { - itemsToRemoveFromMachine.Add(key.DisplayName); + yield return installed; } } } - var machineInstalled = _registryService.get_installer_keys().RegistryKeys.Where((p) => p.is_in_programs_and_features() && !itemsToRemoveFromMachine.Contains(p.DisplayName)).OrderBy((p) => p.DisplayName).Distinct().ToList(); - if (machineInstalled.Count != 0) + } + + private IEnumerable report_registry_programs(ChocolateyConfiguration config, IEnumerable list) + { + var itemsToRemoveFromMachine = list.Select(package => _packageInfoService.get_package_information(package)). + Where(p => p.RegistrySnapshot != null). + Select(p => p.RegistrySnapshot.RegistryKeys.FirstOrDefault()). + Where(p => p != null). + Select(p => p.DisplayName).ToList(); + + var count = 0; + var machineInstalled = _registryService.get_installer_keys().RegistryKeys. + Where((p) => p.is_in_programs_and_features() && !itemsToRemoveFromMachine.Contains(p.DisplayName)). + OrderBy((p) => p.DisplayName).Distinct(); + this.Log().Info(() => ""); + foreach (var key in machineInstalled) { - this.Log().Info(() => ""); - foreach (var key in machineInstalled.or_empty_list_if_null()) - { - this.Log().Info("{0}|{1}".format_with(key.DisplayName, key.DisplayVersion)); - if (config.Verbose) this.Log().Info(" InstallLocation: {0}{1} Uninstall:{2}".format_with(key.InstallLocation.escape_curly_braces(), Environment.NewLine, key.UninstallString.escape_curly_braces())); - } - this.Log().Warn(() => @"{0} applications not managed with Chocolatey.".format_with(machineInstalled.Count)); + if (config.RegularOutput) + { + this.Log().Info("{0}|{1}".format_with(key.DisplayName, key.DisplayVersion)); + if (config.Verbose) this.Log().Info(" InstallLocation: {0}{1} Uninstall:{2}".format_with(key.InstallLocation.escape_curly_braces(), Environment.NewLine, key.UninstallString.escape_curly_braces())); + } + count++; + + yield return new PackageResult(key.DisplayName, key.DisplayName, key.InstallLocation); + } + + if (config.RegularOutput) + { + this.Log().Warn(() => @"{0} applications not managed with Chocolatey.".format_with(count)); } } @@ -476,7 +494,7 @@ public void uninstall_noop(ChocolateyConfiguration config) this.Log().Info(@"Uninstalling the following packages:"); this.Log().Info(ChocolateyLoggers.Important, @"{0}".format_with(config.PackageNames)); - foreach (var packageConfigFile in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries).or_empty_list_if_null().Where(p => p.EndsWith(".config")).ToList()) + if (config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries).or_empty_list_if_null().Any(p => p.EndsWith(".config"))) { throw new ApplicationException("A packages.config file is only used with installs."); } @@ -708,4 +726,4 @@ private void remove_rollback_if_exists(PackageResult packageResult) _nugetService.remove_rollback_directory_if_exists(packageResult.Name); } } -} \ No newline at end of file +} diff --git a/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs index 45b0e2f206..21aaf2fac5 100644 --- a/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/IChocolateyPackageService.cs @@ -16,6 +16,7 @@ namespace chocolatey.infrastructure.app.services { using System.Collections.Concurrent; + using System.Collections.Generic; using configuration; using results; @@ -34,9 +35,8 @@ public interface IChocolateyPackageService /// Lists/searches for packages that meet a search criteria /// /// The configuration. - /// Should results be logged? /// - void list_run(ChocolateyConfiguration config, bool logResults); + IEnumerable list_run(ChocolateyConfiguration config); /// /// Run pack in noop mode diff --git a/src/chocolatey/infrastructure.app/services/INugetService.cs b/src/chocolatey/infrastructure.app/services/INugetService.cs index 29371dbc57..ee7b77b125 100644 --- a/src/chocolatey/infrastructure.app/services/INugetService.cs +++ b/src/chocolatey/infrastructure.app/services/INugetService.cs @@ -35,7 +35,7 @@ public interface INugetService /// The configuration. /// Should results be logged? /// - IEnumerable list_run(ChocolateyConfiguration config, bool logResults); + IEnumerable list_run(ChocolateyConfiguration config); /// /// Run pack in noop mode. diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index d581eacf13..36c4b406d9 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -74,32 +74,33 @@ public void list_noop(ChocolateyConfiguration config) )); } - public IEnumerable list_run(ChocolateyConfiguration config, bool logResults) + public IEnumerable list_run(ChocolateyConfiguration config) { + int count = 0; + if (config.RegularOutput) this.Log().Debug(() => "Running list with the following filter = '{0}'".format_with(config.Input)); if (config.RegularOutput) this.Log().Debug(() => "--- Start of List ---"); - foreach (var package in NugetList.GetPackages(config, _nugetLogger)) + foreach (var pkg in NugetList.GetPackages(config, _nugetLogger)) + { - var pkg = package; // for lamda access - if (logResults) + var package = pkg; // for lamda access + if (!config.QuietOutput) { - if (config.RegularOutput) - { - this.Log().Info(config.Verbose ? ChocolateyLoggers.Important : ChocolateyLoggers.Normal, () => "{0} {1}".format_with(pkg.Id, pkg.Version.to_string())); - if (config.Verbose) this.Log().Info(() => " {0}{1} Description: {2}{1} Tags: {3}{1} Number of Downloads: {4}{1}".format_with(pkg.Title.escape_curly_braces(), Environment.NewLine, pkg.Description.escape_curly_braces(), pkg.Tags.escape_curly_braces(), pkg.DownloadCount <= 0 ? "n/a" : pkg.DownloadCount.to_string())); - // Maintainer(s):{3}{1} | pkg.Owners.join(", ") - null at the moment - } - else - { - this.Log().Info(config.Verbose ? ChocolateyLoggers.Important : ChocolateyLoggers.Normal, () => "{0}|{1}".format_with(pkg.Id, pkg.Version.to_string())); - } + this.Log().Info(config.Verbose ? ChocolateyLoggers.Important : ChocolateyLoggers.Normal, () => "{0} {1}".format_with(package.Id, package.Version.to_string())); + if (config.RegularOutput && config.Verbose) this.Log().Info(() => " {0}{1} Description: {2}{1} Tags: {3}{1} Number of Downloads: {4}{1}".format_with(package.Title.escape_curly_braces(), Environment.NewLine, package.Description.escape_curly_braces(), package.Tags.escape_curly_braces(), package.DownloadCount <= 0 ? "n/a" : package.DownloadCount.to_string())); } else { - this.Log().Debug(() => "{0} {1}".format_with(pkg.Id, pkg.Version.to_string())); + this.Log().Debug(() => "{0} {1}".format_with(package.Id, package.Version.to_string())); } + count++; - yield return new PackageResult(pkg, null); + yield return new PackageResult(package, null, config.Sources); + } + + if (config.RegularOutput) + { + this.Log().Warn(() => @"{0} packages {1}.".format_with(count, config.ListCommand.LocalOnly ? "installed" : "found")); } if (config.RegularOutput) this.Log().Debug(() => "--- End of List ---"); } @@ -1027,8 +1028,12 @@ private void set_package_names_if_all_is_specified(ChocolateyConfiguration confi config.PackageNames = string.Empty; var input = config.Input; config.Input = string.Empty; + var quiet = config.QuietOutput; + config.QuietOutput = true; + + config.PackageNames = list_run(config).Select(p => p.Name).@join(ApplicationParameters.PackageNamesSeparator); - config.PackageNames = list_run(config, false).Select(p => p.Name).@join(ApplicationParameters.PackageNamesSeparator); + config.QuietOutput = quiet; config.Input = input; config.Noop = noop; config.Prerelease = pre; diff --git a/src/chocolatey/infrastructure/commands/IListCommand.cs b/src/chocolatey/infrastructure/commands/IListCommand.cs new file mode 100644 index 0000000000..b61daf5504 --- /dev/null +++ b/src/chocolatey/infrastructure/commands/IListCommand.cs @@ -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. + +namespace chocolatey.infrastructure.commands +{ + using System.Collections.Generic; + using app.configuration; + + public interface IListCommand : ICommand + { + IEnumerable list(ChocolateyConfiguration configuration); + } +} \ No newline at end of file diff --git a/src/chocolatey/infrastructure/results/PackageResult.cs b/src/chocolatey/infrastructure/results/PackageResult.cs index 29aaed8f5d..aadd6322b7 100644 --- a/src/chocolatey/infrastructure/results/PackageResult.cs +++ b/src/chocolatey/infrastructure/results/PackageResult.cs @@ -15,7 +15,9 @@ namespace chocolatey.infrastructure.results { - using System.Linq; + using System; + using System.Collections.Generic; + using System.Linq; using NuGet; /// @@ -37,17 +39,41 @@ public bool Warning public string Version { get; private set; } public IPackage Package { get; private set; } public string InstallLocation { get; set; } + public string Source { get; set; } + public string SourceUri { get; set; } - public PackageResult(IPackage package, string installLocation) : this(package.Id.to_lower(), package.Version.to_string(), installLocation) + public PackageResult(IPackage package, string installLocation, string source = null) : this(package.Id.to_lower(), package.Version.to_string(), installLocation) { Package = package; + Source = source; + var sources = new List(); + if (!string.IsNullOrEmpty(source)) + { + sources.AddRange(source.Split(new[] {";", ","}, StringSplitOptions.RemoveEmptyEntries).Select(s => new Uri(s))); + } + + var rp = Package as DataServicePackage; + if (rp != null) + { + SourceUri = rp.DownloadUrl.ToString(); + Source = sources.FirstOrDefault(uri => uri.IsBaseOf(rp.DownloadUrl)).to_string(); + if (string.IsNullOrEmpty(Source)) + { + Source = sources.FirstOrDefault(uri => uri.DnsSafeHost == rp.DownloadUrl.DnsSafeHost).to_string(); + } + } + else + { + Source = sources.FirstOrDefault(uri => uri.IsFile || uri.IsUnc).to_string(); + } } - public PackageResult(string name, string version, string installLocation) + public PackageResult(string name, string version, string installLocation, string source = null) { Name = name; Version = version; InstallLocation = installLocation; + Source = source; } } } \ No newline at end of file