Merge pull request #872 from urfave/pass-through-regression

Fix 1.21.0 pass through regression
This commit is contained in:
Audrius Butkevicius 2019-10-22 08:14:13 +01:00 committed by GitHub
commit 397c7d6c1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 117 additions and 19 deletions

59
app_regression_test.go Normal file
View File

@ -0,0 +1,59 @@
package cli
import (
"testing"
)
// TestRegression tests a regression that was merged between versions 1.20.0 and 1.21.0
// The included app.Run line worked in 1.20.0, and then was broken in 1.21.0.
// Relevant PR: https://github.com/urfave/cli/pull/872
func TestVersionOneTwoOneRegression(t *testing.T) {
testData := []struct {
testCase string
appRunInput []string
skipArgReorder bool
}{
{
testCase: "with_dash_dash",
appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "--", "docker", "image", "ls", "--no-trunc"},
},
{
testCase: "with_dash_dash_and_skip_reorder",
appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "--", "docker", "image", "ls", "--no-trunc"},
skipArgReorder: true,
},
{
testCase: "without_dash_dash",
appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "docker", "image", "ls", "--no-trunc"},
},
{
testCase: "without_dash_dash_and_skip_reorder",
appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "docker", "image", "ls", "--no-trunc"},
skipArgReorder: true,
},
}
for _, test := range testData {
t.Run(test.testCase, func(t *testing.T) {
// setup
app := NewApp()
app.Commands = []Command{{
Name: "command",
SkipArgReorder: test.skipArgReorder,
Flags: []Flag{
StringFlag{
Name: "flagone",
},
},
Action: func(c *Context) error { return nil },
}}
// logic under test
err := app.Run(test.appRunInput)
// assertions
if err != nil {
t.Errorf("did not expected an error, but there was one: %s", err)
}
})
}
}

View File

@ -190,7 +190,7 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) {
} }
if !c.SkipArgReorder { if !c.SkipArgReorder {
args = reorderArgs(args) args = reorderArgs(c.Flags, args)
} }
set, err := c.newFlagSet() set, err := c.newFlagSet()
@ -219,34 +219,73 @@ func (c *Command) useShortOptionHandling() bool {
return c.UseShortOptionHandling return c.UseShortOptionHandling
} }
// reorderArgs moves all flags before arguments as this is what flag expects // reorderArgs moves all flags (via reorderedArgs) before the rest of
func reorderArgs(args []string) []string { // the arguments (remainingArgs) as this is what flag expects.
var nonflags, flags []string func reorderArgs(commandFlags []Flag, args []string) []string {
var remainingArgs, reorderedArgs []string
readFlagValue := false nextIndexMayContainValue := false
for i, arg := range args { for i, arg := range args {
// dont reorder any args after a --
// read about -- here:
// https://unix.stackexchange.com/questions/11376/what-does-double-dash-mean-also-known-as-bare-double-dash
if arg == "--" { if arg == "--" {
nonflags = append(nonflags, args[i:]...) remainingArgs = append(remainingArgs, args[i:]...)
break break
}
if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") { // checks if this arg is a value that should be re-ordered next to its associated flag
readFlagValue = false } else if nextIndexMayContainValue && !strings.HasPrefix(arg, "-") {
flags = append(flags, arg) nextIndexMayContainValue = false
continue reorderedArgs = append(reorderedArgs, arg)
}
readFlagValue = false
if arg != "-" && strings.HasPrefix(arg, "-") { // checks if this is an arg that should be re-ordered
flags = append(flags, arg) } else if argIsFlag(commandFlags, arg) {
// we have determined that this is a flag that we should re-order
reorderedArgs = append(reorderedArgs, arg)
// if this arg does not contain a "=", then the next index may contain the value for this flag
nextIndexMayContainValue = !strings.Contains(arg, "=")
readFlagValue = !strings.Contains(arg, "=") // simply append any remaining args
} else { } else {
nonflags = append(nonflags, arg) remainingArgs = append(remainingArgs, arg)
} }
} }
return append(flags, nonflags...) return append(reorderedArgs, remainingArgs...)
}
// argIsFlag checks if an arg is one of our command flags
func argIsFlag(commandFlags []Flag, arg string) bool {
// checks if this is just a `-`, and so definitely not a flag
if arg == "-" {
return false
}
// flags always start with a -
if !strings.HasPrefix(arg, "-") {
return false
}
// this line turns `--flag` into `flag`
if strings.HasPrefix(arg, "--") {
arg = strings.Replace(arg, "-", "", 2)
}
// this line turns `-flag` into `flag`
if strings.HasPrefix(arg, "-") {
arg = strings.Replace(arg, "-", "", 1)
}
// this line turns `flag=value` into `flag`
arg = strings.Split(arg, "=")[0]
// look through all the flags, to see if the `arg` is one of our flags
for _, flag := range commandFlags {
for _, key := range strings.Split(flag.GetName(), ",") {
key := strings.TrimSpace(key)
if key == arg {
return true
}
}
}
// return false if this arg was not one of our flags
return false
} }
// Names returns the names including short names and aliases. // Names returns the names including short names and aliases.

View File

@ -18,7 +18,7 @@ func TestCommandFlagParsing(t *testing.T) {
UseShortOptionHandling bool UseShortOptionHandling bool
}{ }{
// Test normal "not ignoring flags" flow // Test normal "not ignoring flags" flow
{[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break"), false}, {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, nil, false},
// Test no arg reorder // Test no arg reorder
{[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false}, {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false},