Skip to content

Commit 4860c8e

Browse files
corbobgep13
authored andcommitted
(chocolatey#2591) Add headers to limit output commands
When a user asks for limited output from Chocolatey, it is not uncommon to pipe that output to `ConvertFrom-String` or `ConvertFrom-Csv` and manually add headers to get back an object. This allows for getting a header row back so that the end user doesn't need to add their own headers and discern what they are. This also adds a StringResources static class that allows us to store constant strings in and use them across the code to reduce duplication.
1 parent 9c86306 commit 4860c8e

17 files changed

+227
-6
lines changed

src/chocolatey/StringResources.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,15 @@ public static class EnvironmentVariables
5656
[Browsable(false)]
5757
internal const string PackageNuspecVersion = "packageNuspecVersion";
5858
}
59+
60+
public static class OptionDescriptions
61+
{
62+
public const string DISPLAY_HEADERS = "Display headers - Display headers when limit-output is used. Requires 2.3.0";
63+
}
64+
65+
public static class Options
66+
{
67+
public const string DISPLAY_HEADERS = "headers"; // TODO: This option name needs to be decided and agreed upon.
68+
}
5969
}
60-
}
70+
}

src/chocolatey/infrastructure.app/ApplicationParameters.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ public static class Features
243243
public static readonly string UsePackageRepositoryOptimizations = "usePackageRepositoryOptimizations";
244244
public static readonly string DisableCompatibilityChecks = "disableCompatibilityChecks";
245245
public static readonly string UsePackageHashValidation = "usePackageHashValidation";
246+
public static readonly string AlwaysDisplayHeaders = "alwaysDisplayHeaders";
246247
}
247248

248249
public static class Messages

src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,8 @@ private static void SetAllFeatureFlags(ChocolateyConfiguration config, ConfigFil
340340
config.Features.UsePackageRepositoryOptimizations = SetFeatureFlag(ApplicationParameters.Features.UsePackageRepositoryOptimizations, configFileSettings, defaultEnabled: true, description: "Use Package Repository Optimizations - Turn on optimizations for reducing bandwidth with repository queries during package install/upgrade/outdated operations. Should generally be left enabled, unless a repository needs to support older methods of query. When disabled, this makes queries similar to the way they were done in earlier versions of Chocolatey.");
341341
config.Features.UsePackageHashValidation = SetFeatureFlag(ApplicationParameters.Features.UsePackageHashValidation, configFileSettings, defaultEnabled: false, description: "Use Package Hash Validation - Check the hash of the downloaded package file against the source provided hash. Only supports sources that provide SHA512 hashes. Disabled by default. Available in 2.3.0+");
342342
config.PromptForConfirmation = !SetFeatureFlag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false, description: "Prompt for confirmation in scripts or bypass.");
343-
config.DisableCompatibilityChecks = SetFeatureFlag(ApplicationParameters.Features.DisableCompatibilityChecks, configFileSettings, defaultEnabled: false, description: "Disable Compatibility Checks - Disable showing a warning when there is an incompatibility between Chocolatey CLI and Chocolatey Licensed Extension.");
343+
config.DisableCompatibilityChecks = SetFeatureFlag(ApplicationParameters.Features.DisableCompatibilityChecks, configFileSettings, defaultEnabled: false, description: "Disable Compatibility Checks - Disable showing a warning when there is an incompatibility between Chocolatey CLI and Chocolatey Licensed Extension. Available in 1.1.0+");
344+
config.DisplayHeaders = SetFeatureFlag(ApplicationParameters.Features.AlwaysDisplayHeaders, configFileSettings, defaultEnabled: false, description: StringResources.OptionDescriptions.DISPLAY_HEADERS);
344345
}
345346

346347
private static bool SetFeatureFlag(string featureName, ConfigFileSettings configFileSettings, bool defaultEnabled, string description)

src/chocolatey/infrastructure.app/commands/ChocolateyApiKeyCommand.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi
5050
.Add("k=|key=|apikey=|api-key=",
5151
"ApiKey - The API key for the source. This is the authentication that identifies you and allows you to push to a source. With some sources this is either a key or it could be a user name and password specified as 'user:password'.",
5252
option => configuration.ApiKeyCommand.Key = option.UnquoteSafe())
53+
.Add(StringResources.Options.DISPLAY_HEADERS,
54+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
55+
option => configuration.DisplayHeaders = true)
5356
;
5457
}
5558

@@ -197,6 +200,11 @@ public virtual void Run(ChocolateyConfiguration configuration)
197200
_configSettingsService.SetApiKey(configuration);
198201
break;
199202
default:
203+
if (!configuration.RegularOutput && configuration.DisplayHeaders)
204+
{
205+
this.Log().Info("Source|Key");
206+
}
207+
200208
_configSettingsService.GetApiKey(configuration, (key) =>
201209
{
202210
var authenticatedString = string.IsNullOrWhiteSpace(key.Key) ? string.Empty : "(Authenticated)";

src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi
5151
"value=",
5252
"Value - the value of the config setting. Required with some actions. Defaults to empty.",
5353
option => configuration.ConfigCommand.ConfigValue = option.UnquoteSafe())
54+
.Add(
55+
StringResources.Options.DISPLAY_HEADERS,
56+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
57+
option => configuration.DisplayHeaders = true)
5458
;
5559
}
5660

src/chocolatey/infrastructure.app/commands/ChocolateyFeatureCommand.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi
4747
.Add("n=|name=",
4848
"Name - the name of the source. Required with actions other than list. Defaults to empty.",
4949
option => configuration.FeatureCommand.Name = option.UnquoteSafe())
50+
.Add(StringResources.Options.DISPLAY_HEADERS,
51+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
52+
option => configuration.DisplayHeaders = true)
5053
;
5154
}
5255

src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ public override void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConf
7777
.Add("include-configured-sources",
7878
"Include Configured Sources - When using the '--source' option, this appends the sources that have been saved into the chocolatey.config file by 'source' command. Available in 2.3.0+",
7979
option => configuration.IncludeConfiguredSources = option != null)
80+
.Add(StringResources.Options.DISPLAY_HEADERS,
81+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
82+
option => configuration.DisplayHeaders = true)
8083
;
8184
}
8285

src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi
137137
option => configuration.ListCommand.IdStartsWith = option != null)
138138
.Add("detail|detailed",
139139
"Detailed - Alias for verbose.",
140-
option => configuration.Verbose = option != null);
140+
option => configuration.Verbose = option != null)
141+
.Add(StringResources.Options.DISPLAY_HEADERS,
142+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
143+
option => configuration.DisplayHeaders = true);
141144
}
142145

143146
public virtual int Count(ChocolateyConfiguration config)

src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi
7575
.Add("include-configured-sources",
7676
"Include Configured Sources - When using the '--source' option, this appends the sources that have been saved into the chocolatey.config file by 'source' command. Available in 2.3.0+",
7777
option => configuration.IncludeConfiguredSources = option != null)
78+
.Add(StringResources.Options.DISPLAY_HEADERS,
79+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
80+
option => configuration.DisplayHeaders = true)
7881
;
7982
}
8083

src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi
5656
.Add("version=",
5757
"Version - Used when multiple versions of a package are installed. Defaults to empty.",
5858
option => configuration.Version = option.UnquoteSafe())
59+
.Add(StringResources.Options.DISPLAY_HEADERS,
60+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
61+
option => configuration.DisplayHeaders = true)
5962
;
6063
}
6164

@@ -178,6 +181,11 @@ public virtual void ListPins(ChocolateyConfiguration config)
178181
config.QuietOutput = quiet;
179182
config.Input = input;
180183

184+
if (!config.RegularOutput && config.DisplayHeaders)
185+
{
186+
this.Log().Info("PackageId|Version");
187+
}
188+
181189
foreach (var pkg in packages.OrEmpty())
182190
{
183191
var pkgInfo = _packageInfoService.Get(pkg.PackageMetadata);

src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ public virtual void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfi
7575
.Add("adminonly|admin-only",
7676
"Visible to Administrators Only - Should this source be visible to non-administrators? Requires business edition. Defaults to false.",
7777
option => configuration.SourceCommand.VisibleToAdminsOnly = option != null)
78+
.Add(StringResources.Options.DISPLAY_HEADERS,
79+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
80+
option => configuration.DisplayHeaders = true)
7881
;
7982
}
8083

src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ public void ConfigureArgumentParser(OptionSet optionSet, ChocolateyConfiguration
4545
optionSet
4646
.Add("n=|name=",
4747
"The name of the template to get information about.",
48-
option => configuration.TemplateCommand.Name = option.UnquoteSafe().ToLower());
48+
option => configuration.TemplateCommand.Name = option.UnquoteSafe().ToLower())
49+
.Add(StringResources.Options.DISPLAY_HEADERS,
50+
StringResources.OptionDescriptions.DISPLAY_HEADERS,
51+
option => configuration.DisplayHeaders = true);
4952
// todo: #2570 Allow for templates from an external path? Requires #1477
5053
}
5154

src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public ChocolateyConfiguration()
5959
TemplateCommand = new TemplateCommandConfiguration();
6060
CacheCommand = new CacheCommandConfiguration();
6161
RuleCommand = new RuleCommandConfiguration();
62+
DisplayHeaders = false;
63+
6264
#if DEBUG
6365
AllowUnofficialBuild = true;
6466
#endif
@@ -360,6 +362,7 @@ private void AppendOutput(StringBuilder propertyValues, string append)
360362
public string DownloadChecksumType { get; set; }
361363
public string DownloadChecksumType64 { get; set; }
362364
public bool PinPackage { get; set; }
365+
public bool DisplayHeaders { get; set; }
363366

364367
/// <summary>
365368
/// Configuration values provided by choco.

src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ public virtual bool SkipSource(ConfigFileSourceSetting source, ChocolateyConfigu
6262

6363
public virtual IEnumerable<ChocolateySource> ListSources(ChocolateyConfiguration configuration)
6464
{
65+
if (!configuration.RegularOutput && configuration.DisplayHeaders)
66+
{
67+
this.Log().Info("SourceId|Location|Disabled|UserName|Certificate|Priority|BypassProxy|AllowSelfService|AdminOnly");
68+
}
69+
6570
var list = new List<ChocolateySource>();
6671
foreach (var source in ConfigFileSettings.Sources.OrEmpty().OrderBy(s => s.Id))
6772
{
@@ -268,6 +273,11 @@ public void EnableSource(ChocolateyConfiguration configuration)
268273

269274
public void ListFeatures(ChocolateyConfiguration configuration)
270275
{
276+
if (!configuration.RegularOutput && configuration.DisplayHeaders)
277+
{
278+
this.Log().Info("FeatureName|Enabled|Description");
279+
}
280+
271281
foreach (var feature in ConfigFileSettings.Features.OrEmpty().OrderBy(f => f.Name))
272282
{
273283
if (configuration.RegularOutput)
@@ -455,6 +465,11 @@ public void RemoveApiKey(ChocolateyConfiguration configuration)
455465

456466
public void ListConfig(ChocolateyConfiguration configuration)
457467
{
468+
if (!configuration.RegularOutput && configuration.DisplayHeaders)
469+
{
470+
this.Log().Info("Name|Value|Description");
471+
}
472+
458473
foreach (var config in ConfigFileSettings.ConfigSettings.OrEmpty().OrderBy(c => c.Key))
459474
{
460475
if (configuration.RegularOutput)

src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,16 @@ public virtual IEnumerable<PackageResult> List(ChocolateyConfiguration config)
257257

258258
if (config.RegularOutput)
259259
{
260+
// This doesn't make sense as a Debug message to me... Debug messages don't really show up when you're running normally...
260261
this.Log().Debug(() => "Searching for package information");
261262
}
263+
else
264+
{
265+
if (config.DisplayHeaders)
266+
{
267+
this.Log().Info("PackageID|Version");
268+
}
269+
}
262270

263271
var packages = new List<PackageResult>();
264272

@@ -876,6 +884,13 @@ public virtual void Outdated(ChocolateyConfiguration config)
876884
Output is package name | current version | available version | pinned?
877885
");
878886
}
887+
else
888+
{
889+
if (config.DisplayHeaders)
890+
{
891+
this.Log().Info("PackageName|CurrentVersion|AvailableVersion|Pinned");
892+
}
893+
}
879894

880895
config.PackageNames = ApplicationParameters.AllPackages;
881896
config.UpgradeCommand.NotifyOnlyAvailableUpgrades = true;

src/chocolatey/infrastructure.app/services/TemplateService.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,11 @@ public void List(ChocolateyConfiguration configuration)
251251

252252
if (string.IsNullOrWhiteSpace(configuration.TemplateCommand.Name))
253253
{
254+
if (!configuration.RegularOutput && configuration.DisplayHeaders)
255+
{
256+
this.Log().Info("TemplateName|Version");
257+
}
258+
254259
if (templateDirList.Any())
255260
{
256261
foreach (var templateDir in templateDirList)
@@ -259,11 +264,11 @@ public void List(ChocolateyConfiguration configuration)
259264
ListCustomTemplateInformation(configuration);
260265
}
261266

262-
this.Log().Info(configuration.RegularOutput ? "{0} Custom templates found at {1}{2}".FormatWith(templateDirList.Count(), ApplicationParameters.TemplatesLocation, Environment.NewLine) : string.Empty);
267+
this.Log().Info(configuration.RegularOutput ? ChocolateyLoggers.Normal : ChocolateyLoggers.LogFileOnly, "{0} Custom templates found at {1}{2}".FormatWith(templateDirList.Count(), ApplicationParameters.TemplatesLocation, Environment.NewLine));
263268
}
264269
else
265270
{
266-
this.Log().Info(configuration.RegularOutput ? "No custom templates installed in {0}{1}".FormatWith(ApplicationParameters.TemplatesLocation, Environment.NewLine) : string.Empty);
271+
this.Log().Info(configuration.RegularOutput ? ChocolateyLoggers.Normal : ChocolateyLoggers.LogFileOnly, "No custom templates installed in {0}{1}".FormatWith(ApplicationParameters.TemplatesLocation, Environment.NewLine));
267272
}
268273

269274
ListBuiltinTemplateInformation(configuration, isBuiltInTemplateOverridden, isBuiltInOrDefaultTemplateDefault);
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
Import-Module helpers/common-helpers
2+
3+
Describe "choco headers tests" -Tag Chocolatey, HeadersFeature {
4+
BeforeDiscovery {
5+
$Commands = @(
6+
@{
7+
Command = 'list'
8+
CommandLine = '--local-only'
9+
ExpectedHeaders = 'PackageID|Version'
10+
}
11+
@{
12+
#
13+
Command = 'info'
14+
CommandLine = 'chocolatey --local-only'
15+
ExpectedHeaders = 'PackageID|Version'
16+
}
17+
@{
18+
# Needs to pin something...
19+
Command = 'pin'
20+
ExpectedHeaders = 'PackageID|Version'
21+
}
22+
@{
23+
Command = 'outdated'
24+
ExpectedHeaders = 'PackageName|CurrentVersion|AvailableVersion|Pinned'
25+
}
26+
@{
27+
Command = 'source'
28+
ExpectedHeaders = 'SourceId|Location|Disabled|UserName|Certificate|Priority|BypassProxy|AllowSelfService|AdminOnly'
29+
}
30+
@{
31+
Command = 'config'
32+
ExpectedHeaders = 'Name|Value|Description'
33+
}
34+
@{
35+
Command = 'feature'
36+
ExpectedHeaders = 'FeatureName|Enabled|Description'
37+
}
38+
@{
39+
Command = 'apikey'
40+
ExpectedHeaders = 'Source|Key'
41+
}
42+
@{
43+
Command = 'template'
44+
ExpectedHeaders = 'TemplateName|Version'
45+
}
46+
)
47+
}
48+
BeforeAll {
49+
Initialize-ChocolateyTestInstall
50+
51+
New-ChocolateyInstallSnapshot
52+
}
53+
54+
AfterAll {
55+
Remove-ChocolateyTestInstall
56+
}
57+
58+
Context "Outputs headers for <Command> when '--display-headers' provided configured" -ForEach $Commands {
59+
BeforeAll {
60+
$Output = Invoke-Choco $Command $CommandLine --limit-output --display-headers
61+
}
62+
63+
It 'Exits success (0)' {
64+
$Output.ExitCode | Should -Be 0 -Because $Output.String
65+
}
66+
67+
It "Displays appropriate header" {
68+
# Some commands won't output anything but the header. In that case we want just the lines instead of indexing into it.
69+
$ActualOutput = if ($Output.Lines.Count -gt 1) {
70+
$Output.Lines[0]
71+
}
72+
else {
73+
$Output.Lines
74+
}
75+
76+
$ActualOutput | Should -Be $ExpectedHeaders
77+
}
78+
79+
}
80+
81+
Context "Does not output headers for <Command> by default" -ForEach $Commands {
82+
BeforeAll {
83+
$Output = Invoke-Choco $Command $CommandLine --limit-output
84+
}
85+
86+
It 'Exits success (0)' {
87+
$Output.ExitCode | Should -Be 0 -Because $Output.String
88+
}
89+
90+
It "Does not display header" {
91+
# Some commands won't output anything but the header. In that case we want just the lines instead of indexing into it.
92+
$ActualOutput = if ($Output.Lines.Count -gt 1) {
93+
$Output.Lines[0]
94+
}
95+
else {
96+
$Output.Lines
97+
}
98+
99+
$ActualOutput | Should -Not -Be $ExpectedHeaders
100+
}
101+
102+
}
103+
104+
Context "Outputs headers when feature enabled" {
105+
BeforeAll {
106+
Restore-ChocolateyInstallSnapshot
107+
108+
Enable-ChocolateyFeature -Name AlwaysDisplayHeaders
109+
}
110+
111+
Context "Outputs headers for <Command>" -ForEach $Commands {
112+
BeforeAll {
113+
$Output = Invoke-Choco $Command $CommandLine --limit-output
114+
}
115+
116+
It 'Exits success (0)' {
117+
$Output.ExitCode | Should -Be 0 -Because $Output.String
118+
}
119+
120+
It "Displays appropriate header" {
121+
# Some commands won't output anything but the header. In that case we want just the lines instead of indexing into it.
122+
$ActualOutput = if ($Output.Lines.Count -gt 1) {
123+
$Output.Lines[0]
124+
}
125+
else {
126+
$Output.Lines
127+
}
128+
129+
$ActualOutput | Should -Be $ExpectedHeaders
130+
}
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)