Compare commits

..

No commits in common. "78bfc837fe358c750faa7e7f0a8016b300044d58" and "bd914e58d69d65e494b45bdb40e90ca816b92fcc" have entirely different histories.

20 changed files with 72 additions and 68 deletions

View File

@ -67,7 +67,6 @@ jobs:
- 20 - 20
- 21 - 21
- 22 - 22
- 23
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

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

View File

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

View File

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

View File

@ -2777,7 +2777,7 @@ func TestFind(t *testing.T) {
func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) { func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) {
testCases := [][]string{ 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", "--unknown", "child", "--bar"},
{"--namespace", "foo", "child", "--unknown", "--bar"}, {"--namespace", "foo", "child", "--unknown", "--bar"},
{"--namespace", "foo", "child", "--bar", "--unknown"}, {"--namespace", "foo", "child", "--bar", "--unknown"},

View File

@ -298,7 +298,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
} }
if err != nil { if err != nil {
// Unable to find the real command. E.g., <program> someInvalidCmd <TAB> // 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 finalCmd.ctx = c.ctx
@ -401,9 +401,8 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
doCompleteFlags := func(flag *pflag.Flag) { doCompleteFlags := func(flag *pflag.Flag) {
if !flag.Changed || if !flag.Changed ||
strings.Contains(flag.Value.Type(), "Slice") || strings.Contains(flag.Value.Type(), "Slice") ||
strings.Contains(flag.Value.Type(), "Array") || 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 or Slice)
// 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 // we suggest it as a completion
completions = append(completions, getFlagNameCompletions(flag, toComplete)...) 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 = &now
} }
header.date = header.Date.Format("Jan 2006") header.date = (*header.Date).Format("Jan 2006")
if header.Source == "" && !disableAutoGen { if header.Source == "" && !disableAutoGen {
header.Source = "Auto generated by spf13/cobra" header.Source = "Auto generated by spf13/cobra"
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,9 +23,9 @@ import (
) )
const ( const (
requiredAsGroupAnnotation = "cobra_annotation_required_if_others_set" requiredAsGroup = "cobra_annotation_required_if_others_set"
oneRequiredAnnotation = "cobra_annotation_one_required" oneRequired = "cobra_annotation_one_required"
mutuallyExclusiveAnnotation = "cobra_annotation_mutually_exclusive" mutuallyExclusive = "cobra_annotation_mutually_exclusive"
) )
// MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors // MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors
@ -37,7 +37,7 @@ func (c *Command) MarkFlagsRequiredTogether(flagNames ...string) {
if f == nil { if f == nil {
panic(fmt.Sprintf("Failed to find flag %q and mark it as being required in a flag group", v)) 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, requiredAsGroupAnnotation, append(f.Annotations[requiredAsGroupAnnotation], strings.Join(flagNames, " "))); err != nil { if err := c.Flags().SetAnnotation(v, requiredAsGroup, append(f.Annotations[requiredAsGroup], strings.Join(flagNames, " "))); err != nil {
// Only errs if the flag isn't found. // Only errs if the flag isn't found.
panic(err) panic(err)
} }
@ -53,7 +53,7 @@ func (c *Command) MarkFlagsOneRequired(flagNames ...string) {
if f == nil { if f == nil {
panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a one-required flag group", v)) 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, oneRequiredAnnotation, append(f.Annotations[oneRequiredAnnotation], strings.Join(flagNames, " "))); err != nil { if err := c.Flags().SetAnnotation(v, oneRequired, append(f.Annotations[oneRequired], strings.Join(flagNames, " "))); err != nil {
// Only errs if the flag isn't found. // Only errs if the flag isn't found.
panic(err) 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)) 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. // 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, mutuallyExclusiveAnnotation, append(f.Annotations[mutuallyExclusiveAnnotation], strings.Join(flagNames, " "))); err != nil { if err := c.Flags().SetAnnotation(v, mutuallyExclusive, append(f.Annotations[mutuallyExclusive], strings.Join(flagNames, " "))); err != nil {
panic(err) panic(err)
} }
} }
@ -91,9 +91,9 @@ func (c *Command) ValidateFlagGroups() error {
oneRequiredGroupStatus := map[string]map[string]bool{} oneRequiredGroupStatus := map[string]map[string]bool{}
mutuallyExclusiveGroupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
flags.VisitAll(func(pflag *flag.Flag) { flags.VisitAll(func(pflag *flag.Flag) {
processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus) processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus) processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, mutuallyExclusiveGroupStatus) processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
}) })
if err := validateRequiredFlagGroups(groupStatus); err != nil { if err := validateRequiredFlagGroups(groupStatus); err != nil {
@ -232,9 +232,9 @@ func (c *Command) enforceFlagGroupsForCompletion() {
oneRequiredGroupStatus := map[string]map[string]bool{} oneRequiredGroupStatus := map[string]map[string]bool{}
mutuallyExclusiveGroupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
c.Flags().VisitAll(func(pflag *flag.Flag) { c.Flags().VisitAll(func(pflag *flag.Flag) {
processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus) processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus) processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, mutuallyExclusiveGroupStatus) processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
}) })
// If a flag that is part of a group is present, we make all the other flags // 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 go 1.15
require ( require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 github.com/cpuguy83/go-md2man/v2 v2.0.3
github.com/inconshreveable/mousetrap v1.1.0 github.com/inconshreveable/mousetrap v1.1.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1

4
go.sum
View File

@ -1,5 +1,5 @@
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 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) { func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
// Variables should not contain a '-' or ':' character // Variables should not contain a '-' or ':' character
nameForVar := name nameForVar := name
nameForVar = strings.ReplaceAll(nameForVar, "-", "_") nameForVar = strings.Replace(nameForVar, "-", "_", -1)
nameForVar = strings.ReplaceAll(nameForVar, ":", "_") nameForVar = strings.Replace(nameForVar, ":", "_", -1)
compCmd := ShellCompRequestCmd compCmd := ShellCompRequestCmd
if !includeDesc { if !includeDesc {

View File

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

View File

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

View File

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

View File

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