This commit is contained in:
Jun Nishimura 2025-01-24 00:07:24 +09:00 committed by GitHub
commit 72f699a553
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 105 additions and 3 deletions

View file

@ -32,6 +32,7 @@ import (
const (
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
trueString = "true"
)
// FParseErrWhitelist configures Flag parse errors to be ignored
@ -1161,7 +1162,7 @@ func (c *Command) ValidateRequiredFlags() error {
if !found {
return
}
if (requiredAnnotation[0] == "true") && !pflag.Changed {
if (requiredAnnotation[0] == trueString) && !pflag.Changed {
missingFlagNames = append(missingFlagNames, pflag.Name)
}
})
@ -1199,7 +1200,7 @@ func (c *Command) InitDefaultHelpFlag() {
usage += name
}
c.Flags().BoolP("help", "h", false, usage)
_ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"})
_ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{trueString})
}
}
@ -1225,7 +1226,7 @@ func (c *Command) InitDefaultVersionFlag() {
} else {
c.Flags().Bool("version", false, usage)
}
_ = c.Flags().SetAnnotation("version", FlagSetByCobraAnnotation, []string{"true"})
_ = c.Flags().SetAnnotation("version", FlagSetByCobraAnnotation, []string{trueString})
}
}
@ -1262,6 +1263,23 @@ Simply type ` + c.DisplayName() + ` help [path to command] for full details.`,
}
return completions, ShellCompDirectiveNoFileComp
},
PersistentPreRunE: func(cmd *Command, args []string) error {
cmd.Flags().VisitAll(func(pflag *flag.Flag) {
requiredAnnotation, found := pflag.Annotations[BashCompOneRequiredFlag]
if found && requiredAnnotation[0] == trueString {
// Disable any persistent required flags for the help command
pflag.Annotations[BashCompOneRequiredFlag] = []string{"false"}
}
})
// Adding PersistentPreRun on sub-commands prevents root's PersistentPreRun from being called.
// So it is intentionally called here.
if cmd.Root().PersistentPreRunE != nil {
return cmd.Root().PersistentPreRunE(cmd, args)
} else if cmd.Root().PersistentPreRun != nil {
cmd.Root().PersistentPreRun(cmd, args)
}
return nil
},
Run: func(c *Command, args []string) {
cmd, _, e := c.Root().Find(args)
if cmd == nil || e != nil {

View file

@ -950,6 +950,18 @@ func TestHelpCommandExecuted(t *testing.T) {
checkStringContains(t, output, rootCmd.Long)
}
func TestHelpCommandExecutedWithPersistentRequiredFlags(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
rootCmd.PersistentFlags().Bool("foo", false, "")
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
assertNoErr(t, rootCmd.MarkPersistentFlagRequired("foo"))
if _, err := executeCommand(rootCmd, "help"); err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func TestHelpCommandExecutedOnChild(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
@ -1703,6 +1715,25 @@ func testPersistentHooks(t *testing.T, expectedHookRunOrder []string) {
}
}
func TestPersistentPreRunHooksForHelpCommand(t *testing.T) {
executed := false
rootCmd := &Command{
Use: "root",
PersistentPreRun: func(*Command, []string) { executed = true },
Run: emptyRun,
}
childCmd := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(childCmd)
if _, err := executeCommand(rootCmd, "help"); err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !executed {
t.Error("Root PersistentPreRun should have been executed")
}
}
// Related to https://github.com/spf13/cobra/issues/521.
func TestGlobalNormFuncPropagation(t *testing.T) {
normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName {

View file

@ -735,6 +735,23 @@ See each sub-command's help for details on how to use the generated script.
ValidArgsFunction: NoFileCompletions,
Hidden: c.CompletionOptions.HiddenDefaultCmd,
GroupID: c.completionCommandGroupID,
PersistentPreRunE: func(cmd *Command, args []string) error {
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
requiredAnnotation, found := flag.Annotations[BashCompOneRequiredFlag]
if found && requiredAnnotation[0] == "true" {
// Disable any persistent required flags for the completion command
flag.Annotations[BashCompOneRequiredFlag] = []string{"false"}
}
})
// Adding PersistentPreRun on sub-commands prevents root's PersistentPreRun from being called.
// So it is intentionally called here.
if cmd.Root().PersistentPreRunE != nil {
return cmd.Root().PersistentPreRunE(cmd, args)
} else if cmd.Root().PersistentPreRun != nil {
cmd.Root().PersistentPreRun(cmd, args)
}
return nil
},
}
c.AddCommand(completionCmd)

View file

@ -2591,6 +2591,42 @@ func TestDefaultCompletionCmd(t *testing.T) {
rootCmd.CompletionOptions.HiddenDefaultCmd = false
// Remove completion command for the next test
removeCompCmd(rootCmd)
// Test that required flag will be ignored
rootCmd.PersistentFlags().Bool("foo", false, "")
assertNoErr(t, rootCmd.MarkPersistentFlagRequired("foo"))
for _, shell := range []string{"bash", "fish", "powershell", "zsh"} {
if _, err = executeCommand(rootCmd, compCmdName, shell); err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
// Remove completion command for the next test
removeCompCmd(rootCmd)
}
func TestPersistentPreRunHooksForCompletionCommand(t *testing.T) {
executed := false
rootCmd := &Command{
Use: "root",
PersistentPreRun: func(*Command, []string) { executed = true },
Run: emptyRun,
}
subCmd := &Command{
Use: "sub",
Run: emptyRun,
}
rootCmd.AddCommand(subCmd)
for _, shell := range []string{"bash", "fish", "powershell", "zsh"} {
if _, err := executeCommand(rootCmd, compCmdName, shell); err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !executed {
t.Error("Root PersistentPreRun should have been executed")
}
executed = false
}
}
func TestCompleteCompletion(t *testing.T) {