mirror of
https://github.com/spf13/cobra
synced 2024-11-24 14:47:12 +00:00
Merge branch 'main' into quote_bash_completions
This commit is contained in:
commit
ec43e79555
25 changed files with 229 additions and 171 deletions
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
|
@ -43,11 +43,11 @@ jobs:
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '^1.21'
|
go-version: '^1.22'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- uses: golangci/golangci-lint-action@v3.7.0
|
- uses: golangci/golangci-lint-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: --verbose
|
args: --verbose
|
||||||
|
@ -66,6 +66,8 @@ jobs:
|
||||||
- 19
|
- 19
|
||||||
- 20
|
- 20
|
||||||
- 21
|
- 21
|
||||||
|
- 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:
|
||||||
|
@ -108,7 +110,7 @@ jobs:
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/go/pkg/mod
|
path: ~/go/pkg/mod
|
||||||
key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
|
||||||
|
|
|
@ -26,33 +26,28 @@ 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
|
||||||
- maligned
|
- misspell
|
||||||
- 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
|
||||||
|
|
|
@ -105,7 +105,7 @@ go install github.com/spf13/cobra-cli@latest
|
||||||
|
|
||||||
For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)
|
For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)
|
||||||
|
|
||||||
For complete details on using the Cobra library, please read the [The Cobra User Guide](site/content/user_guide.md).
|
For complete details on using the Cobra library, please read [The Cobra User Guide](site/content/user_guide.md).
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
|
|
@ -597,19 +597,16 @@ func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
|
||||||
if nonCompletableFlag(flag) {
|
if nonCompletableFlag(flag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for key := range flag.Annotations {
|
if _, ok := flag.Annotations[BashCompOneRequiredFlag]; ok {
|
||||||
switch key {
|
format := " must_have_one_flag+=(\"--%s"
|
||||||
case BashCompOneRequiredFlag:
|
if flag.Value.Type() != "bool" {
|
||||||
format := " must_have_one_flag+=(\"--%s"
|
format += "="
|
||||||
if flag.Value.Type() != "bool" {
|
}
|
||||||
format += "="
|
format += cbn
|
||||||
}
|
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))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
36
command.go
36
command.go
|
@ -281,6 +281,7 @@ 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
|
||||||
|
@ -606,7 +607,7 @@ func (c *Command) VersionTemplate() string {
|
||||||
if c.HasParent() {
|
if c.HasParent() {
|
||||||
return c.parent.VersionTemplate()
|
return c.parent.VersionTemplate()
|
||||||
}
|
}
|
||||||
return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
|
return `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,7 +876,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 {
|
||||||
|
@ -1189,7 +1190,7 @@ func (c *Command) InitDefaultHelpFlag() {
|
||||||
c.mergePersistentFlags()
|
c.mergePersistentFlags()
|
||||||
if c.Flags().Lookup("help") == nil {
|
if c.Flags().Lookup("help") == nil {
|
||||||
usage := "help for "
|
usage := "help for "
|
||||||
name := c.displayName()
|
name := c.DisplayName()
|
||||||
if name == "" {
|
if name == "" {
|
||||||
usage += "this command"
|
usage += "this command"
|
||||||
} else {
|
} else {
|
||||||
|
@ -1215,7 +1216,7 @@ func (c *Command) InitDefaultVersionFlag() {
|
||||||
if c.Name() == "" {
|
if c.Name() == "" {
|
||||||
usage += "this command"
|
usage += "this command"
|
||||||
} else {
|
} else {
|
||||||
usage += c.Name()
|
usage += c.DisplayName()
|
||||||
}
|
}
|
||||||
if c.Flags().ShorthandLookup("v") == nil {
|
if c.Flags().ShorthandLookup("v") == nil {
|
||||||
c.Flags().BoolP("version", "v", false, usage)
|
c.Flags().BoolP("version", "v", false, usage)
|
||||||
|
@ -1239,7 +1240,7 @@ func (c *Command) InitDefaultHelpCmd() {
|
||||||
Use: "help [command]",
|
Use: "help [command]",
|
||||||
Short: "Help about any command",
|
Short: "Help about any command",
|
||||||
Long: `Help provides help for any command in the application.
|
Long: `Help provides help for any command in the application.
|
||||||
Simply type ` + c.displayName() + ` help [path to command] for full details.`,
|
Simply type ` + c.DisplayName() + ` help [path to command] for full details.`,
|
||||||
ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
||||||
var completions []string
|
var completions []string
|
||||||
cmd, _, e := c.Root().Find(args)
|
cmd, _, e := c.Root().Find(args)
|
||||||
|
@ -1430,10 +1431,12 @@ func (c *Command) CommandPath() string {
|
||||||
if c.HasParent() {
|
if c.HasParent() {
|
||||||
return c.Parent().CommandPath() + " " + c.Name()
|
return c.Parent().CommandPath() + " " + c.Name()
|
||||||
}
|
}
|
||||||
return c.displayName()
|
return c.DisplayName()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) displayName() string {
|
// DisplayName returns the name to display in help text. Returns command Name()
|
||||||
|
// If CommandDisplayNameAnnoation is not set
|
||||||
|
func (c *Command) DisplayName() string {
|
||||||
if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok {
|
if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok {
|
||||||
return displayName
|
return displayName
|
||||||
}
|
}
|
||||||
|
@ -1443,7 +1446,7 @@ func (c *Command) displayName() string {
|
||||||
// UseLine puts out the full usage for a given command (including parents).
|
// UseLine puts out the full usage for a given command (including parents).
|
||||||
func (c *Command) UseLine() string {
|
func (c *Command) UseLine() string {
|
||||||
var useline string
|
var useline string
|
||||||
use := strings.Replace(c.Use, c.Name(), c.displayName(), 1)
|
use := strings.Replace(c.Use, c.Name(), c.DisplayName(), 1)
|
||||||
if c.HasParent() {
|
if c.HasParent() {
|
||||||
useline = c.parent.CommandPath() + " " + use
|
useline = c.parent.CommandPath() + " " + use
|
||||||
} else {
|
} else {
|
||||||
|
@ -1460,7 +1463,6 @@ 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)
|
||||||
|
@ -1650,7 +1652,7 @@ func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) f
|
||||||
// to this command (local and persistent declared here and by all parents).
|
// to this command (local and persistent declared here and by all parents).
|
||||||
func (c *Command) Flags() *flag.FlagSet {
|
func (c *Command) Flags() *flag.FlagSet {
|
||||||
if c.flags == nil {
|
if c.flags == nil {
|
||||||
c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
if c.flagErrorBuf == nil {
|
if c.flagErrorBuf == nil {
|
||||||
c.flagErrorBuf = new(bytes.Buffer)
|
c.flagErrorBuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
|
@ -1665,7 +1667,7 @@ func (c *Command) Flags() *flag.FlagSet {
|
||||||
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
|
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
|
||||||
persistentFlags := c.PersistentFlags()
|
persistentFlags := c.PersistentFlags()
|
||||||
|
|
||||||
out := flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
out := flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
c.LocalFlags().VisitAll(func(f *flag.Flag) {
|
c.LocalFlags().VisitAll(func(f *flag.Flag) {
|
||||||
if persistentFlags.Lookup(f.Name) == nil {
|
if persistentFlags.Lookup(f.Name) == nil {
|
||||||
out.AddFlag(f)
|
out.AddFlag(f)
|
||||||
|
@ -1680,7 +1682,7 @@ func (c *Command) LocalFlags() *flag.FlagSet {
|
||||||
c.mergePersistentFlags()
|
c.mergePersistentFlags()
|
||||||
|
|
||||||
if c.lflags == nil {
|
if c.lflags == nil {
|
||||||
c.lflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
c.lflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
if c.flagErrorBuf == nil {
|
if c.flagErrorBuf == nil {
|
||||||
c.flagErrorBuf = new(bytes.Buffer)
|
c.flagErrorBuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
|
@ -1708,7 +1710,7 @@ func (c *Command) InheritedFlags() *flag.FlagSet {
|
||||||
c.mergePersistentFlags()
|
c.mergePersistentFlags()
|
||||||
|
|
||||||
if c.iflags == nil {
|
if c.iflags == nil {
|
||||||
c.iflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
c.iflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
if c.flagErrorBuf == nil {
|
if c.flagErrorBuf == nil {
|
||||||
c.flagErrorBuf = new(bytes.Buffer)
|
c.flagErrorBuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
|
@ -1737,7 +1739,7 @@ func (c *Command) NonInheritedFlags() *flag.FlagSet {
|
||||||
// PersistentFlags returns the persistent FlagSet specifically set in the current command.
|
// PersistentFlags returns the persistent FlagSet specifically set in the current command.
|
||||||
func (c *Command) PersistentFlags() *flag.FlagSet {
|
func (c *Command) PersistentFlags() *flag.FlagSet {
|
||||||
if c.pflags == nil {
|
if c.pflags == nil {
|
||||||
c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
if c.flagErrorBuf == nil {
|
if c.flagErrorBuf == nil {
|
||||||
c.flagErrorBuf = new(bytes.Buffer)
|
c.flagErrorBuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
|
@ -1750,9 +1752,9 @@ func (c *Command) PersistentFlags() *flag.FlagSet {
|
||||||
func (c *Command) ResetFlags() {
|
func (c *Command) ResetFlags() {
|
||||||
c.flagErrorBuf = new(bytes.Buffer)
|
c.flagErrorBuf = new(bytes.Buffer)
|
||||||
c.flagErrorBuf.Reset()
|
c.flagErrorBuf.Reset()
|
||||||
c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
c.flags.SetOutput(c.flagErrorBuf)
|
c.flags.SetOutput(c.flagErrorBuf)
|
||||||
c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
c.pflags.SetOutput(c.flagErrorBuf)
|
c.pflags.SetOutput(c.flagErrorBuf)
|
||||||
|
|
||||||
c.lflags = nil
|
c.lflags = nil
|
||||||
|
@ -1869,7 +1871,7 @@ func (c *Command) mergePersistentFlags() {
|
||||||
// If c.parentsPflags == nil, it makes new.
|
// If c.parentsPflags == nil, it makes new.
|
||||||
func (c *Command) updateParentsPflags() {
|
func (c *Command) updateParentsPflags() {
|
||||||
if c.parentsPflags == nil {
|
if c.parentsPflags == nil {
|
||||||
c.parentsPflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError)
|
c.parentsPflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
||||||
c.parentsPflags.SetOutput(c.flagErrorBuf)
|
c.parentsPflags.SetOutput(c.flagErrorBuf)
|
||||||
c.parentsPflags.SortFlags = false
|
c.parentsPflags.SortFlags = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -371,8 +371,9 @@ func TestAliasPrefixMatching(t *testing.T) {
|
||||||
// text should reflect the way we run the command.
|
// text should reflect the way we run the command.
|
||||||
func TestPlugin(t *testing.T) {
|
func TestPlugin(t *testing.T) {
|
||||||
cmd := &Command{
|
cmd := &Command{
|
||||||
Use: "kubectl-plugin",
|
Use: "kubectl-plugin",
|
||||||
Args: NoArgs,
|
Version: "1.0.0",
|
||||||
|
Args: NoArgs,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
CommandDisplayNameAnnotation: "kubectl plugin",
|
CommandDisplayNameAnnotation: "kubectl plugin",
|
||||||
},
|
},
|
||||||
|
@ -386,13 +387,15 @@ func TestPlugin(t *testing.T) {
|
||||||
|
|
||||||
checkStringContains(t, cmdHelp, "kubectl plugin [flags]")
|
checkStringContains(t, cmdHelp, "kubectl plugin [flags]")
|
||||||
checkStringContains(t, cmdHelp, "help for kubectl plugin")
|
checkStringContains(t, cmdHelp, "help for kubectl plugin")
|
||||||
|
checkStringContains(t, cmdHelp, "version for kubectl plugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPlugin checks usage as plugin with sub commands.
|
// TestPlugin checks usage as plugin with sub commands.
|
||||||
func TestPluginWithSubCommands(t *testing.T) {
|
func TestPluginWithSubCommands(t *testing.T) {
|
||||||
rootCmd := &Command{
|
rootCmd := &Command{
|
||||||
Use: "kubectl-plugin",
|
Use: "kubectl-plugin",
|
||||||
Args: NoArgs,
|
Version: "1.0.0",
|
||||||
|
Args: NoArgs,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
CommandDisplayNameAnnotation: "kubectl plugin",
|
CommandDisplayNameAnnotation: "kubectl plugin",
|
||||||
},
|
},
|
||||||
|
@ -408,6 +411,7 @@ func TestPluginWithSubCommands(t *testing.T) {
|
||||||
|
|
||||||
checkStringContains(t, rootHelp, "kubectl plugin [command]")
|
checkStringContains(t, rootHelp, "kubectl plugin [command]")
|
||||||
checkStringContains(t, rootHelp, "help for kubectl plugin")
|
checkStringContains(t, rootHelp, "help for kubectl plugin")
|
||||||
|
checkStringContains(t, rootHelp, "version for kubectl plugin")
|
||||||
checkStringContains(t, rootHelp, "kubectl plugin [command] --help")
|
checkStringContains(t, rootHelp, "kubectl plugin [command] --help")
|
||||||
|
|
||||||
childHelp, err := executeCommand(rootCmd, "sub", "-h")
|
childHelp, err := executeCommand(rootCmd, "sub", "-h")
|
||||||
|
@ -1090,6 +1094,24 @@ func TestVersionFlagExecuted(t *testing.T) {
|
||||||
checkStringContains(t, output, "root version 1.0.0")
|
checkStringContains(t, output, "root version 1.0.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVersionFlagExecutedDiplayName(t *testing.T) {
|
||||||
|
rootCmd := &Command{
|
||||||
|
Use: "kubectl-plugin",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Annotations: map[string]string{
|
||||||
|
CommandDisplayNameAnnotation: "kubectl plugin",
|
||||||
|
},
|
||||||
|
Run: emptyRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := executeCommand(rootCmd, "--version", "arg1")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkStringContains(t, output, "kubectl plugin version 1.0.0")
|
||||||
|
}
|
||||||
|
|
||||||
func TestVersionFlagExecutedWithNoName(t *testing.T) {
|
func TestVersionFlagExecutedWithNoName(t *testing.T) {
|
||||||
rootCmd := &Command{Version: "1.0.0", Run: emptyRun}
|
rootCmd := &Command{Version: "1.0.0", Run: emptyRun}
|
||||||
|
|
||||||
|
@ -2092,12 +2114,12 @@ func TestCommandPrintRedirection(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotErrBytes, err := ioutil.ReadAll(errBuff)
|
gotErrBytes, err := io.ReadAll(errBuff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotOutBytes, err := ioutil.ReadAll(outBuff)
|
gotOutBytes, err := io.ReadAll(outBuff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -2777,7 +2799,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"},
|
||||||
|
|
|
@ -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,8 +401,9 @@ 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") ||
|
||||||
// If the flag is not already present, or if it can be specified multiple times (Array or Slice)
|
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
|
// we suggest it as a completion
|
||||||
completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
|
completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -168,7 +167,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 := ioutil.TempDir("", "test-gen-man-tree")
|
tmpdir, err := os.MkdirTemp("", "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())
|
||||||
}
|
}
|
||||||
|
@ -219,7 +218,7 @@ func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkGenManToFile(b *testing.B) {
|
func BenchmarkGenManToFile(b *testing.B) {
|
||||||
file, err := ioutil.TempFile("", "")
|
file, err := os.CreateTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ func GenMarkdownTree(cmd *cobra.Command, dir string) error {
|
||||||
return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)
|
return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenMarkdownTreeCustom is the the same as GenMarkdownTree, but
|
// GenMarkdownTreeCustom is the same as GenMarkdownTree, but
|
||||||
// with custom filePrepender and linkHandler.
|
// with custom filePrepender and linkHandler.
|
||||||
func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
|
func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
|
||||||
for _, c := range cmd.Commands() {
|
for _, c := range cmd.Commands() {
|
||||||
|
|
|
@ -16,7 +16,6 @@ package doc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -94,7 +93,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 := ioutil.TempDir("", "test-gen-md-tree")
|
tmpdir, err := os.MkdirTemp("", "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)
|
||||||
}
|
}
|
||||||
|
@ -110,7 +109,7 @@ func TestGenMdTree(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkGenMarkdownToFile(b *testing.B) {
|
func BenchmarkGenMarkdownToFile(b *testing.B) {
|
||||||
file, err := ioutil.TempFile("", "")
|
file, err := os.CreateTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ func GenReSTTree(cmd *cobra.Command, dir string) error {
|
||||||
return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler)
|
return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenReSTTreeCustom is the the same as GenReSTTree, but
|
// GenReSTTreeCustom is the same as GenReSTTree, but
|
||||||
// with custom filePrepender and linkHandler.
|
// with custom filePrepender and linkHandler.
|
||||||
func GenReSTTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error {
|
func GenReSTTreeCustom(cmd *cobra.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error {
|
||||||
for _, c := range cmd.Commands() {
|
for _, c := range cmd.Commands() {
|
||||||
|
|
|
@ -16,7 +16,6 @@ package doc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -81,7 +80,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 := ioutil.TempDir("", "test-gen-rst-tree")
|
tmpdir, err := os.MkdirTemp("", "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())
|
||||||
}
|
}
|
||||||
|
@ -97,7 +96,7 @@ func TestGenRSTTree(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkGenReSTToFile(b *testing.B) {
|
func BenchmarkGenReSTToFile(b *testing.B) {
|
||||||
file, err := ioutil.TempFile("", "")
|
file, err := os.CreateTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = s + "\n"
|
s += "\n"
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package doc
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -58,7 +57,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 := ioutil.TempDir("", "test-gen-yaml-tree")
|
tmpdir, err := os.MkdirTemp("", "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())
|
||||||
}
|
}
|
||||||
|
@ -85,7 +84,7 @@ func TestGenYamlDocRunnable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkGenYamlToFile(b *testing.B) {
|
func BenchmarkGenYamlToFile(b *testing.B) {
|
||||||
file, err := ioutil.TempFile("", "")
|
file, err := os.CreateTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
requiredAsGroup = "cobra_annotation_required_if_others_set"
|
requiredAsGroupAnnotation = "cobra_annotation_required_if_others_set"
|
||||||
oneRequired = "cobra_annotation_one_required"
|
oneRequiredAnnotation = "cobra_annotation_one_required"
|
||||||
mutuallyExclusive = "cobra_annotation_mutually_exclusive"
|
mutuallyExclusiveAnnotation = "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, 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.
|
// 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, 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.
|
// 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, 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)
|
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, requiredAsGroup, groupStatus)
|
processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus)
|
||||||
processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
|
processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus)
|
||||||
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
|
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, 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, requiredAsGroup, groupStatus)
|
processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus)
|
||||||
processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
|
processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus)
|
||||||
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
|
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, 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
2
go.mod
|
@ -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.3
|
github.com/cpuguy83/go-md2man/v2 v2.0.4
|
||||||
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
4
go.sum
|
@ -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.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
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 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=
|
||||||
|
|
|
@ -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.Replace(nameForVar, "-", "_", -1)
|
nameForVar = strings.ReplaceAll(nameForVar, "-", "_")
|
||||||
nameForVar = strings.Replace(nameForVar, ":", "_", -1)
|
nameForVar = strings.ReplaceAll(nameForVar, ":", "_")
|
||||||
|
|
||||||
compCmd := ShellCompRequestCmd
|
compCmd := ShellCompRequestCmd
|
||||||
if !includeDesc {
|
if !includeDesc {
|
||||||
|
@ -162,7 +162,10 @@ filter __%[1]s_escapeStringWithSpecialChars {
|
||||||
if (-Not $Description) {
|
if (-Not $Description) {
|
||||||
$Description = " "
|
$Description = " "
|
||||||
}
|
}
|
||||||
@{Name="$Name";Description="$Description"}
|
New-Object -TypeName PSCustomObject -Property @{
|
||||||
|
Name = "$Name"
|
||||||
|
Description = "$Description"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -240,7 +243,12 @@ filter __%[1]s_escapeStringWithSpecialChars {
|
||||||
__%[1]s_debug "Only one completion left"
|
__%[1]s_debug "Only one completion left"
|
||||||
|
|
||||||
# insert space after value
|
# insert space after value
|
||||||
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
|
$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space
|
||||||
|
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
|
||||||
|
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
|
||||||
|
} else {
|
||||||
|
$CompletionText
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
# Add the proper number of spaces to align the descriptions
|
# Add the proper number of spaces to align the descriptions
|
||||||
|
@ -255,7 +263,12 @@ filter __%[1]s_escapeStringWithSpecialChars {
|
||||||
$Description = " ($($comp.Description))"
|
$Description = " ($($comp.Description))"
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
|
$CompletionText = "$($comp.Name)$Description"
|
||||||
|
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
|
||||||
|
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
|
||||||
|
} else {
|
||||||
|
$CompletionText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +277,13 @@ filter __%[1]s_escapeStringWithSpecialChars {
|
||||||
# insert space after value
|
# insert space after value
|
||||||
# MenuComplete will automatically show the ToolTip of
|
# MenuComplete will automatically show the ToolTip of
|
||||||
# the highlighted value at the bottom of the suggestions.
|
# the highlighted value at the bottom of the suggestions.
|
||||||
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
|
|
||||||
|
$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space
|
||||||
|
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
|
||||||
|
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
|
||||||
|
} else {
|
||||||
|
$CompletionText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# TabCompleteNext and in case we get something unknown
|
# TabCompleteNext and in case we get something unknown
|
||||||
|
@ -272,7 +291,13 @@ filter __%[1]s_escapeStringWithSpecialChars {
|
||||||
# Like MenuComplete but we don't want to add a space here because
|
# Like MenuComplete but we don't want to add a space here because
|
||||||
# the user need to press space anyway to get the completion.
|
# the user need to press space anyway to get the completion.
|
||||||
# Description will not be shown because that's not possible with TabCompleteNext
|
# Description will not be shown because that's not possible with TabCompleteNext
|
||||||
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
|
|
||||||
|
$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars)
|
||||||
|
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
|
||||||
|
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
|
||||||
|
} else {
|
||||||
|
$CompletionText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,27 +3,29 @@
|
||||||
Active Help is a framework provided by Cobra which allows a program to define messages (hints, warnings, etc) that will be printed during program usage. It aims to make it easier for your users to learn how to use your program. If configured by the program, Active Help is printed when the user triggers shell completion.
|
Active Help is a framework provided by Cobra which allows a program to define messages (hints, warnings, etc) that will be printed during program usage. It aims to make it easier for your users to learn how to use your program. If configured by the program, Active Help is printed when the user triggers shell completion.
|
||||||
|
|
||||||
For example,
|
For example,
|
||||||
```
|
|
||||||
bash-5.1$ helm repo add [tab]
|
```console
|
||||||
|
$ helm repo add [tab]
|
||||||
You must choose a name for the repo you are adding.
|
You must choose a name for the repo you are adding.
|
||||||
|
|
||||||
bash-5.1$ bin/helm package [tab]
|
$ bin/helm package [tab]
|
||||||
Please specify the path to the chart to package
|
Please specify the path to the chart to package
|
||||||
|
|
||||||
bash-5.1$ bin/helm package [tab][tab]
|
$ bin/helm package [tab][tab]
|
||||||
bin/ internal/ scripts/ pkg/ testdata/
|
bin/ internal/ scripts/ pkg/ testdata/
|
||||||
```
|
```
|
||||||
|
|
||||||
**Hint**: A good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions to guide the user in knowing what is expected by the program.
|
**Hint**: A good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions to guide the user in knowing what is expected by the program.
|
||||||
|
|
||||||
## Supported shells
|
## Supported shells
|
||||||
|
|
||||||
Active Help is currently only supported for the following shells:
|
Active Help is currently only supported for the following shells:
|
||||||
- Bash (using [bash completion V2](shell_completions.md#bash-completion-v2) only). Note that bash 4.4 or higher is required for the prompt to appear when an Active Help message is printed.
|
- Bash (using [bash completion V2](completions/_index.md#bash-completion-v2) only). Note that bash 4.4 or higher is required for the prompt to appear when an Active Help message is printed.
|
||||||
- Zsh
|
- Zsh
|
||||||
|
|
||||||
## Adding Active Help messages
|
## Adding Active Help messages
|
||||||
|
|
||||||
As Active Help uses the shell completion system, the implementation of Active Help messages is done by enhancing custom dynamic completions. If you are not familiar with dynamic completions, please refer to [Shell Completions](shell_completions.md).
|
As Active Help uses the shell completion system, the implementation of Active Help messages is done by enhancing custom dynamic completions. If you are not familiar with dynamic completions, please refer to [Shell Completions](completions/_index.md).
|
||||||
|
|
||||||
Adding Active Help is done through the use of the `cobra.AppendActiveHelp(...)` function, where the program repeatedly adds Active Help messages to the list of completions. Keep reading for details.
|
Adding Active Help is done through the use of the `cobra.AppendActiveHelp(...)` function, where the program repeatedly adds Active Help messages to the list of completions. Keep reading for details.
|
||||||
|
|
||||||
|
@ -52,22 +54,26 @@ cmd := &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The example above defines the completions (none, in this specific example) as well as the Active Help messages for the `helm repo add` command. It yields the following behavior:
|
The example above defines the completions (none, in this specific example) as well as the Active Help messages for the `helm repo add` command. It yields the following behavior:
|
||||||
```
|
|
||||||
bash-5.1$ helm repo add [tab]
|
```console
|
||||||
|
$ helm repo add [tab]
|
||||||
You must choose a name for the repo you are adding
|
You must choose a name for the repo you are adding
|
||||||
|
|
||||||
bash-5.1$ helm repo add grafana [tab]
|
$ helm repo add grafana [tab]
|
||||||
You must specify the URL for the repo you are adding
|
You must specify the URL for the repo you are adding
|
||||||
|
|
||||||
bash-5.1$ helm repo add grafana https://grafana.github.io/helm-charts [tab]
|
$ helm repo add grafana https://grafana.github.io/helm-charts [tab]
|
||||||
This command does not take any more arguments
|
This command does not take any more arguments
|
||||||
```
|
```
|
||||||
|
|
||||||
**Hint**: As can be seen in the above example, a good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions.
|
**Hint**: As can be seen in the above example, a good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions.
|
||||||
|
|
||||||
### Active Help for flags
|
### Active Help for flags
|
||||||
|
|
||||||
Providing Active Help for flags is done in the same fashion as for nouns, but using the completion function registered for the flag. For example:
|
Providing Active Help for flags is done in the same fashion as for nouns, but using the completion function registered for the flag. For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
_ = cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
_ = cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
|
@ -77,11 +83,12 @@ _ = cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []st
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
The example above prints an Active Help message when not enough information was given by the user to complete the `--version` flag.
|
The example above prints an Active Help message when not enough information was given by the user to complete the `--version` flag.
|
||||||
```
|
|
||||||
bash-5.1$ bin/helm install myrelease --version 2.0.[tab]
|
```console
|
||||||
|
$ bin/helm install myrelease --version 2.0.[tab]
|
||||||
You must first specify the chart to install before the --version flag can be completed
|
You must first specify the chart to install before the --version flag can be completed
|
||||||
|
|
||||||
bash-5.1$ bin/helm install myrelease bitnami/solr --version 2.0.[tab][tab]
|
$ bin/helm install myrelease bitnami/solr --version 2.0.[tab][tab]
|
||||||
2.0.1 2.0.2 2.0.3
|
2.0.1 2.0.2 2.0.3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -103,6 +110,7 @@ Active Help configuration using the `cobra.GetActiveHelpConfig(cmd)` function an
|
||||||
should or should not be added (instead of reading the environment variable directly).
|
should or should not be added (instead of reading the environment variable directly).
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
activeHelpLevel := cobra.GetActiveHelpConfig(cmd)
|
activeHelpLevel := cobra.GetActiveHelpConfig(cmd)
|
||||||
|
@ -124,11 +132,13 @@ ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([
|
||||||
return comps, cobra.ShellCompDirectiveNoFileComp
|
return comps, cobra.ShellCompDirectiveNoFileComp
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note 1**: If the `<PROGRAM>_ACTIVE_HELP` environment variable is set to the string "0", Cobra will automatically disable all Active Help output (even if some output was specified by the program using the `cobra.AppendActiveHelp(...)` function). Using "0" can simplify your code in situations where you want to blindly disable Active Help without having to call `cobra.GetActiveHelpConfig(cmd)` explicitly.
|
**Note 1**: If the `<PROGRAM>_ACTIVE_HELP` environment variable is set to the string "0", Cobra will automatically disable all Active Help output (even if some output was specified by the program using the `cobra.AppendActiveHelp(...)` function). Using "0" can simplify your code in situations where you want to blindly disable Active Help without having to call `cobra.GetActiveHelpConfig(cmd)` explicitly.
|
||||||
|
|
||||||
**Note 2**: If a user wants to disable Active Help for every single program based on Cobra, she can set the environment variable `COBRA_ACTIVE_HELP` to "0". In this case `cobra.GetActiveHelpConfig(cmd)` will return "0" no matter what the variable `<PROGRAM>_ACTIVE_HELP` is set to.
|
**Note 2**: If a user wants to disable Active Help for every single program based on Cobra, she can set the environment variable `COBRA_ACTIVE_HELP` to "0". In this case `cobra.GetActiveHelpConfig(cmd)` will return "0" no matter what the variable `<PROGRAM>_ACTIVE_HELP` is set to.
|
||||||
|
|
||||||
**Note 3**: If the user does not set `<PROGRAM>_ACTIVE_HELP` or `COBRA_ACTIVE_HELP` (which will be a common case), the default value for the Active Help configuration returned by `cobra.GetActiveHelpConfig(cmd)` will be the empty string.
|
**Note 3**: If the user does not set `<PROGRAM>_ACTIVE_HELP` or `COBRA_ACTIVE_HELP` (which will be a common case), the default value for the Active Help configuration returned by `cobra.GetActiveHelpConfig(cmd)` will be the empty string.
|
||||||
|
|
||||||
## Active Help with Cobra's default completion command
|
## Active Help with Cobra's default completion command
|
||||||
|
|
||||||
Cobra provides a default `completion` command for programs that wish to use it.
|
Cobra provides a default `completion` command for programs that wish to use it.
|
||||||
|
@ -138,10 +148,11 @@ details for your users.
|
||||||
|
|
||||||
## Debugging Active Help
|
## Debugging Active Help
|
||||||
|
|
||||||
Debugging your Active Help code is done in the same way as debugging your dynamic completion code, which is with Cobra's hidden `__complete` command. Please refer to [debugging shell completion](shell_completions.md#debugging) for details.
|
Debugging your Active Help code is done in the same way as debugging your dynamic completion code, which is with Cobra's hidden `__complete` command. Please refer to [debugging shell completion](completions/_index.md#debugging) for details.
|
||||||
|
|
||||||
When debugging with the `__complete` command, if you want to specify different Active Help configurations, you should use the active help environment variable. That variable is named `<PROGRAM>_ACTIVE_HELP` where any non-ASCII-alphanumeric characters are replaced by an `_`. For example, we can test deactivating some Active Help as shown below:
|
When debugging with the `__complete` command, if you want to specify different Active Help configurations, you should use the active help environment variable. That variable is named `<PROGRAM>_ACTIVE_HELP` where any non-ASCII-alphanumeric characters are replaced by an `_`. For example, we can test deactivating some Active Help as shown below:
|
||||||
```
|
|
||||||
|
```console
|
||||||
$ HELM_ACTIVE_HELP=1 bin/helm __complete install wordpress bitnami/h<ENTER>
|
$ HELM_ACTIVE_HELP=1 bin/helm __complete install wordpress bitnami/h<ENTER>
|
||||||
bitnami/haproxy
|
bitnami/haproxy
|
||||||
bitnami/harbor
|
bitnami/harbor
|
||||||
|
|
|
@ -35,7 +35,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"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, ioutil.Discard, ioutil.Discard)
|
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard)
|
||||||
err := doc.GenMarkdownTree(kubectl, "./")
|
err := doc.GenMarkdownTree(kubectl, "./")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -35,7 +35,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"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, ioutil.Discard, ioutil.Discard)
|
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard)
|
||||||
err := doc.GenReSTTree(kubectl, "./")
|
err := doc.GenReSTTree(kubectl, "./")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -34,7 +34,7 @@ This program can actually generate docs for the kubectl command in the kubernete
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
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, "./")
|
err := doc.GenYamlTree(kubectl, "./")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
- [Allero](https://github.com/allero-io/allero)
|
- [Allero](https://github.com/allero-io/allero)
|
||||||
- [Arewefastyet](https://benchmark.vitess.io)
|
- [Arewefastyet](https://benchmark.vitess.io)
|
||||||
- [Arduino CLI](https://github.com/arduino/arduino-cli)
|
- [Arduino CLI](https://github.com/arduino/arduino-cli)
|
||||||
|
- [Azion](https://github.com/aziontech/azion)
|
||||||
- [Bleve](https://blevesearch.com/)
|
- [Bleve](https://blevesearch.com/)
|
||||||
- [Cilium](https://cilium.io/)
|
- [Cilium](https://cilium.io/)
|
||||||
- [CloudQuery](https://github.com/cloudquery/cloudquery)
|
- [CloudQuery](https://github.com/cloudquery/cloudquery)
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
- [GoReleaser](https://goreleaser.com)
|
- [GoReleaser](https://goreleaser.com)
|
||||||
- [Helm](https://helm.sh)
|
- [Helm](https://helm.sh)
|
||||||
- [Hugo](https://gohugo.io)
|
- [Hugo](https://gohugo.io)
|
||||||
|
- [Incus](https://linuxcontainers.org/incus/)
|
||||||
- [Infracost](https://github.com/infracost/infracost)
|
- [Infracost](https://github.com/infracost/infracost)
|
||||||
- [Istio](https://istio.io)
|
- [Istio](https://istio.io)
|
||||||
- [Kool](https://github.com/kool-dev/kool)
|
- [Kool](https://github.com/kool-dev/kool)
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
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:
|
||||||
|
|
||||||
```
|
```console
|
||||||
▾ appName/
|
▾ appName/
|
||||||
▾ cmd/
|
▾ cmd/
|
||||||
add.go
|
add.go
|
||||||
your.go
|
your.go
|
||||||
commands.go
|
commands.go
|
||||||
here.go
|
here.go
|
||||||
main.go
|
main.go
|
||||||
```
|
```
|
||||||
|
|
||||||
In a Cobra app, typically the main.go file is very bare. It serves one purpose: initializing Cobra.
|
In a Cobra app, typically the main.go file is very bare. It serves one purpose: initializing Cobra.
|
||||||
|
@ -18,9 +18,7 @@ In a Cobra app, typically the main.go file is very bare. It serves one purpose:
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "{pathToYourApp}/cmd"
|
||||||
"{pathToYourApp}/cmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
|
@ -148,9 +146,7 @@ In a Cobra app, typically the main.go file is very bare. It serves one purpose:
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "{pathToYourApp}/cmd"
|
||||||
"{pathToYourApp}/cmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
|
@ -197,7 +193,7 @@ its own go package.
|
||||||
The suggested approach is for the parent command to use `AddCommand` to add its most immediate
|
The suggested approach is for the parent command to use `AddCommand` to add its most immediate
|
||||||
subcommands. For example, consider the following directory structure:
|
subcommands. For example, consider the following directory structure:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
├── cmd
|
├── cmd
|
||||||
│ ├── root.go
|
│ ├── root.go
|
||||||
│ └── sub1
|
│ └── sub1
|
||||||
|
@ -301,6 +297,7 @@ 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
|
||||||
|
|
||||||
|
@ -320,12 +317,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
|
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")
|
||||||
|
@ -335,6 +334,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
|
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)")
|
||||||
|
@ -343,6 +343,7 @@ 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")
|
||||||
|
@ -351,6 +352,7 @@ 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")
|
||||||
|
@ -428,7 +430,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
|
||||||
|
@ -502,30 +504,31 @@ create' is called. Every command will automatically have the '--help' flag adde
|
||||||
The following output is automatically generated by Cobra. Nothing beyond the
|
The following output is automatically generated by Cobra. Nothing beyond the
|
||||||
command and flag definitions are needed.
|
command and flag definitions are needed.
|
||||||
|
|
||||||
$ cobra-cli help
|
```console
|
||||||
|
$ cobra-cli help
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
Cobra is a CLI library for Go that empowers applications.
|
||||||
This application is a tool to generate the needed files
|
This application is a tool to generate the needed files
|
||||||
to quickly create a Cobra application.
|
to quickly create a Cobra application.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
cobra-cli [command]
|
cobra-cli [command]
|
||||||
|
|
||||||
Available Commands:
|
Available Commands:
|
||||||
add Add a command to a Cobra Application
|
add Add a command to a Cobra Application
|
||||||
completion Generate the autocompletion script for the specified shell
|
completion Generate the autocompletion script for the specified shell
|
||||||
help Help about any command
|
help Help about any command
|
||||||
init Initialize a Cobra Application
|
init Initialize a Cobra Application
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-a, --author string author name for copyright attribution (default "YOUR NAME")
|
-a, --author string author name for copyright attribution (default "YOUR NAME")
|
||||||
--config string config file (default is $HOME/.cobra.yaml)
|
--config string config file (default is $HOME/.cobra.yaml)
|
||||||
-h, --help help for cobra-cli
|
-h, --help help for cobra-cli
|
||||||
-l, --license string name of license for the project
|
-l, --license string name of license for the project
|
||||||
--viper use Viper for configuration
|
--viper use Viper for configuration
|
||||||
|
|
||||||
Use "cobra-cli [command] --help" for more information about a command.
|
|
||||||
|
|
||||||
|
Use "cobra-cli [command] --help" for more information about a command.
|
||||||
|
```
|
||||||
|
|
||||||
Help is just a command like any other. There is no special logic or behavior
|
Help is just a command like any other. There is no special logic or behavior
|
||||||
around it. In fact, you can provide your own if you want.
|
around it. In fact, you can provide your own if you want.
|
||||||
|
@ -560,25 +563,27 @@ showing the user the 'usage'.
|
||||||
You may recognize this from the help above. That's because the default help
|
You may recognize this from the help above. That's because the default help
|
||||||
embeds the usage as part of its output.
|
embeds the usage as part of its output.
|
||||||
|
|
||||||
$ cobra-cli --invalid
|
```console
|
||||||
Error: unknown flag: --invalid
|
$ cobra-cli --invalid
|
||||||
Usage:
|
Error: unknown flag: --invalid
|
||||||
cobra-cli [command]
|
Usage:
|
||||||
|
cobra-cli [command]
|
||||||
|
|
||||||
Available Commands:
|
Available Commands:
|
||||||
add Add a command to a Cobra Application
|
add Add a command to a Cobra Application
|
||||||
completion Generate the autocompletion script for the specified shell
|
completion Generate the autocompletion script for the specified shell
|
||||||
help Help about any command
|
help Help about any command
|
||||||
init Initialize a Cobra Application
|
init Initialize a Cobra Application
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-a, --author string author name for copyright attribution (default "YOUR NAME")
|
-a, --author string author name for copyright attribution (default "YOUR NAME")
|
||||||
--config string config file (default is $HOME/.cobra.yaml)
|
--config string config file (default is $HOME/.cobra.yaml)
|
||||||
-h, --help help for cobra-cli
|
-h, --help help for cobra-cli
|
||||||
-l, --license string name of license for the project
|
-l, --license string name of license for the project
|
||||||
--viper use Viper for configuration
|
--viper use Viper for configuration
|
||||||
|
|
||||||
Use "cobra [command] --help" for more information about a command.
|
Use "cobra [command] --help" for more information about a command.
|
||||||
|
```
|
||||||
|
|
||||||
### Defining your own usage
|
### Defining your own usage
|
||||||
You can provide your own usage function or template for Cobra to use.
|
You can provide your own usage function or template for Cobra to use.
|
||||||
|
@ -695,7 +700,7 @@ Set `EnableTraverseRunHooks` global variable to `true` if you want to execute al
|
||||||
|
|
||||||
Cobra will print automatic suggestions when "unknown command" errors happen. This allows Cobra to behave similarly to the `git` command when a typo happens. For example:
|
Cobra will print automatic suggestions when "unknown command" errors happen. This allows Cobra to behave similarly to the `git` command when a typo happens. For example:
|
||||||
|
|
||||||
```
|
```console
|
||||||
$ hugo srever
|
$ hugo srever
|
||||||
Error: unknown command "srever" for "hugo"
|
Error: unknown command "srever" for "hugo"
|
||||||
|
|
||||||
|
@ -722,7 +727,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:
|
||||||
|
|
||||||
```
|
```console
|
||||||
$ kubectl remove
|
$ kubectl remove
|
||||||
Error: unknown command "remove" for "kubectl"
|
Error: unknown command "remove" for "kubectl"
|
||||||
|
|
||||||
|
@ -787,7 +792,7 @@ func main() {
|
||||||
|
|
||||||
Example run as a kubectl plugin:
|
Example run as a kubectl plugin:
|
||||||
|
|
||||||
```
|
```console
|
||||||
$ kubectl myplugin
|
$ kubectl myplugin
|
||||||
Usage:
|
Usage:
|
||||||
kubectl myplugin [command]
|
kubectl myplugin [command]
|
||||||
|
|
Loading…
Reference in a new issue