Merge master, and adapt flag completion getters

This commit is contained in:
maxlandon 2023-09-30 20:43:04 +02:00
parent 53fb4ebbd1
commit 4d3a6e1d56
No known key found for this signature in database
GPG key ID: 2DE5C14975A86900
5 changed files with 94 additions and 5 deletions

View file

@ -43,13 +43,13 @@ jobs:
- uses: actions/setup-go@v3 - uses: actions/setup-go@v3
with: with:
go-version: '^1.20' go-version: '^1.21'
check-latest: true check-latest: true
cache: true cache: true
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: golangci/golangci-lint-action@v3.6.0 - uses: golangci/golangci-lint-action@v3.7.0
with: with:
version: latest version: latest
args: --verbose args: --verbose
@ -67,6 +67,7 @@ jobs:
- 18 - 18
- 19 - 19
- 20 - 20
- 21
name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x' name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x'
runs-on: ${{ matrix.platform }}-latest runs-on: ${{ matrix.platform }}-latest
steps: steps:

View file

@ -189,6 +189,9 @@ type Command struct {
// versionTemplate is the version template defined by user. // versionTemplate is the version template defined by user.
versionTemplate string versionTemplate string
// errPrefix is the error message prefix defined by user.
errPrefix string
// inReader is a reader defined by the user that replaces stdin // inReader is a reader defined by the user that replaces stdin
inReader io.Reader inReader io.Reader
// outWriter is a writer defined by the user that replaces stdout // outWriter is a writer defined by the user that replaces stdout
@ -354,6 +357,11 @@ func (c *Command) SetVersionTemplate(s string) {
c.versionTemplate = s c.versionTemplate = s
} }
// SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix.
func (c *Command) SetErrPrefix(s string) {
c.errPrefix = s
}
// SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands. // SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands.
// The user should not have a cyclic dependency on commands. // The user should not have a cyclic dependency on commands.
func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) { func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) {
@ -603,6 +611,18 @@ func (c *Command) VersionTemplate() string {
` `
} }
// ErrPrefix return error message prefix for the command
func (c *Command) ErrPrefix() string {
if c.errPrefix != "" {
return c.errPrefix
}
if c.HasParent() {
return c.parent.ErrPrefix()
}
return "Error:"
}
func hasNoOptDefVal(name string, fs *flag.FlagSet) bool { func hasNoOptDefVal(name string, fs *flag.FlagSet) bool {
flag := fs.Lookup(name) flag := fs.Lookup(name)
if flag == nil { if flag == nil {
@ -760,7 +780,9 @@ func (c *Command) findNext(next string) *Command {
} }
if len(matches) == 1 { if len(matches) == 1 {
return matches[0] // Temporarily disable gosec G602, which produces a false positive.
// See https://github.com/securego/gosec/issues/1005.
return matches[0] // #nosec G602
} }
return nil return nil
@ -1056,7 +1078,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
c = cmd c = cmd
} }
if !c.SilenceErrors { if !c.SilenceErrors {
c.PrintErrln("Error:", err.Error()) c.PrintErrln(c.ErrPrefix(), err.Error())
c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath()) c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath())
} }
return c, err return c, err
@ -1085,7 +1107,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
// If root command has SilenceErrors flagged, // If root command has SilenceErrors flagged,
// all subcommands should respect it // all subcommands should respect it
if !cmd.SilenceErrors && !c.SilenceErrors { if !cmd.SilenceErrors && !c.SilenceErrors {
c.PrintErrln("Error:", err.Error()) c.PrintErrln(cmd.ErrPrefix(), err.Error())
} }
// If root command has SilenceUsage flagged, // If root command has SilenceUsage flagged,

View file

@ -1099,6 +1099,39 @@ func TestShorthandVersionTemplate(t *testing.T) {
checkStringContains(t, output, "customized version: 1.0.0") checkStringContains(t, output, "customized version: 1.0.0")
} }
func TestRootErrPrefixExecutedOnSubcommand(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
rootCmd.SetErrPrefix("root error prefix:")
rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
output, err := executeCommand(rootCmd, "sub", "--unknown-flag")
if err == nil {
t.Errorf("Expected error")
}
checkStringContains(t, output, "root error prefix: unknown flag: --unknown-flag")
}
func TestRootAndSubErrPrefix(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
subCmd := &Command{Use: "sub", Run: emptyRun}
rootCmd.AddCommand(subCmd)
rootCmd.SetErrPrefix("root error prefix:")
subCmd.SetErrPrefix("sub error prefix:")
if output, err := executeCommand(rootCmd, "--unknown-root-flag"); err == nil {
t.Errorf("Expected error")
} else {
checkStringContains(t, output, "root error prefix: unknown flag: --unknown-root-flag")
}
if output, err := executeCommand(rootCmd, "sub", "--unknown-sub-flag"); err == nil {
t.Errorf("Expected error")
} else {
checkStringContains(t, output, "sub error prefix: unknown flag: --unknown-sub-flag")
}
}
func TestVersionFlagExecutedOnSubcommand(t *testing.T) { func TestVersionFlagExecutedOnSubcommand(t *testing.T) {
rootCmd := &Command{Use: "root", Version: "1.0.0"} rootCmd := &Command{Use: "root", Version: "1.0.0"}
rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun}) rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})

View file

@ -150,6 +150,33 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman
return nil return nil
} }
// GetFlagCompletion returns the completion function for the given flag, if available.
func (c *Command) GetFlagCompletion(flag *pflag.Flag) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) {
c.initializeCompletionStorage()
c.flagCompletionMutex.RLock()
defer c.flagCompletionMutex.RUnlock()
completionFunc, exists := c.flagCompletionFunctions[flag]
if completionFunc != nil && exists {
return completionFunc, exists
}
// If not found on the current, walk up the command tree.
return c.Parent().GetFlagCompletion(flag)
}
// GetFlagCompletionByName returns the completion function for the given flag in the command by name, if available.
func (c *Command) GetFlagCompletionByName(flagName string) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) {
flag := c.Flags().Lookup(flagName)
if flag == nil {
return nil, false
}
return c.GetFlagCompletion(flag)
}
// initializeCompletionStorage is (and should be) called in all // initializeCompletionStorage is (and should be) called in all
// functions that make use of the command's flag completion functions. // functions that make use of the command's flag completion functions.
func (c *Command) initializeCompletionStorage() { func (c *Command) initializeCompletionStorage() {

View file

@ -596,6 +596,12 @@ Running an application with the '--version' flag will print the version to stdou
the version template. The template can be customized using the the version template. The template can be customized using the
`cmd.SetVersionTemplate(s string)` function. `cmd.SetVersionTemplate(s string)` function.
## Error Message Prefix
Cobra prints an error message when receiving a non-nil error value.
The default error message is `Error: <error contents>`.
The Prefix, `Error:` can be customized using the `cmd.SetErrPrefix(s string)` function.
## PreRun and PostRun Hooks ## PreRun and PostRun Hooks
It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: