Skip to content

Commit

Permalink
Added a small utility method to display warnings when parsing command…
Browse files Browse the repository at this point in the history
… arguments. (#16441)

* Added a small utility method to display warnings when parsing command arguments

Will print warning if flag is passed after arguments e.g.
vault <command> -a b -c
In this example -c will be interpreted as an argument which may be misleading
  • Loading branch information
maxcoulombe committed Jul 27, 2022
1 parent a269ca6 commit 2166d6e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 3 deletions.
3 changes: 3 additions & 0 deletions changelog/16441.txt
@@ -0,0 +1,3 @@
```release-note:improvement
cli: CLI commands will print a warning if flags will be ignored because they are passed after positional arguments.
```
16 changes: 13 additions & 3 deletions command/base.go
Expand Up @@ -549,6 +549,7 @@ type FlagSets struct {
mainSet *flag.FlagSet
hiddens map[string]struct{}
completions complete.Flags
ui cli.Ui
}

// NewFlagSets creates a new flag sets.
Expand All @@ -564,6 +565,7 @@ func NewFlagSets(ui cli.Ui) *FlagSets {
mainSet: mainSet,
hiddens: make(map[string]struct{}),
completions: complete.Flags{},
ui: ui,
}
}

Expand All @@ -582,8 +584,16 @@ func (f *FlagSets) Completions() complete.Flags {
}

// Parse parses the given flags, returning any errors.
// Warnings, if any, regarding the arguments format are sent to stdout
func (f *FlagSets) Parse(args []string) error {
return f.mainSet.Parse(args)
err := f.mainSet.Parse(args)

warnings := generateFlagWarnings(f.Args())
if warnings != "" {
f.ui.Warn(warnings)
}

return err
}

// Parsed reports whether the command-line flags have been parsed.
Expand All @@ -603,10 +613,10 @@ func (f *FlagSets) Visit(fn func(*flag.Flag)) {
}

// Help builds custom help for this command, grouping by flag set.
func (fs *FlagSets) Help() string {
func (f *FlagSets) Help() string {
var out bytes.Buffer

for _, set := range fs.flagSets {
for _, set := range f.flagSets {
printFlagTitle(&out, set.name+":")
set.VisitAll(func(f *flag.Flag) {
// Skip any hidden flags
Expand Down
16 changes: 16 additions & 0 deletions command/base_helpers.go
Expand Up @@ -292,3 +292,19 @@ func parseFlagFile(raw string) (string, error) {

return raw, nil
}

func generateFlagWarnings(args []string) string {
var trailingFlags []string
for _, arg := range args {
if strings.HasPrefix(arg, "-") {
trailingFlags = append(trailingFlags, arg)
}
}

if len(trailingFlags) > 0 {
return fmt.Sprintf("Flags must be provided before positional arguments. "+
"The following arguments will not be parsed as flags: [%s]", strings.Join(trailingFlags, ","))
} else {
return ""
}
}
46 changes: 46 additions & 0 deletions command/base_helpers_test.go
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"io/ioutil"
"os"
"strings"
"testing"
"time"
)
Expand Down Expand Up @@ -209,3 +210,48 @@ func TestParseFlagFile(t *testing.T) {
})
}
}

func TestArgWarnings(t *testing.T) {
t.Parallel()

cases := []struct {
args []string
expected string
}{
{
[]string{"a", "b", "c"},
"",
},
{
[]string{"a", "-b"},
"-b",
},
{
[]string{"a", "--b"},
"--b",
},
{
[]string{"a-b", "-c"},
"-c",
},
{
[]string{"a", "-b-c"},
"-b-c",
},
{
[]string{"-a", "b"},
"-a",
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.expected, func(t *testing.T) {
warnings := generateFlagWarnings(tc.args)
if !strings.Contains(warnings, tc.expected) {
t.Fatalf("expected %s to contain %s", warnings, tc.expected)
}
})
}
}

0 comments on commit 2166d6e

Please sign in to comment.