Compare commits

...

10 Commits

Author SHA1 Message Date
Nir Soffer
78bfc837fe
Test also with go 1.23 (#2182)
Some checks are pending
Test / lic-headers (push) Waiting to run
Test / golangci-lint (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (17, macOS) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (17, ubuntu) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (18, macOS) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (18, ubuntu) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (19, macOS) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (19, ubuntu) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (20, macOS) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (20, ubuntu) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (21, macOS) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (21, ubuntu) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (22, macOS) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (22, ubuntu) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (23, macOS) (push) Waiting to run
Test / ${{ matrix.platform }} | 1.${{ matrix.go }}.x (23, ubuntu) (push) Waiting to run
Test / MINGW64 (push) Waiting to run
2024-08-24 19:02:44 -04:00
Nir Soffer
511af59cb3
Replace deprecated ioutil usage (#2181)
Fixing golangci-lint errors[1]:

    Error: SA1019: "io/ioutil" has been deprecated since Go 1.19: As of
    Go 1.16, the same functionality is now provided by package [io] or
    package [os], and those implementations should be preferred in new
    code. See the specific function documentation for details.
    (staticcheck)

[1] https://github.com/spf13/cobra/actions/runs/10535452454/job/29194442289?pr=2180
2024-08-24 07:05:26 -04:00
Gabe Cook
756ba6dad6
fix(completions): Complete map flags multiple times (#2174) 2024-07-28 12:18:07 -04:00
Sebastiaan van Stijn
371ae25d2c
Fix deprecation comment for Command.SetOutput (#2172)
Deprecation comments should be at the start of a paragraph [1], and because
of that have a whitespace above them [2];

> To signal that an identifier should not be used, add a paragraph to its
> doc comment that begins with Deprecated: followed by some information
> about the deprecation (...)

With the whitespace missing, some tools, including pkg.go.dev [3] don't
detect it to be deprecated.

[1]: https://go.dev/wiki/Deprecated
[2]: https://go.dev/doc/comment#paragraphs
[3]: https://pkg.go.dev/github.com/spf13/cobra@v1.8.1#Command.SetOutput

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-16 19:36:29 -04:00
Ville Skyttä
e94f6d0dd9
Address golangci-lint deprecation warnings, enable some more linters (#2152)
* Address golangci-lint linter deprecation warnings

1.59.0 outputs:

WARN [lintersdb] The name "gas" is deprecated. The linter has been renamed to: gosec.
WARN [lintersdb] The linter named "megacheck" is deprecated. It has been split into: gosimple, staticcheck, unused.

* Enable some more linters, address finding
2024-06-01 06:31:11 -04:00
Nir Soffer
8003b74a10
Remove fully inactivated linters (#2148)
* Remove fully inactivated linters

Currently golangci-lint fails with these errors:

ERRO [linters_context] golint: This linter is fully inactivated: it will not produce any reports.
ERRO [linters_context] interfacer: This linter is fully inactivated: it will not produce any reports.
ERRO [linters_context] maligned: This linter is fully inactivated: it will not produce any reports.

I could not find any docs explaining what "fully inactivated" mean, but
based this PR[1] it seems that these linters do nothing now. Removing
the linters fixes this issue without changing linting, as they did not
produce any report.

Looking in the linters docs[2] I did not find a replacement for
"interfacer" and "malinged" linters. "stylecheck" seems to be a
replacement for "golint", but we need to fix the code to enable it.

[1] https://github.com/golangci/golangci-lint/pull/4436
[2] https://golangci-lint.run/usage/linters/

* Add stylecheck linter, replacement for golint

This revealed 2 capitalized error messages.

https://golangci-lint.run/usage/linters/#stylecheck
2024-05-18 22:12:02 -04:00
Nir Soffer
5c2c1d627d
Consistent annotation names (#2140)
Add `Annotation` suffix to the private annotations to allow nicer code
using the constants.

For example one can use the current annotation names as a temporary
variable instead of unclear shortcut. Instead of this:

    rag := flagsFromAnnotation(c, f, requiredAsGroup)
    me := flagsFromAnnotation(c, f, mutuallyExclusive)
    or := flagsFromAnnotation(c, f, oneRequired)

We can use now:

    requiredAsGrop := flagsFromAnnotation(c, f, requiredAsGroupAnnotation)
    mutuallyExclusive := flagsFromAnnotation(c, f, mutuallyExclusiveAnnotation)
    oneRequired := flagsFromAnnotation(c, f, oneRequiredAnnotation)

Example taken from #2105.
2024-05-18 09:41:31 -04:00
dependabot[bot]
5a1acea321
build(deps): bump github.com/cpuguy83/go-md2man/v2 from 2.0.3 to 2.0.4 (#2127) 2024-04-13 02:21:03 +00:00
Xinwei Xiong
0fc86c2ffd
docs: update user guide (#2128) 2024-04-08 06:47:35 -04:00
Ville Skyttä
6b5f577ebc
More linting (#2099)
* Address gocritic findings, enable it

* Enable gosimple, no new findings to address
2024-04-01 08:42:08 -04:00
20 changed files with 68 additions and 72 deletions

View File

@ -67,6 +67,7 @@ jobs:
- 20
- 21
- 22
- 23
name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x'
runs-on: ${{ matrix.platform }}-latest
steps:

View File

@ -26,33 +26,28 @@ linters:
- errcheck
#- exhaustive
#- funlen
- gas
#- gochecknoinits
- goconst
#- gocritic
- gocritic
#- gocyclo
#- gofmt
- gofmt
- goimports
- golint
#- gomnd
#- goprintffuncname
#- gosec
#- gosimple
- gosec
- gosimple
- govet
- ineffassign
- interfacer
#- lll
- maligned
- megacheck
#- misspell
- misspell
#- nakedret
#- noctx
#- nolintlint
- nolintlint
#- rowserrcheck
#- scopelint
#- staticcheck
- staticcheck
#- structcheck ! deprecated since v1.49.0; replaced by 'unused'
#- stylecheck
- stylecheck
#- typecheck
- unconvert
#- unparam

View File

@ -597,19 +597,16 @@ func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
if nonCompletableFlag(flag) {
return
}
for key := range flag.Annotations {
switch key {
case BashCompOneRequiredFlag:
format := " must_have_one_flag+=(\"--%s"
if flag.Value.Type() != "bool" {
format += "="
}
format += cbn
WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))
if _, ok := flag.Annotations[BashCompOneRequiredFlag]; ok {
format := " must_have_one_flag+=(\"--%s"
if flag.Value.Type() != "bool" {
format += "="
}
format += cbn
WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))
if len(flag.Shorthand) > 0 {
WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
}
if len(flag.Shorthand) > 0 {
WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
}
}
})

View File

@ -281,6 +281,7 @@ func (c *Command) SetArgs(a []string) {
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
//
// Deprecated: Use SetOut and/or SetErr instead
func (c *Command) SetOutput(output io.Writer) {
c.outWriter = output
@ -875,7 +876,7 @@ func (c *Command) ArgsLenAtDash() int {
func (c *Command) execute(a []string) (err error) {
if c == nil {
return fmt.Errorf("Called Execute() on a nil Command")
return fmt.Errorf("called Execute() on a nil Command")
}
if len(c.Deprecated) > 0 {
@ -1460,7 +1461,6 @@ func (c *Command) UseLine() string {
// DebugFlags used to determine which flags have been assigned to which commands
// and which persist.
// nolint:goconst
func (c *Command) DebugFlags() {
c.Println("DebugFlags called on", c.Name())
var debugflags func(*Command)

View File

@ -2777,7 +2777,7 @@ func TestFind(t *testing.T) {
func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) {
testCases := [][]string{
//{"--unknown", "--namespace", "foo", "child", "--bar"}, // FIXME: This test case fails, returning the error `unknown command "foo" for "root"` instead of the expected error `unknown flag: --unknown`
// {"--unknown", "--namespace", "foo", "child", "--bar"}, // FIXME: This test case fails, returning the error `unknown command "foo" for "root"` instead of the expected error `unknown flag: --unknown`
{"--namespace", "foo", "--unknown", "child", "--bar"},
{"--namespace", "foo", "child", "--unknown", "--bar"},
{"--namespace", "foo", "child", "--bar", "--unknown"},

View File

@ -298,7 +298,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
}
if err != nil {
// Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs)
return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs)
}
finalCmd.ctx = c.ctx
@ -401,8 +401,9 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
doCompleteFlags := func(flag *pflag.Flag) {
if !flag.Changed ||
strings.Contains(flag.Value.Type(), "Slice") ||
strings.Contains(flag.Value.Type(), "Array") {
// If the flag is not already present, or if it can be specified multiple times (Array or Slice)
strings.Contains(flag.Value.Type(), "Array") ||
strings.HasPrefix(flag.Value.Type(), "stringTo") {
// If the flag is not already present, or if it can be specified multiple times (Array, Slice, or stringTo)
// we suggest it as a completion
completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
}

View File

@ -133,7 +133,7 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
}
header.Date = &now
}
header.date = (*header.Date).Format("Jan 2006")
header.date = header.Date.Format("Jan 2006")
if header.Source == "" && !disableAutoGen {
header.Source = "Auto generated by spf13/cobra"
}

View File

@ -18,7 +18,6 @@ import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@ -168,7 +167,7 @@ func TestManPrintFlagsHidesShortDeprecated(t *testing.T) {
func TestGenManTree(t *testing.T) {
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
header := &GenManHeader{Section: "2"}
tmpdir, err := ioutil.TempDir("", "test-gen-man-tree")
tmpdir, err := os.MkdirTemp("", "test-gen-man-tree")
if err != nil {
t.Fatalf("Failed to create tmpdir: %s", err.Error())
}
@ -219,7 +218,7 @@ func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error {
}
func BenchmarkGenManToFile(b *testing.B) {
file, err := ioutil.TempFile("", "")
file, err := os.CreateTemp("", "")
if err != nil {
b.Fatal(err)
}

View File

@ -16,7 +16,6 @@ package doc
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -94,7 +93,7 @@ func TestGenMdNoTag(t *testing.T) {
func TestGenMdTree(t *testing.T) {
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
tmpdir, err := ioutil.TempDir("", "test-gen-md-tree")
tmpdir, err := os.MkdirTemp("", "test-gen-md-tree")
if err != nil {
t.Fatalf("Failed to create tmpdir: %v", err)
}
@ -110,7 +109,7 @@ func TestGenMdTree(t *testing.T) {
}
func BenchmarkGenMarkdownToFile(b *testing.B) {
file, err := ioutil.TempFile("", "")
file, err := os.CreateTemp("", "")
if err != nil {
b.Fatal(err)
}

View File

@ -16,7 +16,6 @@ package doc
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -81,7 +80,7 @@ func TestGenRSTNoTag(t *testing.T) {
func TestGenRSTTree(t *testing.T) {
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
tmpdir, err := ioutil.TempDir("", "test-gen-rst-tree")
tmpdir, err := os.MkdirTemp("", "test-gen-rst-tree")
if err != nil {
t.Fatalf("Failed to create tmpdir: %s", err.Error())
}
@ -97,7 +96,7 @@ func TestGenRSTTree(t *testing.T) {
}
func BenchmarkGenReSTToFile(b *testing.B) {
file, err := ioutil.TempFile("", "")
file, err := os.CreateTemp("", "")
if err != nil {
b.Fatal(err)
}

View File

@ -40,7 +40,7 @@ func hasSeeAlso(cmd *cobra.Command) bool {
// that do not contain \n.
func forceMultiLine(s string) string {
if len(s) > 60 && !strings.Contains(s, "\n") {
s = s + "\n"
s += "\n"
}
return s
}

View File

@ -17,7 +17,6 @@ package doc
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -58,7 +57,7 @@ func TestGenYamlNoTag(t *testing.T) {
func TestGenYamlTree(t *testing.T) {
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
tmpdir, err := ioutil.TempDir("", "test-gen-yaml-tree")
tmpdir, err := os.MkdirTemp("", "test-gen-yaml-tree")
if err != nil {
t.Fatalf("Failed to create tmpdir: %s", err.Error())
}
@ -85,7 +84,7 @@ func TestGenYamlDocRunnable(t *testing.T) {
}
func BenchmarkGenYamlToFile(b *testing.B) {
file, err := ioutil.TempFile("", "")
file, err := os.CreateTemp("", "")
if err != nil {
b.Fatal(err)
}

View File

@ -23,9 +23,9 @@ import (
)
const (
requiredAsGroup = "cobra_annotation_required_if_others_set"
oneRequired = "cobra_annotation_one_required"
mutuallyExclusive = "cobra_annotation_mutually_exclusive"
requiredAsGroupAnnotation = "cobra_annotation_required_if_others_set"
oneRequiredAnnotation = "cobra_annotation_one_required"
mutuallyExclusiveAnnotation = "cobra_annotation_mutually_exclusive"
)
// MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors
@ -37,7 +37,7 @@ func (c *Command) MarkFlagsRequiredTogether(flagNames ...string) {
if f == nil {
panic(fmt.Sprintf("Failed to find flag %q and mark it as being required in a flag group", v))
}
if err := c.Flags().SetAnnotation(v, requiredAsGroup, append(f.Annotations[requiredAsGroup], strings.Join(flagNames, " "))); err != nil {
if err := c.Flags().SetAnnotation(v, requiredAsGroupAnnotation, append(f.Annotations[requiredAsGroupAnnotation], strings.Join(flagNames, " "))); err != nil {
// Only errs if the flag isn't found.
panic(err)
}
@ -53,7 +53,7 @@ func (c *Command) MarkFlagsOneRequired(flagNames ...string) {
if f == nil {
panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a one-required flag group", v))
}
if err := c.Flags().SetAnnotation(v, oneRequired, append(f.Annotations[oneRequired], strings.Join(flagNames, " "))); err != nil {
if err := c.Flags().SetAnnotation(v, oneRequiredAnnotation, append(f.Annotations[oneRequiredAnnotation], strings.Join(flagNames, " "))); err != nil {
// Only errs if the flag isn't found.
panic(err)
}
@ -70,7 +70,7 @@ func (c *Command) MarkFlagsMutuallyExclusive(flagNames ...string) {
panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a mutually exclusive flag group", v))
}
// Each time this is called is a single new entry; this allows it to be a member of multiple groups if needed.
if err := c.Flags().SetAnnotation(v, mutuallyExclusive, append(f.Annotations[mutuallyExclusive], strings.Join(flagNames, " "))); err != nil {
if err := c.Flags().SetAnnotation(v, mutuallyExclusiveAnnotation, append(f.Annotations[mutuallyExclusiveAnnotation], strings.Join(flagNames, " "))); err != nil {
panic(err)
}
}
@ -91,9 +91,9 @@ func (c *Command) ValidateFlagGroups() error {
oneRequiredGroupStatus := map[string]map[string]bool{}
mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
flags.VisitAll(func(pflag *flag.Flag) {
processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus)
processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus)
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, mutuallyExclusiveGroupStatus)
})
if err := validateRequiredFlagGroups(groupStatus); err != nil {
@ -232,9 +232,9 @@ func (c *Command) enforceFlagGroupsForCompletion() {
oneRequiredGroupStatus := map[string]map[string]bool{}
mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
c.Flags().VisitAll(func(pflag *flag.Flag) {
processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus)
processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus)
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, mutuallyExclusiveGroupStatus)
})
// If a flag that is part of a group is present, we make all the other flags

2
go.mod
View File

@ -3,7 +3,7 @@ module github.com/spf13/cobra
go 1.15
require (
github.com/cpuguy83/go-md2man/v2 v2.0.3
github.com/cpuguy83/go-md2man/v2 v2.0.4
github.com/inconshreveable/mousetrap v1.1.0
github.com/spf13/pflag v1.0.5
gopkg.in/yaml.v3 v3.0.1

4
go.sum
View File

@ -1,5 +1,5 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=

View File

@ -28,8 +28,8 @@ import (
func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
// Variables should not contain a '-' or ':' character
nameForVar := name
nameForVar = strings.Replace(nameForVar, "-", "_", -1)
nameForVar = strings.Replace(nameForVar, ":", "_", -1)
nameForVar = strings.ReplaceAll(nameForVar, "-", "_")
nameForVar = strings.ReplaceAll(nameForVar, ":", "_")
compCmd := ShellCompRequestCmd
if !includeDesc {

View File

@ -35,7 +35,7 @@ package main
import (
"log"
"io/ioutil"
"io"
"os"
"k8s.io/kubernetes/pkg/kubectl/cmd"
@ -45,7 +45,7 @@ import (
)
func main() {
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard)
err := doc.GenMarkdownTree(kubectl, "./")
if err != nil {
log.Fatal(err)

View File

@ -35,7 +35,7 @@ package main
import (
"log"
"io/ioutil"
"io"
"os"
"k8s.io/kubernetes/pkg/kubectl/cmd"
@ -45,7 +45,7 @@ import (
)
func main() {
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard)
err := doc.GenReSTTree(kubectl, "./")
if err != nil {
log.Fatal(err)

View File

@ -34,7 +34,7 @@ This program can actually generate docs for the kubectl command in the kubernete
package main
import (
"io/ioutil"
"io"
"log"
"os"
@ -45,7 +45,7 @@ import (
)
func main() {
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard)
err := doc.GenYamlTree(kubectl, "./")
if err != nil {
log.Fatal(err)

View File

@ -3,7 +3,7 @@
While you are welcome to provide your own organization, typically a Cobra-based
application will follow the following organizational structure:
```
```test
▾ appName/
▾ cmd/
add.go
@ -301,6 +301,7 @@ command := cobra.Command{
### Bind Flags with Config
You can also bind your flags with [viper](https://github.com/spf13/viper):
```go
var author string
@ -320,12 +321,14 @@ More in [viper documentation](https://github.com/spf13/viper#working-with-flags)
Flags are optional by default. If instead you wish your command to report an error
when a flag has not been set, mark it as required:
```go
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")
```
Or, for persistent flags:
```go
rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkPersistentFlagRequired("region")
@ -335,6 +338,7 @@ rootCmd.MarkPersistentFlagRequired("region")
If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then
Cobra can enforce that requirement:
```go
rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
@ -343,6 +347,7 @@ rootCmd.MarkFlagsRequiredTogether("username", "password")
You can also prevent different flags from being provided together if they represent mutually
exclusive options such as specifying an output format as either `--json` or `--yaml` but never both:
```go
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
@ -351,6 +356,7 @@ rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")
If you want to require at least one flag from a group to be present, you can use `MarkFlagsOneRequired`.
This can be combined with `MarkFlagsMutuallyExclusive` to enforce exactly one flag from a given group:
```go
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
@ -428,7 +434,7 @@ by not providing a 'Run' for the 'rootCmd'.
We have only defined one flag for a single command.
More documentation about flags is available at https://github.com/spf13/pflag
More documentation about flags is available at https://github.com/spf13/pflag.
```go
package main
@ -722,7 +728,7 @@ command.SuggestionsMinimumDistance = 1
You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but make sense in your set of commands but for which
you don't want aliases. Example:
```
```bash
$ kubectl remove
Error: unknown command "remove" for "kubectl"
@ -787,7 +793,7 @@ func main() {
Example run as a kubectl plugin:
```
```bash
$ kubectl myplugin
Usage:
kubectl myplugin [command]