command-line-api icon indicating copy to clipboard operation
command-line-api copied to clipboard

Provide flag on Option to hide default value if present

Open nathan-alden-sr opened this issue 3 years ago • 7 comments

I created an Option<string> for a --password option. I am defaulting the option to Environment.GetVariableName("MY_PASSWORD_ENV"). I don't want this environment variable to appear in plain text in the option description, but it currently does. I cannot find a way to stop this behavior.

Option<string> option = new("password");

option.SetDefaultValue(Environment.GetEnvironmentVariable(PasswordEnvironmentVariable));

AddOption(option);
Options:
  password        [default: password123] <-- Provide an option to hide this rendering

Perhaps something like Option.IsSecure?

Another suggestion, although more work, is to incorporate the process of defaulting to environment variables into the library itself. This is a common enough need that it might be worth it.

It might also be nice to support cascading default values. This would support cases where a value should come from an environment variable, if present, but then fall back to a constant value if all else fails. Think options like database port:

Option value from CLI -> environment variable -> 5432

nathan-alden-sr avatar Oct 31 '21 21:10 nathan-alden-sr

While i can't help with hiding the default value in the help output, the last part about cascading default values could be achieved by using one of the Option<T> constructor overloads featuring a Func<T> getDefaultValue parameter or the Option.SetDefaultValueFactory method, kinda like:

Option<string> option = new(
    "password",
    () => Environment.GetEnvironmentVariable("EnvVarName") ?? "abc"
);

or

Option<string> option = new("password");
option.SetDefaultValueFactory(() => Environment.GetEnvironmentVariable("EnvVarName") ?? "abc");

(I doubt it would be easy to generalize cascading default values. Some (incl. you) like to employ environment vars, others might like to use master config files instead of env vars, some others might prefer CLI -> env var -> config file -> hard-coded value, and some others might prefer some other combinations of whatever other default value providers...)


(P.S.: Off-topic side note: Suggesting the property name "IsSecure" for hiding the password value makes the impression to me (perhaps wrongly?) that you believe hiding the default password value in the help output is conducive to some sense of security. However, i would argue that anyone who is capable enough to use the command line -- which is obviously a requirement for using any form of CLI, can simply type set, printenv, or some such depending on the OS and shell in use, and still see the default password in all its plain-text glory ;-) )

elgonzo avatar Nov 01 '21 10:11 elgonzo

Good point on the naming. I honestly don't care what they name it, but I do need the feature. Since defaults are resolved before help is displayed, there doesn't seem to be a way to stop it from exposing my environment variables. Imagine I'm sharing something on Zoom in a meeting: I wouldn't want it exposing some sensitive credential to everyone.

nathan-alden-sr avatar Nov 01 '21 18:11 nathan-alden-sr

Another suggestion, although more work, is to incorporate the process of defaulting to environment variables into the library itself. This is a common enough need that it might be worth it.

This has been proposed and it's something we'd like to support. See #1191.

jonsequitur avatar Nov 05 '21 18:11 jonsequitur

We've been making a number of changes to make help easier to customize. One approach that can work here is the following. Caveats afterward.

using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Help;
using System.CommandLine.Parsing;

var passwordOption = new Option<string>("--password", description: "Default description", getDefaultValue: () => "S00p3r S33kr!t");

var root = new RootCommand
{
    passwordOption
};

var parser = new CommandLineBuilder(root)
    .UseDefaults()
    .UseHelpBuilder(bindingContext => 
        { 
            var hb = new HelpBuilder(bindingContext.Console);
            hb.Customize(passwordOption, "--password", "From environment variable XYZ");
            return hb;
        })
    .Build();

parser.Invoke("-h");

This produces the following output:

MyApp

Usage:
  MyApp [options]

Options:
  --password      Default description [default: From environment variable XYZ]
  --version       Show version information
  -?, -h, --help  Show help and usage information

So this API is nigh impossible to discover and taking on creating the HelpBuilder to get at this API is too complicated even once you know you can do this. A better customization API will go a long way toward letting people address a lot of these issues: https://github.com/dotnet/command-line-api/issues?q=is%3Aopen+is%3Aissue+label%3AArea-Help. We actually started work on System.CommandLine.Rendering in part because a templating engine seemed more suited than an API to the wide variety of help layouts people want. Our more pragmatic goal is:

  • Enable the API to handle 80% of cases, and
  • Allow a simple way to bring your own templating API.

Suggestions for improvements are more than welcome.

jonsequitur avatar Nov 05 '21 18:11 jonsequitur

Hi there,

I have a similar problem. My point is that I want to provide an option which should stay null when not explicitly set, and also display a hint about what is the default value if none is provided in the command-line.

I suppose it's a "standard" behavior:

  • if a value is provided in the command-line, use it
  • if not, check an environment variable
  • if not, use a default const value

I don't know how the help/api could address that. I suppose that for such a case, as you suggested, the default value text in the help screen should be decorrelated from the () => null default value.

tbolon avatar Feb 15 '22 21:02 tbolon

Hi everyone,

just want to add that we are also in the same boat and would like to hide default values from the --help output.

We have a password option and also a lot of options that are simply null or an empty string. Most feedback we got is that the output of [Default: ] is not helpful or is even irritating to the user.

DelphinRP avatar Apr 27 '22 12:04 DelphinRP

You can override the default help output using the APIs described here: https://docs.microsoft.com/en-us/dotnet/standard/commandline/customize-help#customize-help-for-a-single-option-or-argument.

Take a look at specifying a different secondColumnText and let us know whether this works for you.

jonsequitur avatar Apr 28 '22 02:04 jonsequitur