From 4955da7c11ea14678cbca3a4016bd4b0b1c51f39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:53:34 +0000 Subject: [PATCH 1/5] build(deps): bump golangci/golangci-lint-action from 3.6.0 to 3.7.0 (#2021) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 498e3bec..3a365c85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v3 - - uses: golangci/golangci-lint-action@v3.6.0 + - uses: golangci/golangci-lint-action@v3.7.0 with: version: latest args: --verbose From 285460dca6152bb86994fa4a9659c24ca4060e2f Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral <38422348+umarcor@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:04:59 +0200 Subject: [PATCH 2/5] command: temporarily disable G602 due to securego/gosec#1005 (#2022) --- command.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 01f7c6f1..e4945ab8 100644 --- a/command.go +++ b/command.go @@ -752,7 +752,9 @@ func (c *Command) findNext(next string) *Command { } 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 From c5dacb3ea4512e6766f86907dfa7014ceb9cb041 Mon Sep 17 00:00:00 2001 From: Nuno Adrego <55922671+nunoadrego@users.noreply.github.com> Date: Fri, 8 Sep 2023 01:30:51 +0100 Subject: [PATCH 3/5] ci: test golang 1.21 (#2024) --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3a365c85..6b4c165d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,7 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: '^1.20' + go-version: '^1.21' check-latest: true cache: true @@ -67,6 +67,7 @@ jobs: - 18 - 19 - 20 + - 21 name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x' runs-on: ${{ matrix.platform }}-latest steps: From 0c72800b8dba637092b57a955ecee75949e79a73 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Sat, 9 Sep 2023 02:29:06 +0900 Subject: [PATCH 4/5] Customizable error message prefix (#2023) --- command.go | 24 ++++++++++++++++++++++-- command_test.go | 33 +++++++++++++++++++++++++++++++++ site/content/user_guide.md | 6 ++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/command.go b/command.go index e4945ab8..6866f7d0 100644 --- a/command.go +++ b/command.go @@ -181,6 +181,9 @@ type Command struct { // versionTemplate is the version template defined by user. 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 io.Reader // outWriter is a writer defined by the user that replaces stdout @@ -346,6 +349,11 @@ func (c *Command) SetVersionTemplate(s string) { 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. // The user should not have a cyclic dependency on commands. func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) { @@ -595,6 +603,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 { flag := fs.Lookup(name) if flag == nil { @@ -1050,7 +1070,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { c = cmd } if !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(c.ErrPrefix(), err.Error()) c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath()) } return c, err @@ -1079,7 +1099,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // If root command has SilenceErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(cmd.ErrPrefix(), err.Error()) } // If root command has SilenceUsage flagged, diff --git a/command_test.go b/command_test.go index b0f5e860..4afb7f7b 100644 --- a/command_test.go +++ b/command_test.go @@ -1099,6 +1099,39 @@ func TestShorthandVersionTemplate(t *testing.T) { 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) { rootCmd := &Command{Use: "root", Version: "1.0.0"} rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun}) diff --git a/site/content/user_guide.md b/site/content/user_guide.md index 56e8e44a..93daadf5 100644 --- a/site/content/user_guide.md +++ b/site/content/user_guide.md @@ -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 `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: `. +The Prefix, `Error:` can be customized using the `cmd.SetErrPrefix(s string)` function. + ## 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: From bd4d1655f6655f690f45d281fe698dacb9c7d750 Mon Sep 17 00:00:00 2001 From: Alexandru-Claudius Virtopeanu Date: Tue, 26 Sep 2023 03:04:25 +0300 Subject: [PATCH 5/5] feat: add getters for flag completions (#1943) --- completions.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/completions.go b/completions.go index ee38c4d0..55a9f62c 100644 --- a/completions.go +++ b/completions.go @@ -145,6 +145,25 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman return nil } +// GetFlagCompletion returns the completion function for the given flag, if available. +func GetFlagCompletion(flag *pflag.Flag) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) { + flagCompletionMutex.RLock() + defer flagCompletionMutex.RUnlock() + + completionFunc, exists := flagCompletionFunctions[flag] + return completionFunc, exists +} + +// 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 GetFlagCompletion(flag) +} + // Returns a string listing the different directive enabled in the specified parameter func (d ShellCompDirective) string() string { var directives []string