-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CLI Enhancements #3897
CLI Enhancements #3897
Conversation
/cc @sethvargo, it'd be great to get your feedback on this :) This PR mainly aims to address two main points:
|
Hey @calvn Thank you for the ping. This looks really good, but I do have a few comments.
|
command/main.go
Outdated
@@ -23,6 +23,10 @@ func Run(args []string) int { | |||
args = []string{"version"} | |||
break | |||
} | |||
|
|||
if arg == "-no-color" { | |||
os.Setenv("VAULT_OUTPUT_NO_COLOR", "true") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think VAULT_CLI_NO_COLOR would be better here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to remove this flag entirely. If we do the os.Getenv logic check on command.go's init()
func as discussed, the envvar will be read before this gets applied.
Optionally, we could keep this if we change init()
to a func that needs to be called explicitly (and call it after the args have been parsed) in main.go's Run()
.
@sethvargo thanks for taking a look! I think I've got a path forward with points 2 and 3, as we will probably end up removing the I am still unclear on the first point though, and why we'd need to feed the output functions through |
Hey @calvn Sorry - I'm not suggesting we need to use
|
Ahh, I see. I am using a pretty much the same logic to detect whether there's a TTY, but instead of grabbing the Lines 126 to 138 in 8f2a229
|
Yea, so the problem is that, even if you don't use color, you'll still include a newline at the end of the line if piping to another process with the non-colored UI. The only way to avoid that is to use the underlying type VaultUI struct {
cli.ColoredUi
}
func (u *VaultUI) Output(m string) {
terminal.IsTerminal(int(os.Stdout.Fd())) {
c.ColoredUi.Output(m)
} else {
getWriter(c).Write(m)
}
} Then we just basically never think about it. We instantiate exactly 1 type of CLI, and that CLI decides, based on each output, whether there's a TTY present and "does the right thing". We could add the envvar logic in there too. If we want to be efficient, we could use an initializer and parse the envvar and tty check once and cache it on the struct. |
Ah, I see what you mean, but I don't think that the additional newline would be an issue here as the intention for the no-color option was to avoid having ANSI color escape sequences specifically. I think for most cases people still end up using Jeff suggested that in the future we may add a |
command/format.go
Outdated
switch data.(type) { | ||
case *api.Secret: | ||
secret := data.(*api.Secret) | ||
// Best-guess effort that is this a list, so parse out the keys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for doing this? There could very well be "keys" coming back from various different backends, including k/v.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, so in that case you output things that are inside secret.Data[“keys”]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OutputWithFormat
is basically the combined functionality of OutputSecret
and OutputList
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, so in that case you output things that are inside secret.Data[“keys”]
But....why? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the original OutputList behavior: https://github.com/hashicorp/vault/pull/3897/files#diff-3e4377911a05a6f261016d1d5e1fec91L27
If you just passed in the secret
, listing keys would be not formatted correctly:
Key Value
--- -----
keys [foo]
Instead of:
$ vault list secret/
Keys
----
foo
#3950) * Pull formatting much further up to remove the need to use c.flagFormat Also remove OutputWithFormat as the logic can cause issues. * Use const for env var
command/base.go
Outdated
flagField string | ||
flagFormat string | ||
flagField string | ||
flagNoColor bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this and the var below if we are just using the envvar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to allow it in a flag but not advertise it. Now that you mention autocorrect below, I wonder if it's better to just remove it rather than have people get it autocorrected and then complain that it's not documented. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the code will be a lot simpler if we drop the flag entirely. People are more likely to "never want colored output" that "not want colored output for this specific command", and even in the latter case, they can specify the envvar first.
command/format.go
Outdated
@@ -58,6 +67,14 @@ var Formatters = map[string]Formatter{ | |||
"yml": YamlFormatter{}, | |||
} | |||
|
|||
func Format() string { | |||
format := os.Getenv(EnvVaultFormat) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we retrieve this as part of an init function instead of doing the envvar lookup each time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just got away from init functions in favor of being able to intercept the args earlier in the main run function. This isn't going to be meaningfully slow for interactive uses, is there a reason not to just call this a couple of times?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've experienced Getenv
being slow (in relative terms), particularly on Windows systems. I wonder if we could use a Once instead and cache the result?
command/main.go
Outdated
@@ -23,8 +47,85 @@ func Run(args []string) int { | |||
args = []string{"version"} | |||
break | |||
} | |||
|
|||
if arg == "-no-color" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UX-ey thing. We should be nice and add || arg == "-nocolor"
. Even if we never advertise that, people will forget the dash and "auto-correcting them" will be a nice UX.
@sethvargo Your comments should be addressed in 9b62480 |
command/main.go
Outdated
@@ -23,7 +48,83 @@ func Run(args []string) int { | |||
args = []string{"version"} | |||
break | |||
} | |||
|
|||
// Parse a given flag here, which overrides the env var | |||
if strings.HasPrefix(arg, "-format=") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't tested, but does this handle --format
as well? (L53-L59)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't even know we supported that. Will address.
command/main.go
Outdated
} | ||
|
||
// setupEnv parses args and may replace them and sets some env vars to known | ||
// values based on color/format options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't handle color (anymore..?)
envVaultFormat := os.Getenv(EnvVaultFormat) | ||
// If we did not parse a value, fetch the env var | ||
if format == "" && envVaultFormat != "" { | ||
format = envVaultFormat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably do format = strings.ToLower(envVaultFormat)
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd still need it outside the if
block so it doesn't buy us anything.
} | ||
|
||
func Run(args []string) int { | ||
args = setupEnv(args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should rename setupEnv
to setupEnvFormat
.
-no-color
flag to all leaf subcommands(caveat: Warnings printed by DeprecatedCommand is always colored, since flags gets parsed afterwards)-format
to all applicable subcommandsIgnore warnings and return only structured responses for non-table outputs (toggleable).(This is not done since the current behavior for existing commands that supports formatted output do print warnings; e.g. token_renew.go).Fixes #3869
Closes #3914