diff --git a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs index 36a8096b96..99d2154d4b 100644 --- a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyListCommandSpecs.cs @@ -208,7 +208,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 21d5121208..e9248924b2 100644 --- a/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPinCommandSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/commands/ChocolateyPinCommandSpecs.cs @@ -337,10 +337,12 @@ public override void Context() configuration.Sources = ApplicationParameters.PackagesLocation; configuration.ListCommand.LocalOnly = true; configuration.AllVersions = true; - var packageResults = new ConcurrentDictionary(); - packageResults.GetOrAdd("regular", new PackageResult(package.Object, null)); - packageResults.GetOrAdd("pinned", new PackageResult(pinnedPackage.Object, null)); - nugetService.Setup(n => n.list_run(It.IsAny(), false)).Returns(packageResults); + var packageResults = new [] + { + new PackageResult(package.Object, null), + new PackageResult(pinnedPackage.Object, null) + }; + nugetService.Setup(n => n.list_run(It.IsAny())).Returns(packageResults); configuration.PinCommand.Command = PinCommandType.list; } @@ -412,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(), false), 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/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs index cd5b0af76b..338b3763b0 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; @@ -60,12 +62,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) @@ -107,7 +103,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.Quiet = true; + // here it's up to the caller to enumerate the results + return _packageService.list_run(configuration); } } } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs index 4c30d38998..57ea3ee3e8 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs @@ -139,10 +139,9 @@ public void run(ChocolateyConfiguration configuration) public void list_pins(IPackageManager packageManager, ChocolateyConfiguration config) { - var localPackages = _nugetService.list_run(config, logResults: false); - foreach (var pkg in localPackages.or_empty_list_if_null()) + foreach (var pkg in _nugetService.list_run(config)) { - var pkgInfo = _packageInfoService.get_package_information(pkg.Value.Package); + var pkgInfo = _packageInfoService.get_package_information(pkg.Package); if (pkgInfo != null && pkgInfo.IsPinned) { this.Log().Info(() => "{0}|{1}".format_with(pkgInfo.Package.Id,pkgInfo.Package.Version)); 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 f8a163b778..a5298e8f91 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -150,7 +150,19 @@ 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 just use output levels: Debug, Verbose, Info (Regular), Important, Error (Quiet) + /// + /// 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. + public bool Quiet { get; set; } public bool PromptForConfirmation { get; set; } public bool AcceptLicense { get; set; } public bool AllowUnofficialBuild { get; set; } diff --git a/src/chocolatey/infrastructure.app/nuget/NugetList.cs b/src/chocolatey/infrastructure.app/nuget/NugetList.cs index 957cc6e04a..fcce1986ce 100644 --- a/src/chocolatey/infrastructure.app/nuget/NugetList.cs +++ b/src/chocolatey/infrastructure.app/nuget/NugetList.cs @@ -22,7 +22,7 @@ namespace chocolatey.infrastructure.app.nuget // ReSharper disable InconsistentNaming - public sealed class NugetList + public static class NugetList { public static IEnumerable GetPackages(ChocolateyConfiguration configuration, ILogger nugetLogger) { @@ -31,7 +31,8 @@ public static IEnumerable GetPackages(ChocolateyConfiguration configur if (configuration.AllVersions) { - return results.Where(PackageExtensions.IsListed).OrderBy(p => p.Id).ToList(); + // just trust the server order, don't sort because that's a blocking operation + return results.Where(PackageExtensions.IsListed); } if (configuration.Prerelease && packageRepository.SupportsPrereleasePackages) @@ -43,11 +44,12 @@ public static IEnumerable GetPackages(ChocolateyConfiguration configur results = results.Where(p => p.IsLatestVersion); } - return results.OrderBy(p => p.Id) - .AsEnumerable() - .Where(PackageExtensions.IsListed) - .Where(p => configuration.Prerelease || p.IsReleaseVersion()) - .distinct_last(PackageEqualityComparer.Id, PackageComparer.Version).ToList(); + // just trust the server order, don't sort because that's a blocking operation + // also don't worry about multiple versions, considering Is*LatestVersion only applies to one + return results.Where(PackageExtensions.IsListed) + .Where(p => configuration.Prerelease || p.IsReleaseVersion()); + // .OrderBy(p => p.Id).ThenByDescending(p => p.Version) + // .GroupBy(p => p.Id).Select(g => g.First()); } } diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index 98739afff8..0632b4db2e 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -22,14 +22,15 @@ namespace chocolatey.infrastructure.app.services using commandline; using configuration; using domain; - using filesystem; 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; @@ -64,7 +65,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"); @@ -74,51 +75,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, ConcurrentDictionary list) - { - var itemsToRemoveFromMachine = new List(); - foreach (var packageResult in list) - { - if (packageResult.Value != null && packageResult.Value.Package != null) + if (config.ListCommand.LocalOnly && config.ListCommand.IncludeRegistryPrograms) { - var pkginfo = _packageInfoService.get_package_information(packageResult.Value.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)); } } @@ -415,7 +433,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi 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."); } @@ -635,4 +653,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 7e468977a5..8ddfde5395 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 63d8ac92bb..ee7b77b125 100644 --- a/src/chocolatey/infrastructure.app/services/INugetService.cs +++ b/src/chocolatey/infrastructure.app/services/INugetService.cs @@ -17,6 +17,7 @@ namespace chocolatey.infrastructure.app.services { using System; using System.Collections.Concurrent; + using System.Collections.Generic; using configuration; using results; @@ -34,7 +35,7 @@ public interface INugetService /// The configuration. /// Should results be logged? /// - ConcurrentDictionary 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 cf28d204b5..241f624b8b 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -71,36 +71,30 @@ public void list_noop(ChocolateyConfiguration config) )); } - public ConcurrentDictionary list_run(ChocolateyConfiguration config, bool logResults = true) + public IEnumerable list_run(ChocolateyConfiguration config) { - var packageResults = new ConcurrentDictionary(); - - var packages = NugetList.GetPackages(config, _nugetLogger).ToList(); - - foreach (var package in packages.or_empty_list_if_null()) + int count = 0; + foreach (var pkg in NugetList.GetPackages(config, _nugetLogger)) { - if (logResults) + var package = pkg; // for lamda access + if (!config.Quiet) { - if (config.RegularOutput) - { - this.Log().Info(config.Verbose ? ChocolateyLoggers.Important : ChocolateyLoggers.Normal, () => "{0} {1}".format_with(package.Id, package.Version.to_string())); - if (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())); - // Maintainer(s):{3}{1} | package.Owners.join(", ") - null at the moment - } - else - { - this.Log().Info(config.Verbose ? ChocolateyLoggers.Important : ChocolateyLoggers.Normal, () => "{0}|{1}".format_with(package.Id, package.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(package.Id, package.Version.to_string())); } + count++; - packageResults.GetOrAdd(package.Id, new PackageResult(package, null)); + yield return new PackageResult(package, null, config.Sources); } - return packageResults; + if (config.RegularOutput) + { + this.Log().Warn(() => @"{0} packages {1}.".format_with(count, config.ListCommand.LocalOnly ? "installed" : "found")); + } } public void pack_noop(ChocolateyConfiguration config) @@ -895,14 +889,16 @@ 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.Quiet; + config.Quiet = true; - var localPackages = list_run(config, logResults: false); + config.PackageNames = list_run(config).Select(p => p.Name).@join(ApplicationParameters.PackageNamesSeparator); + config.Quiet = quiet; config.Input = input; config.Noop = noop; config.Prerelease = pre; config.Sources = sources; - config.PackageNames = string.Join(ApplicationParameters.PackageNamesSeparator, localPackages.Select((p) => p.Key).or_empty_list_if_null()); if (customAction != null) customAction.Invoke(); } 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