Repository: spf13/cobra
Pull request #522 (2017-08-29 10:47:47)

Proposed fix for issue #521
Related to issue #521

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/spf13/cobra?pullRequest=522) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/spf13/cobra?pullRequest=522) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/spf13/cobra?pullRequest=522) it.</sub>
Itizir: Hm, sorry about the hesitation: got confused with what normalization function inherited flags should get... Probably the one of the parent, in fact.
Let me know if that makes sense and I'll change this, too.

Patch:
diff --git a/bash_completions.go b/bash_completions.go
index e0cfb34..e402065 100644
--- a/bash_completions.go
+++ b/bash_completions.go
@@ -463,14 +463,14 @@ func gen(buf *bytes.Buffer, cmd *Command) {
 }
 
 // GenBashCompletion generates bash completion file and writes to the passed writer.
-func (cmd *Command) GenBashCompletion(w io.Writer) error {
+func (c *Command) GenBashCompletion(w io.Writer) error {
 	buf := new(bytes.Buffer)
-	writePreamble(buf, cmd.Name())
-	if len(cmd.BashCompletionFunction) > 0 {
-		buf.WriteString(cmd.BashCompletionFunction + "\n")
+	writePreamble(buf, c.Name())
+	if len(c.BashCompletionFunction) > 0 {
+		buf.WriteString(c.BashCompletionFunction + "\n")
 	}
-	gen(buf, cmd)
-	writePostscript(buf, cmd.Name())
+	gen(buf, c)
+	writePostscript(buf, c.Name())
 
 	_, err := buf.WriteTo(w)
 	return err
@@ -481,24 +481,24 @@ func nonCompletableFlag(flag *pflag.Flag) bool {
 }
 
 // GenBashCompletionFile generates bash completion file.
-func (cmd *Command) GenBashCompletionFile(filename string) error {
+func (c *Command) GenBashCompletionFile(filename string) error {
 	outFile, err := os.Create(filename)
 	if err != nil {
 		return err
 	}
 	defer outFile.Close()
 
-	return cmd.GenBashCompletion(outFile)
+	return c.GenBashCompletion(outFile)
 }
 
 // MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists.
-func (cmd *Command) MarkFlagRequired(name string) error {
-	return MarkFlagRequired(cmd.Flags(), name)
+func (c *Command) MarkFlagRequired(name string) error {
+	return MarkFlagRequired(c.Flags(), name)
 }
 
 // MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag, if it exists.
-func (cmd *Command) MarkPersistentFlagRequired(name string) error {
-	return MarkFlagRequired(cmd.PersistentFlags(), name)
+func (c *Command) MarkPersistentFlagRequired(name string) error {
+	return MarkFlagRequired(c.PersistentFlags(), name)
 }
 
 // MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag in the flag set, if it exists.
@@ -508,20 +508,20 @@ func MarkFlagRequired(flags *pflag.FlagSet, name string) error {
 
 // MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag, if it exists.
 // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
-func (cmd *Command) MarkFlagFilename(name string, extensions ...string) error {
-	return MarkFlagFilename(cmd.Flags(), name, extensions...)
+func (c *Command) MarkFlagFilename(name string, extensions ...string) error {
+	return MarkFlagFilename(c.Flags(), name, extensions...)
 }
 
 // MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists.
 // Generated bash autocompletion will call the bash function f for the flag.
-func (cmd *Command) MarkFlagCustom(name string, f string) error {
-	return MarkFlagCustom(cmd.Flags(), name, f)
+func (c *Command) MarkFlagCustom(name string, f string) error {
+	return MarkFlagCustom(c.Flags(), name, f)
 }
 
 // MarkPersistentFlagFilename adds the BashCompFilenameExt annotation to the named persistent flag, if it exists.
 // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
-func (cmd *Command) MarkPersistentFlagFilename(name string, extensions ...string) error {
-	return MarkFlagFilename(cmd.PersistentFlags(), name, extensions...)
+func (c *Command) MarkPersistentFlagFilename(name string, extensions ...string) error {
+	return MarkFlagFilename(c.PersistentFlags(), name, extensions...)
 }
 
 // MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag in the flag set, if it exists.
diff --git a/cobra/cmd/helpers.go b/cobra/cmd/helpers.go
index c5e261c..e5b37ec 100644
--- a/cobra/cmd/helpers.go
+++ b/cobra/cmd/helpers.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 	"io"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"strings"
 	"text/template"
@@ -31,7 +32,27 @@ func init() {
 	envGoPath := os.Getenv("GOPATH")
 	goPaths := filepath.SplitList(envGoPath)
 	if len(goPaths) == 0 {
-		er("$GOPATH is not set")
+		// Adapted from https://github.com/Masterminds/glide/pull/798/files.
+		// As of Go 1.8 the GOPATH is no longer required to be set. Instead there
+		// is a default value. If there is no GOPATH check for the default value.
+		// Note, checking the GOPATH first to avoid invoking the go toolchain if
+		// possible.
+
+		goExecutable := os.Getenv("COBRA_GO_EXECUTABLE")
+		if len(goExecutable) <= 0 {
+			goExecutable = "go"
+		}
+
+		out, err := exec.Command(goExecutable, "env", "GOPATH").Output()
+		if err != nil {
+			er(err)
+		}
+
+		toolchainGoPath := strings.TrimSpace(string(out))
+		goPaths = filepath.SplitList(toolchainGoPath)
+		if len(goPaths) == 0 {
+			er("$GOPATH is not set")
+		}
 	}
 	srcPaths = make([]string, 0, len(goPaths))
 	for _, goPath := range goPaths {
diff --git a/command.go b/command.go
index 185e452..a0a633d 100644
--- a/command.go
+++ b/command.go
@@ -806,6 +806,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
 
 // ResetCommands used for testing.
 func (c *Command) ResetCommands() {
+	c.parent = nil
 	c.commands = nil
 	c.helpCommand = nil
 	c.parentsPflags = nil
@@ -1132,6 +1133,9 @@ func (c *Command) LocalFlags() *flag.FlagSet {
 		c.lflags.SetOutput(c.flagErrorBuf)
 	}
 	c.lflags.SortFlags = c.Flags().SortFlags
+	if c.globNormFunc != nil {
+		c.lflags.SetNormalizeFunc(c.globNormFunc)
+	}
 
 	addToLocal := func(f *flag.Flag) {
 		if c.lflags.Lookup(f.Name) == nil && c.parentsPflags.Lookup(f.Name) == nil {
@@ -1156,6 +1160,10 @@ func (c *Command) InheritedFlags() *flag.FlagSet {
 	}
 
 	local := c.LocalFlags()
+	if c.globNormFunc != nil {
+		c.iflags.SetNormalizeFunc(c.globNormFunc)
+	}
+
 	c.parentsPflags.VisitAll(func(f *flag.Flag) {
 		if c.iflags.Lookup(f.Name) == nil && local.Lookup(f.Name) == nil {
 			c.iflags.AddFlag(f)
@@ -1189,6 +1197,10 @@ func (c *Command) ResetFlags() {
 	c.flags.SetOutput(c.flagErrorBuf)
 	c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
 	c.pflags.SetOutput(c.flagErrorBuf)
+
+	c.lflags = nil
+	c.iflags = nil
+	c.parentsPflags = nil
 }
 
 // HasFlags checks if the command contains any flags (local plus persistent from the entire structure).
@@ -1298,6 +1310,10 @@ func (c *Command) updateParentsPflags() {
 		c.parentsPflags.SortFlags = false
 	}
 
+	if c.globNormFunc != nil {
+		c.parentsPflags.SetNormalizeFunc(c.globNormFunc)
+	}
+
 	c.Root().PersistentFlags().AddFlagSet(flag.CommandLine)
 
 	c.VisitParents(func(parent *Command) {
diff --git a/zsh_completions.go b/zsh_completions.go
index b350aee..889c22e 100644
--- a/zsh_completions.go
+++ b/zsh_completions.go
@@ -4,17 +4,29 @@ import (
 	"bytes"
 	"fmt"
 	"io"
+	"os"
 	"strings"
 )
 
+// GenZshCompletionFile generates zsh completion file.
+func (c *Command) GenZshCompletionFile(filename string) error {
+	outFile, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer outFile.Close()
+
+	return c.GenZshCompletion(outFile)
+}
+
 // GenZshCompletion generates a zsh completion file and writes to the passed writer.
-func (cmd *Command) GenZshCompletion(w io.Writer) error {
+func (c *Command) GenZshCompletion(w io.Writer) error {
 	buf := new(bytes.Buffer)
 
-	writeHeader(buf, cmd)
-	maxDepth := maxDepth(cmd)
+	writeHeader(buf, c)
+	maxDepth := maxDepth(c)
 	writeLevelMapping(buf, maxDepth)
-	writeLevelCases(buf, maxDepth, cmd)
+	writeLevelCases(buf, maxDepth, c)
 
 	_, err := buf.WriteTo(w)
 	return err


Test patch:
diff --git a/bash_completions_test.go b/bash_completions_test.go
index 071a6a2..a3b13a3 100644
--- a/bash_completions_test.go
+++ b/bash_completions_test.go
@@ -118,7 +118,7 @@ func TestBashCompletions(t *testing.T) {
 	check(t, str, `flags_completion+=("_filedir")`)
 	// check for filename extension flags
 	check(t, str, `must_have_one_noun+=("three")`)
-	// check for filename extention flags
+	// check for filename extension flags
 	check(t, str, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`)
 	// check for custom flags
 	check(t, str, `flags_completion+=("__complete_custom")`)
diff --git a/cobra_test.go b/cobra_test.go
index d5df951..8192b52 100644
--- a/cobra_test.go
+++ b/cobra_test.go
@@ -190,6 +190,7 @@ func flagInit() {
 	cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo")
 	cmdTimes.Flags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp)
 	cmdTimes.PersistentFlags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp)
+	cmdTimes.LocalFlags() // populate lflags before parent is set
 	cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree")
 	cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree")
 }
@@ -210,8 +211,8 @@ func initialize() *Command {
 	rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil
 
 	var c = cmdRootNoRun
-	flagInit()
 	commandInit()
+	flagInit()
 	return c
 }
 
@@ -219,8 +220,8 @@ func initializeWithSameName() *Command {
 	tt, tp, te = nil, nil, nil
 	rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil
 	var c = cmdRootSameName
-	flagInit()
 	commandInit()
+	flagInit()
 	return c
 }
 
@@ -910,6 +911,7 @@ func TestRootHelp(t *testing.T) {
 func TestFlagAccess(t *testing.T) {
 	initialize()
 
+	cmdEcho.AddCommand(cmdTimes)
 	local := cmdTimes.LocalFlags()
 	inherited := cmdTimes.InheritedFlags()
 
@@ -1165,11 +1167,18 @@ func TestGlobalNormFuncPropagation(t *testing.T) {
 	}
 
 	rootCmd := initialize()
+	rootCmd.AddCommand(cmdEcho)
+
 	rootCmd.SetGlobalNormalizationFunc(normFunc)
 	if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
 		t.Error("rootCmd seems to have a wrong normalization function")
 	}
 
+	// Also check it propagates retroactively
+	if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(cmdEcho.GlobalNormalizationFunc()).Pointer() {
+		t.Error("cmdEcho should have had the normalization function of rootCmd")
+	}
+
 	// First add the cmdEchoSub to cmdPrint
 	cmdPrint.AddCommand(cmdEchoSub)
 	if cmdPrint.GlobalNormalizationFunc() != nil && cmdEchoSub.GlobalNormalizationFunc() != nil {
@@ -1184,6 +1193,67 @@ func TestGlobalNormFuncPropagation(t *testing.T) {
 	}
 }
 
+func TestNormPassedOnLocal(t *testing.T) {
+	n := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
+		return pflag.NormalizedName(strings.ToUpper(name))
+	}
+
+	cmd := &Command{}
+	flagVal := false
+
+	cmd.Flags().BoolVar(&flagVal, "flagname", true, "this is a dummy flag")
+	cmd.SetGlobalNormalizationFunc(n)
+	if cmd.LocalFlags().Lookup("flagname") != cmd.LocalFlags().Lookup("FLAGNAME") {
+		t.Error("Normalization function should be passed on to Local flag set")
+	}
+}
+
+func TestNormPassedOnInherited(t *testing.T) {
+	n := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
+		return pflag.NormalizedName(strings.ToUpper(name))
+	}
+
+	cmd, childBefore, childAfter := &Command{}, &Command{}, &Command{}
+	flagVal := false
+	cmd.AddCommand(childBefore)
+
+	cmd.PersistentFlags().BoolVar(&flagVal, "flagname", true, "this is a dummy flag")
+	cmd.SetGlobalNormalizationFunc(n)
+
+	cmd.AddCommand(childAfter)
+
+	if f := childBefore.InheritedFlags(); f.Lookup("flagname") == nil || f.Lookup("flagname") != f.Lookup("FLAGNAME") {
+		t.Error("Normalization function should be passed on to inherited flag set in command added before flag")
+	}
+	if f := childAfter.InheritedFlags(); f.Lookup("flagname") == nil || f.Lookup("flagname") != f.Lookup("FLAGNAME") {
+		t.Error("Normalization function should be passed on to inherited flag set in command added after flag")
+	}
+}
+
+// Related to https://github.com/spf13/cobra/issues/521.
+func TestNormConsistent(t *testing.T) {
+	n := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
+		return pflag.NormalizedName(strings.ToUpper(name))
+	}
+	id := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
+		return pflag.NormalizedName(name)
+	}
+
+	cmd := &Command{}
+	flagVal := false
+
+	cmd.Flags().BoolVar(&flagVal, "flagname", true, "this is a dummy flag")
+	// Build local flag set
+	cmd.LocalFlags()
+
+	cmd.SetGlobalNormalizationFunc(n)
+	cmd.SetGlobalNormalizationFunc(id)
+
+	if cmd.LocalFlags().Lookup("flagname") == cmd.LocalFlags().Lookup("FLAGNAME") {
+		t.Error("Normalizing flag names should not result in duplicate flags")
+	}
+}
+
 func TestFlagOnPflagCommandLine(t *testing.T) {
 	flagName := "flagOnCommandLine"
 	pflag.String(flagName, "", "about my flag")