diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..212566614
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto
\ No newline at end of file
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
new file mode 100644
index 000000000..a8b26b6ec
--- /dev/null
+++ b/.github/workflows/push.yml
@@ -0,0 +1,58 @@
+name: 'push'
+
+on:
+ push:
+ pull_request:
+
+env:
+ GO111MODULE: on
+
+jobs:
+
+ test:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ ubuntu, windows, macOS ]
+ go: [
+ 1.12.x,
+ 1.13.x,
+ 1.14.x
+ ]
+
+ runs-on: ${{ matrix.os }}-latest
+
+ steps:
+
+ - name: Setup go
+ uses: actions/setup-go@v1
+ with:
+ go-version: ${{ matrix.go }}
+
+
+ - run: git config --global core.autocrlf input
+ if: matrix.os == 'windows'
+ shell: bash
+
+ - uses: actions/checkout@v2
+
+ - name: Install golangci-lint, richgo and gox
+ run: |
+ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $HOME/go/bin/ latest
+ go install github.com/kyoh86/richgo
+ go install github.com/mitchellh/gox
+
+ - name: Run tests
+ shell: bash
+ run: |
+ export PATH=$PATH:$HOME/go/bin/
+ make test
+
+ - name: Check formatting
+ run: make fmt
+
+ - name: Build generator
+ shell: bash
+ run: |
+ export PATH=$PATH:$HOME/go/bin/
+ make cobra_generator
diff --git a/.gitignore b/.gitignore
index c7b459e4d..3b087d766 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,7 @@ Session.vim
tags
*.exe
+/cobra/cobra
cobra.test
bin
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 000000000..0d6e61793
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,48 @@
+run:
+ deadline: 5m
+
+linters:
+ disable-all: true
+ enable:
+ #- bodyclose
+ - deadcode
+ #- depguard
+ #- dogsled
+ #- dupl
+ - errcheck
+ #- exhaustive
+ #- funlen
+ - gas
+ #- gochecknoinits
+ - goconst
+ #- gocritic
+ #- gocyclo
+ #- gofmt
+ - goimports
+ - golint
+ #- gomnd
+ #- goprintffuncname
+ #- gosec
+ #- gosimple
+ - govet
+ - ineffassign
+ - interfacer
+ #- lll
+ - maligned
+ - megacheck
+ #- misspell
+ #- nakedret
+ #- noctx
+ #- nolintlint
+ #- rowserrcheck
+ #- scopelint
+ #- staticcheck
+ - structcheck
+ #- stylecheck
+ #- typecheck
+ - unconvert
+ #- unparam
+ #- unused
+ - varcheck
+ #- whitespace
+ fast: false
diff --git a/.travis.yml b/.travis.yml
index a9bd4e547..d69d64618 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
language: go
stages:
- - diff
- test
- build
@@ -10,20 +9,23 @@ go:
- 1.13.x
- tip
+env: GO111MODULE=on
+
before_install:
- go get -u github.com/kyoh86/richgo
- go get -u github.com/mitchellh/gox
+ - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest
+
+env:
+ - GO111MODULE=on
matrix:
allow_failures:
- go: tip
include:
- - stage: diff
- go: 1.13.x
- script: make fmt
- stage: build
go: 1.13.x
script: make cobra_generator
-script:
+script:
- make test
diff --git a/Makefile b/Makefile
index e9740d1e1..472c73bf1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,21 +1,29 @@
BIN="./bin"
SRC=$(shell find . -name "*.go")
+ifeq (, $(shell which golangci-lint))
+$(warning "could not find golangci-lint in $(PATH), run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh")
+endif
+
ifeq (, $(shell which richgo))
$(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo")
endif
-.PHONY: fmt vet test cobra_generator install_deps clean
+.PHONY: fmt lint test cobra_generator install_deps clean
default: all
-all: fmt vet test cobra_generator
+all: fmt test cobra_generator
fmt:
$(info ******************** checking formatting ********************)
@test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1)
-test: install_deps vet
+lint:
+ $(info ******************** running lint tools ********************)
+ golangci-lint run -v
+
+test: install_deps lint
$(info ******************** running tests ********************)
richgo test -v ./...
@@ -28,9 +36,5 @@ install_deps:
$(info ******************** downloading dependencies ********************)
go get -v ./...
-vet:
- $(info ******************** vetting ********************)
- go vet ./...
-
clean:
rm -rf $(BIN)
diff --git a/README.md b/README.md
index b8cda7aab..7e65c88f8 100644
--- a/README.md
+++ b/README.md
@@ -194,7 +194,6 @@ import (
"fmt"
"os"
- homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@@ -234,27 +233,19 @@ func init() {
rootCmd.AddCommand(initCmd)
}
-func er(msg interface{}) {
- fmt.Println("Error:", msg)
- os.Exit(1)
-}
-
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
- home, err := homedir.Dir()
- if err != nil {
- er(err)
- }
+ home, err := os.UserHomeDir()
+ cobra.CheckErr(err)
// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".cobra")
}
-
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
@@ -406,7 +397,7 @@ func init() {
In this example the persistent flag `author` is bound with `viper`.
**Note**, that the variable `author` will not be set to the value from config,
-when the `--author` flag is not provided by user.
+when the `--author` flag is provided by user.
More in [viper documentation](https://github.com/spf13/viper#working-with-flags).
@@ -422,28 +413,33 @@ rootCmd.MarkFlagRequired("region")
## Positional and Custom Arguments
Validation of positional arguments can be specified using the `Args` field
-of `Command`.
+of `Command`. The following validators are built in:
+
+- `NoArgs` - report an error if there are any positional args.
+- `ArbitraryArgs` - accept any args.
+- `MinimumNArgs(int)` - report an error if less than N positional args are provided.
+- `MaximumNArgs(int)` - report an error if more than N positional args are provided.
+- `ExactArgs(int)` - report an error if there are not exactly N positional args.
+- `RangeArgs(min, max)` - report an error if the number of args is not between `min` and `max`.
+- `MatchAll(pargs ...PositionalArgs)` - enables combining existing checks with arbitrary other checks (e.g. you want to check the ExactArgs length along with other qualities).
+
+If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`.
-The following validators are built in:
+Field `ValidArgs` of type `[]string` can be defined in `Command`, in order to report an error if there are any positional args that are not in the list. This validation is executed implicitly before the validator defined in `Args`.
-- `NoArgs` - the command will report an error if there are any positional args.
-- `ArbitraryArgs` - the command will accept any args.
-- `OnlyValidArgs` - the command will report an error if there are any positional args that are not in the `ValidArgs` field of `Command`.
-- `MinimumNArgs(int)` - the command will report an error if there are not at least N positional args.
-- `MaximumNArgs(int)` - the command will report an error if there are more than N positional args.
-- `ExactArgs(int)` - the command will report an error if there are not exactly N positional args.
-- `ExactValidArgs(int)` - the command will report an error if there are not exactly N positional args OR if there are any positional args that are not in the `ValidArgs` field of `Command`
-- `RangeArgs(min, max)` - the command will report an error if the number of args is not between the minimum and maximum number of expected args.
+> NOTE: `OnlyValidArgs` and `ExactValidArgs(int)` are now deprecated. `ArbitraryArgs` and `ExactArgs(int)` provide the same functionality now.
-An example of setting the custom validator:
+Moreover, it is possible to set any custom validator that satisfies `func(cmd *cobra.Command, args []string) error`. For example:
```go
var cmd = &cobra.Command{
Short: "hello",
Args: func(cmd *cobra.Command, args []string) error {
- if len(args) < 1 {
- return errors.New("requires a color argument")
+ // Optionally run one of the validators provided by cobra
+ if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
+ return err
}
+ // Run the custom validation logic
if myapp.IsValidColor(args[0]) {
return nil
}
diff --git a/args.go b/args.go
index 70e9b2629..aad963bd1 100644
--- a/args.go
+++ b/args.go
@@ -7,6 +7,25 @@ import (
type PositionalArgs func(cmd *Command, args []string) error
+// validateArgs returns an error if there are any positional args that are not in
+// the `ValidArgs` field of `Command`
+func validateArgs(cmd *Command, args []string) error {
+ if len(cmd.ValidArgs) > 0 {
+ // Remove any description that may be included in ValidArgs.
+ // A description is following a tab character.
+ var validArgs []string
+ for _, v := range cmd.ValidArgs {
+ validArgs = append(validArgs, strings.Split(v, "\t")[0])
+ }
+ for _, v := range args {
+ if !stringInSlice(v, validArgs) {
+ return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
+ }
+ }
+ }
+ return nil
+}
+
// Legacy arg validation has the following behaviour:
// - root commands with no subcommands can take arbitrary arguments
// - root commands with subcommands will do subcommand validity checking
@@ -32,25 +51,6 @@ func NoArgs(cmd *Command, args []string) error {
return nil
}
-// OnlyValidArgs returns an error if any args are not in the list of ValidArgs.
-func OnlyValidArgs(cmd *Command, args []string) error {
- if len(cmd.ValidArgs) > 0 {
- // Remove any description that may be included in ValidArgs.
- // A description is following a tab character.
- var validArgs []string
- for _, v := range cmd.ValidArgs {
- validArgs = append(validArgs, strings.Split(v, "\t")[0])
- }
-
- for _, v := range args {
- if !stringInSlice(v, validArgs) {
- return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
- }
- }
- }
- return nil
-}
-
// ArbitraryArgs never returns an error.
func ArbitraryArgs(cmd *Command, args []string) error {
return nil
@@ -86,24 +86,39 @@ func ExactArgs(n int) PositionalArgs {
}
}
-// ExactValidArgs returns an error if
-// there are not exactly N positional args OR
-// there are any positional args that are not in the `ValidArgs` field of `Command`
-func ExactValidArgs(n int) PositionalArgs {
+// RangeArgs returns an error if the number of args is not within the expected range.
+func RangeArgs(min int, max int) PositionalArgs {
return func(cmd *Command, args []string) error {
- if err := ExactArgs(n)(cmd, args); err != nil {
- return err
+ if len(args) < min || len(args) > max {
+ return fmt.Errorf("accepts between %d and %d arg(s), received %d", min, max, len(args))
}
- return OnlyValidArgs(cmd, args)
+ return nil
}
}
-// RangeArgs returns an error if the number of args is not within the expected range.
-func RangeArgs(min int, max int) PositionalArgs {
+// MatchAll allows combining several PositionalArgs to work in concert.
+func MatchAll(pargs ...PositionalArgs) PositionalArgs {
return func(cmd *Command, args []string) error {
- if len(args) < min || len(args) > max {
- return fmt.Errorf("accepts between %d and %d arg(s), received %d", min, max, len(args))
+ for _, parg := range pargs {
+ if err := parg(cmd, args); err != nil {
+ return err
+ }
}
return nil
}
}
+
+// ExactValidArgs returns an error if there are not exactly N positional args OR
+// there are any positional args that are not in the `ValidArgs` field of `Command`
+//
+// Deprecated: now `ExactArgs` honors `ValidArgs`, when defined and not empty
+func ExactValidArgs(n int) PositionalArgs {
+ return ExactArgs(n)
+}
+
+// OnlyValidArgs returns an error if any args are not in the list of `ValidArgs`.
+//
+// Deprecated: now `ArbitraryArgs` honors `ValidArgs`, when defined and not empty
+func OnlyValidArgs(cmd *Command, args []string) error {
+ return ArbitraryArgs(cmd, args)
+}
diff --git a/args_test.go b/args_test.go
index c81b212ec..c1ec164d4 100644
--- a/args_test.go
+++ b/args_test.go
@@ -1,234 +1,134 @@
package cobra
import (
+ "fmt"
"strings"
"testing"
)
-func TestNoArgs(t *testing.T) {
- c := &Command{Use: "c", Args: NoArgs, Run: emptyRun}
-
- output, err := executeCommand(c)
- if output != "" {
- t.Errorf("Unexpected string: %v", output)
- }
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
-}
-
-func TestNoArgsWithArgs(t *testing.T) {
- c := &Command{Use: "c", Args: NoArgs, Run: emptyRun}
-
- _, err := executeCommand(c, "illegal")
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := `unknown command "illegal" for "c"`
- if got != expected {
- t.Errorf("Expected: %q, got: %q", expected, got)
- }
+type argsTestcase struct {
+ exerr string // Expected error key (see map[string][string])
+ args PositionalArgs // Args validator
+ wValid bool // Define `ValidArgs` in the command
+ rargs []string // Runtime args
}
-func TestOnlyValidArgs(t *testing.T) {
- c := &Command{
- Use: "c",
- Args: OnlyValidArgs,
- ValidArgs: []string{"one", "two"},
- Run: emptyRun,
- }
-
- output, err := executeCommand(c, "one", "two")
- if output != "" {
- t.Errorf("Unexpected output: %v", output)
- }
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
+var errStrings = map[string]string{
+ "invalid": `invalid argument "a" for "c"`,
+ "unknown": `unknown command "one" for "c"`,
+ "less": "requires at least 2 arg(s), only received 1",
+ "more": "accepts at most 2 arg(s), received 3",
+ "notexact": "accepts 2 arg(s), received 3",
+ "notinrange": "accepts between 2 and 4 arg(s), received 1",
}
-func TestOnlyValidArgsWithInvalidArgs(t *testing.T) {
+func (tc *argsTestcase) test(t *testing.T) {
c := &Command{
- Use: "c",
- Args: OnlyValidArgs,
- ValidArgs: []string{"one", "two"},
- Run: emptyRun,
- }
-
- _, err := executeCommand(c, "three")
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := `invalid argument "three" for "c"`
- if got != expected {
- t.Errorf("Expected: %q, got: %q", expected, got)
- }
-}
-
-func TestArbitraryArgs(t *testing.T) {
- c := &Command{Use: "c", Args: ArbitraryArgs, Run: emptyRun}
- output, err := executeCommand(c, "a", "b")
- if output != "" {
- t.Errorf("Unexpected output: %v", output)
- }
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
-}
-
-func TestMinimumNArgs(t *testing.T) {
- c := &Command{Use: "c", Args: MinimumNArgs(2), Run: emptyRun}
- output, err := executeCommand(c, "a", "b", "c")
- if output != "" {
- t.Errorf("Unexpected output: %v", output)
- }
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
-}
-
-func TestMinimumNArgsWithLessArgs(t *testing.T) {
- c := &Command{Use: "c", Args: MinimumNArgs(2), Run: emptyRun}
- _, err := executeCommand(c, "a")
-
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := "requires at least 2 arg(s), only received 1"
- if got != expected {
- t.Fatalf("Expected %q, got %q", expected, got)
- }
-}
-
-func TestMaximumNArgs(t *testing.T) {
- c := &Command{Use: "c", Args: MaximumNArgs(3), Run: emptyRun}
- output, err := executeCommand(c, "a", "b")
- if output != "" {
- t.Errorf("Unexpected output: %v", output)
- }
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
-}
-
-func TestMaximumNArgsWithMoreArgs(t *testing.T) {
- c := &Command{Use: "c", Args: MaximumNArgs(2), Run: emptyRun}
- _, err := executeCommand(c, "a", "b", "c")
-
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := "accepts at most 2 arg(s), received 3"
- if got != expected {
- t.Fatalf("Expected %q, got %q", expected, got)
- }
-}
-
-func TestExactArgs(t *testing.T) {
- c := &Command{Use: "c", Args: ExactArgs(3), Run: emptyRun}
- output, err := executeCommand(c, "a", "b", "c")
- if output != "" {
- t.Errorf("Unexpected output: %v", output)
- }
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
-}
-
-func TestExactArgsWithInvalidCount(t *testing.T) {
- c := &Command{Use: "c", Args: ExactArgs(2), Run: emptyRun}
- _, err := executeCommand(c, "a", "b", "c")
-
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := "accepts 2 arg(s), received 3"
- if got != expected {
- t.Fatalf("Expected %q, got %q", expected, got)
- }
-}
-
-func TestExactValidArgs(t *testing.T) {
- c := &Command{Use: "c", Args: ExactValidArgs(3), ValidArgs: []string{"a", "b", "c"}, Run: emptyRun}
- output, err := executeCommand(c, "a", "b", "c")
- if output != "" {
- t.Errorf("Unexpected output: %v", output)
- }
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
-}
-
-func TestExactValidArgsWithInvalidCount(t *testing.T) {
- c := &Command{Use: "c", Args: ExactValidArgs(2), Run: emptyRun}
- _, err := executeCommand(c, "a", "b", "c")
-
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := "accepts 2 arg(s), received 3"
- if got != expected {
- t.Fatalf("Expected %q, got %q", expected, got)
- }
-}
-
-func TestExactValidArgsWithInvalidArgs(t *testing.T) {
- c := &Command{
- Use: "c",
- Args: ExactValidArgs(1),
- ValidArgs: []string{"one", "two"},
- Run: emptyRun,
- }
-
- _, err := executeCommand(c, "three")
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := `invalid argument "three" for "c"`
- if got != expected {
- t.Errorf("Expected: %q, got: %q", expected, got)
- }
-}
-
-func TestRangeArgs(t *testing.T) {
- c := &Command{Use: "c", Args: RangeArgs(2, 4), Run: emptyRun}
- output, err := executeCommand(c, "a", "b", "c")
- if output != "" {
- t.Errorf("Unexpected output: %v", output)
- }
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
-}
-
-func TestRangeArgsWithInvalidCount(t *testing.T) {
- c := &Command{Use: "c", Args: RangeArgs(2, 4), Run: emptyRun}
- _, err := executeCommand(c, "a")
-
- if err == nil {
- t.Fatal("Expected an error")
- }
-
- got := err.Error()
- expected := "accepts between 2 and 4 arg(s), received 1"
- if got != expected {
- t.Fatalf("Expected %q, got %q", expected, got)
- }
-}
+ Use: "c",
+ Args: tc.args,
+ Run: emptyRun,
+ }
+ if tc.wValid {
+ c.ValidArgs = []string{"one", "two", "three"}
+ }
+
+ o, e := executeCommand(c, tc.rargs...)
+
+ if len(tc.exerr) > 0 {
+ // Expect error
+ if e == nil {
+ t.Fatal("Expected an error")
+ }
+ expected, ok := errStrings[tc.exerr]
+ if !ok {
+ t.Errorf(`key "%s" is not found in map "errStrings"`, tc.exerr)
+ return
+ }
+ if got := e.Error(); got != expected {
+ t.Errorf("Expected: %q, got: %q", expected, got)
+ }
+ } else {
+ // Expect success
+ if o != "" {
+ t.Errorf("Unexpected output: %v", o)
+ }
+ if e != nil {
+ t.Fatalf("Unexpected error: %v", e)
+ }
+ }
+}
+
+func testArgs(t *testing.T, tests map[string]argsTestcase) {
+ for name, tc := range tests {
+ t.Run(name, tc.test)
+ }
+}
+
+func TestArgs_No(t *testing.T) {
+ testArgs(t, map[string]argsTestcase{
+ " | ": {"", NoArgs, false, []string{}},
+ " | Arb": {"unknown", NoArgs, false, []string{"one"}},
+ "Valid | Valid": {"unknown", NoArgs, true, []string{"one"}},
+ })
+}
+func TestArgs_Arbitrary(t *testing.T) {
+ testArgs(t, map[string]argsTestcase{
+ " | Arb": {"", ArbitraryArgs, false, []string{"a", "b"}},
+ "Valid | Valid": {"", ArbitraryArgs, true, []string{"one", "two"}},
+ "Valid | Invalid": {"invalid", ArbitraryArgs, true, []string{"a"}},
+ })
+}
+func TestArgs_MinimumN(t *testing.T) {
+ testArgs(t, map[string]argsTestcase{
+ " | Arb": {"", MinimumNArgs(2), false, []string{"a", "b", "c"}},
+ "Valid | Valid": {"", MinimumNArgs(2), true, []string{"one", "three"}},
+ "Valid | Invalid": {"invalid", MinimumNArgs(2), true, []string{"a", "b"}},
+ " | Less": {"less", MinimumNArgs(2), false, []string{"a"}},
+ "Valid | Less": {"less", MinimumNArgs(2), true, []string{"one"}},
+ "Valid | LessInvalid": {"invalid", MinimumNArgs(2), true, []string{"a"}},
+ })
+}
+func TestArgs_MaximumN(t *testing.T) {
+ testArgs(t, map[string]argsTestcase{
+ " | Arb": {"", MaximumNArgs(3), false, []string{"a", "b"}},
+ "Valid | Valid": {"", MaximumNArgs(2), true, []string{"one", "three"}},
+ "Valid | Invalid": {"invalid", MaximumNArgs(2), true, []string{"a", "b"}},
+ " | More": {"more", MaximumNArgs(2), false, []string{"a", "b", "c"}},
+ "Valid | More": {"more", MaximumNArgs(2), true, []string{"one", "three", "two"}},
+ "Valid | MoreInvalid": {"invalid", MaximumNArgs(2), true, []string{"a", "b", "c"}},
+ })
+}
+func TestArgs_Exact(t *testing.T) {
+ testArgs(t, map[string]argsTestcase{
+ " | Arb": {"", ExactArgs(3), false, []string{"a", "b", "c"}},
+ "Valid | Valid": {"", ExactArgs(3), true, []string{"three", "one", "two"}},
+ "Valid | Invalid": {"invalid", ExactArgs(3), true, []string{"three", "a", "two"}},
+ " | InvalidCount": {"notexact", ExactArgs(2), false, []string{"a", "b", "c"}},
+ "Valid | InvalidCount": {"notexact", ExactArgs(2), true, []string{"three", "one", "two"}},
+ "Valid | InvalidCountInvalid": {"invalid", ExactArgs(2), true, []string{"three", "a", "two"}},
+ })
+}
+func TestArgs_Range(t *testing.T) {
+ testArgs(t, map[string]argsTestcase{
+ " | Arb": {"", RangeArgs(2, 4), false, []string{"a", "b", "c"}},
+ "Valid | Valid": {"", RangeArgs(2, 4), true, []string{"three", "one", "two"}},
+ "Valid | Invalid": {"invalid", RangeArgs(2, 4), true, []string{"three", "a", "two"}},
+ " | InvalidCount": {"notinrange", RangeArgs(2, 4), false, []string{"a"}},
+ "Valid | InvalidCount": {"notinrange", RangeArgs(2, 4), true, []string{"two"}},
+ "Valid | InvalidCountInvalid": {"invalid", RangeArgs(2, 4), true, []string{"a"}},
+ })
+}
+func TestArgs_DEPRECATED(t *testing.T) {
+ testArgs(t, map[string]argsTestcase{
+ "OnlyValid | Valid | Valid": {"", OnlyValidArgs, true, []string{"one", "two"}},
+ "OnlyValid | Valid | Invalid": {"invalid", OnlyValidArgs, true, []string{"a"}},
+ "ExactValid | Valid | Valid": {"", ExactValidArgs(3), true, []string{"two", "three", "one"}},
+ "ExactValid | Valid | InvalidCount": {"notexact", ExactValidArgs(2), true, []string{"two", "three", "one"}},
+ "ExactValid | Valid | Invalid": {"invalid", ExactValidArgs(2), true, []string{"two", "a"}},
+ })
+}
+
+// Takes(No)Args
func TestRootTakesNoArgs(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
@@ -285,3 +185,51 @@ func TestChildTakesArgs(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}
}
+
+func TestMatchAll(t *testing.T) {
+ // Somewhat contrived example check that ensures there are exactly 3
+ // arguments, and each argument is exactly 2 bytes long.
+ pargs := MatchAll(
+ ExactArgs(3),
+ func(cmd *Command, args []string) error {
+ for _, arg := range args {
+ if len([]byte(arg)) != 2 {
+ return fmt.Errorf("expected to be exactly 2 bytes long")
+ }
+ }
+ return nil
+ },
+ )
+
+ testCases := map[string]struct {
+ args []string
+ fail bool
+ }{
+ "happy path": {
+ []string{"aa", "bb", "cc"},
+ false,
+ },
+ "incorrect number of args": {
+ []string{"aa", "bb", "cc", "dd"},
+ true,
+ },
+ "incorrect number of bytes in one arg": {
+ []string{"aa", "bb", "abc"},
+ true,
+ },
+ }
+
+ rootCmd := &Command{Use: "root", Args: pargs, Run: emptyRun}
+
+ for name, tc := range testCases {
+ t.Run(name, func(t *testing.T) {
+ _, err := executeCommand(rootCmd, tc.args...)
+ if err != nil && !tc.fail {
+ t.Errorf("unexpected: %v\n", err)
+ }
+ if err == nil && tc.fail {
+ t.Errorf("expected error")
+ }
+ })
+ }
+}
diff --git a/bash_completions.go b/bash_completions.go
index 846636d75..710614793 100644
--- a/bash_completions.go
+++ b/bash_completions.go
@@ -19,9 +19,9 @@ const (
BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir"
)
-func writePreamble(buf *bytes.Buffer, name string) {
- buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
- buf.WriteString(fmt.Sprintf(`
+func writePreamble(buf io.StringWriter, name string) {
+ WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
+ WriteStringAndCheck(buf, fmt.Sprintf(`
__%[1]s_debug()
{
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
@@ -380,10 +380,10 @@ __%[1]s_handle_word()
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
}
-func writePostscript(buf *bytes.Buffer, name string) {
+func writePostscript(buf io.StringWriter, name string) {
name = strings.Replace(name, ":", "__", -1)
- buf.WriteString(fmt.Sprintf("__start_%s()\n", name))
- buf.WriteString(fmt.Sprintf(`{
+ WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))
+ WriteStringAndCheck(buf, fmt.Sprintf(`{
local cur prev words cword
declare -A flaghash 2>/dev/null || :
declare -A aliashash 2>/dev/null || :
@@ -410,33 +410,33 @@ func writePostscript(buf *bytes.Buffer, name string) {
}
`, name))
- buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
+ WriteStringAndCheck(buf, fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
complete -o default -F __start_%s %s
else
complete -o default -o nospace -F __start_%s %s
fi
`, name, name, name, name))
- buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n")
+ WriteStringAndCheck(buf, "# ex: ts=4 sw=4 et filetype=sh\n")
}
-func writeCommands(buf *bytes.Buffer, cmd *Command) {
- buf.WriteString(" commands=()\n")
+func writeCommands(buf io.StringWriter, cmd *Command) {
+ WriteStringAndCheck(buf, " commands=()\n")
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() && c != cmd.helpCommand {
continue
}
- buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name()))
+ WriteStringAndCheck(buf, fmt.Sprintf(" commands+=(%q)\n", c.Name()))
writeCmdAliases(buf, c)
}
- buf.WriteString("\n")
+ WriteStringAndCheck(buf, "\n")
}
-func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) {
+func writeFlagHandler(buf io.StringWriter, name string, annotations map[string][]string, cmd *Command) {
for key, value := range annotations {
switch key {
case BashCompFilenameExt:
- buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
+ WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
var ext string
if len(value) > 0 {
@@ -444,17 +444,18 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
} else {
ext = "_filedir"
}
- buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext))
+ WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))
case BashCompCustom:
- buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
+ WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
+
if len(value) > 0 {
handlers := strings.Join(value, "; ")
- buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers))
+ WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", handlers))
} else {
- buf.WriteString(" flags_completion+=(:)\n")
+ WriteStringAndCheck(buf, " flags_completion+=(:)\n")
}
case BashCompSubdirsInDir:
- buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
+ WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
var ext string
if len(value) == 1 {
@@ -462,46 +463,48 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
} else {
ext = "_filedir -d"
}
- buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext))
+ WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))
}
}
}
-func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
+const cbn = "\")\n"
+
+func writeShortFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
name := flag.Shorthand
format := " "
if len(flag.NoOptDefVal) == 0 {
format += "two_word_"
}
- format += "flags+=(\"-%s\")\n"
- buf.WriteString(fmt.Sprintf(format, name))
+ format += "flags+=(\"-%s" + cbn
+ WriteStringAndCheck(buf, fmt.Sprintf(format, name))
writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
}
-func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
+func writeFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
name := flag.Name
format := " flags+=(\"--%s"
if len(flag.NoOptDefVal) == 0 {
format += "="
}
- format += "\")\n"
- buf.WriteString(fmt.Sprintf(format, name))
+ format += cbn
+ WriteStringAndCheck(buf, fmt.Sprintf(format, name))
if len(flag.NoOptDefVal) == 0 {
- format = " two_word_flags+=(\"--%s\")\n"
- buf.WriteString(fmt.Sprintf(format, name))
+ format = " two_word_flags+=(\"--%s" + cbn
+ WriteStringAndCheck(buf, fmt.Sprintf(format, name))
}
writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
}
-func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
+func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) {
name := flag.Name
- format := " local_nonpersistent_flags+=(\"--%[1]s\")\n"
+ format := " local_nonpersistent_flags+=(\"--%[1]s" + cbn
if len(flag.NoOptDefVal) == 0 {
- format += " local_nonpersistent_flags+=(\"--%[1]s=\")\n"
+ format += " local_nonpersistent_flags+=(\"--%[1]s=" + cbn
}
- buf.WriteString(fmt.Sprintf(format, name))
+ WriteStringAndCheck(buf, fmt.Sprintf(format, name))
if len(flag.Shorthand) > 0 {
- buf.WriteString(fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))
+ WriteStringAndCheck(buf, fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))
}
}
@@ -519,9 +522,9 @@ func prepareCustomAnnotationsForFlags(cmd *Command) {
}
}
-func writeFlags(buf *bytes.Buffer, cmd *Command) {
+func writeFlags(buf io.StringWriter, cmd *Command) {
prepareCustomAnnotationsForFlags(cmd)
- buf.WriteString(` flags=()
+ WriteStringAndCheck(buf, ` flags=()
two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=()
@@ -553,11 +556,11 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
}
})
- buf.WriteString("\n")
+ WriteStringAndCheck(buf, "\n")
}
-func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
- buf.WriteString(" must_have_one_flag=()\n")
+func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
+ WriteStringAndCheck(buf, " must_have_one_flag=()\n")
flags := cmd.NonInheritedFlags()
flags.VisitAll(func(flag *pflag.Flag) {
if nonCompletableFlag(flag) {
@@ -570,55 +573,55 @@ func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
if flag.Value.Type() != "bool" {
format += "="
}
- format += "\")\n"
- buf.WriteString(fmt.Sprintf(format, flag.Name))
+ format += cbn
+ WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))
if len(flag.Shorthand) > 0 {
- buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand))
+ WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
}
}
}
})
}
-func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
- buf.WriteString(" must_have_one_noun=()\n")
- sort.Sort(sort.StringSlice(cmd.ValidArgs))
+func writeRequiredNouns(buf io.StringWriter, cmd *Command) {
+ WriteStringAndCheck(buf, " must_have_one_noun=()\n")
+ sort.Strings(cmd.ValidArgs)
for _, value := range cmd.ValidArgs {
// Remove any description that may be included following a tab character.
// Descriptions are not supported by bash completion.
value = strings.Split(value, "\t")[0]
- buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))
+ WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))
}
if cmd.ValidArgsFunction != nil {
- buf.WriteString(" has_completion_function=1\n")
+ WriteStringAndCheck(buf, " has_completion_function=1\n")
}
}
-func writeCmdAliases(buf *bytes.Buffer, cmd *Command) {
+func writeCmdAliases(buf io.StringWriter, cmd *Command) {
if len(cmd.Aliases) == 0 {
return
}
- sort.Sort(sort.StringSlice(cmd.Aliases))
+ sort.Strings(cmd.Aliases)
- buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
+ WriteStringAndCheck(buf, fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
for _, value := range cmd.Aliases {
- buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value))
- buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name()))
+ WriteStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value))
+ WriteStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name()))
}
- buf.WriteString(` fi`)
- buf.WriteString("\n")
+ WriteStringAndCheck(buf, ` fi`)
+ WriteStringAndCheck(buf, "\n")
}
-func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
- buf.WriteString(" noun_aliases=()\n")
- sort.Sort(sort.StringSlice(cmd.ArgAliases))
+func writeArgAliases(buf io.StringWriter, cmd *Command) {
+ WriteStringAndCheck(buf, " noun_aliases=()\n")
+ sort.Strings(cmd.ArgAliases)
for _, value := range cmd.ArgAliases {
- buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value))
+ WriteStringAndCheck(buf, fmt.Sprintf(" noun_aliases+=(%q)\n", value))
}
}
-func gen(buf *bytes.Buffer, cmd *Command) {
+func gen(buf io.StringWriter, cmd *Command) {
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() && c != cmd.helpCommand {
continue
@@ -630,22 +633,22 @@ func gen(buf *bytes.Buffer, cmd *Command) {
commandName = strings.Replace(commandName, ":", "__", -1)
if cmd.Root() == cmd {
- buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName))
+ WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))
} else {
- buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName))
+ WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName))
}
- buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName))
- buf.WriteString("\n")
- buf.WriteString(" command_aliases=()\n")
- buf.WriteString("\n")
+ WriteStringAndCheck(buf, fmt.Sprintf(" last_command=%q\n", commandName))
+ WriteStringAndCheck(buf, "\n")
+ WriteStringAndCheck(buf, " command_aliases=()\n")
+ WriteStringAndCheck(buf, "\n")
writeCommands(buf, cmd)
writeFlags(buf, cmd)
writeRequiredFlag(buf, cmd)
writeRequiredNouns(buf, cmd)
writeArgAliases(buf, cmd)
- buf.WriteString("}\n\n")
+ WriteStringAndCheck(buf, "}\n\n")
}
// GenBashCompletion generates bash completion file and writes to the passed writer.
diff --git a/bash_completions_test.go b/bash_completions_test.go
index 2c182ba73..46e8e674c 100644
--- a/bash_completions_test.go
+++ b/bash_completions_test.go
@@ -40,10 +40,9 @@ func checkRegex(t *testing.T, found, pattern string) {
}
func runShellCheck(s string) error {
- excluded := []string{
+ cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e",
"SC2034", // PREFIX appears unused. Verify it or export it.
- }
- cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e", strings.Join(excluded, ","))
+ )
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
@@ -52,7 +51,9 @@ func runShellCheck(s string) error {
return err
}
go func() {
- stdin.Write([]byte(s))
+ _, err := stdin.Write([]byte(s))
+ CheckErr(err)
+
stdin.Close()
}()
@@ -74,26 +75,26 @@ func TestBashCompletions(t *testing.T) {
Run: emptyRun,
}
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
- rootCmd.MarkFlagRequired("introot")
+ assertNoErr(t, rootCmd.MarkFlagRequired("introot"))
// Filename.
rootCmd.Flags().String("filename", "", "Enter a filename")
- rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml")
+ assertNoErr(t, rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml"))
// Persistent filename.
rootCmd.PersistentFlags().String("persistent-filename", "", "Enter a filename")
- rootCmd.MarkPersistentFlagFilename("persistent-filename")
- rootCmd.MarkPersistentFlagRequired("persistent-filename")
+ assertNoErr(t, rootCmd.MarkPersistentFlagFilename("persistent-filename"))
+ assertNoErr(t, rootCmd.MarkPersistentFlagRequired("persistent-filename"))
// Filename extensions.
rootCmd.Flags().String("filename-ext", "", "Enter a filename (extension limited)")
- rootCmd.MarkFlagFilename("filename-ext")
+ assertNoErr(t, rootCmd.MarkFlagFilename("filename-ext"))
rootCmd.Flags().String("custom", "", "Enter a filename (extension limited)")
- rootCmd.MarkFlagCustom("custom", "__complete_custom")
+ assertNoErr(t, rootCmd.MarkFlagCustom("custom", "__complete_custom"))
// Subdirectories in a given directory.
rootCmd.Flags().String("theme", "", "theme to use (located in /themes/THEMENAME/)")
- rootCmd.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"})
+ assertNoErr(t, rootCmd.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"}))
// For two word flags check
rootCmd.Flags().StringP("two", "t", "", "this is two word flags")
@@ -109,9 +110,9 @@ func TestBashCompletions(t *testing.T) {
}
echoCmd.Flags().String("filename", "", "Enter a filename")
- echoCmd.MarkFlagFilename("filename", "json", "yaml", "yml")
+ assertNoErr(t, echoCmd.MarkFlagFilename("filename", "json", "yaml", "yml"))
echoCmd.Flags().String("config", "", "config to use (located in /config/PROFILE/)")
- echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"})
+ assertNoErr(t, echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"}))
printCmd := &Command{
Use: "print [string to print]",
@@ -138,7 +139,7 @@ func TestBashCompletions(t *testing.T) {
timesCmd := &Command{
Use: "times [# times] [string to echo]",
SuggestFor: []string{"counts"},
- Args: OnlyValidArgs,
+ Args: ArbitraryArgs,
ValidArgs: []string{"one", "two", "three", "four"},
Short: "Echo anything to the screen more times",
Long: "a slightly useless command for testing.",
@@ -149,7 +150,7 @@ func TestBashCompletions(t *testing.T) {
rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd)
buf := new(bytes.Buffer)
- rootCmd.GenBashCompletion(buf)
+ assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String()
check(t, output, "_root")
@@ -216,10 +217,10 @@ func TestBashCompletionHiddenFlag(t *testing.T) {
const flagName = "hiddenFlag"
c.Flags().Bool(flagName, false, "")
- c.Flags().MarkHidden(flagName)
+ assertNoErr(t, c.Flags().MarkHidden(flagName))
buf := new(bytes.Buffer)
- c.GenBashCompletion(buf)
+ assertNoErr(t, c.GenBashCompletion(buf))
output := buf.String()
if strings.Contains(output, flagName) {
@@ -232,10 +233,10 @@ func TestBashCompletionDeprecatedFlag(t *testing.T) {
const flagName = "deprecated-flag"
c.Flags().Bool(flagName, false, "")
- c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead")
+ assertNoErr(t, c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead"))
buf := new(bytes.Buffer)
- c.GenBashCompletion(buf)
+ assertNoErr(t, c.GenBashCompletion(buf))
output := buf.String()
if strings.Contains(output, flagName) {
@@ -250,7 +251,7 @@ func TestBashCompletionTraverseChildren(t *testing.T) {
c.Flags().BoolP("bool-flag", "b", false, "bool flag")
buf := new(bytes.Buffer)
- c.GenBashCompletion(buf)
+ assertNoErr(t, c.GenBashCompletion(buf))
output := buf.String()
// check that local nonpersistent flag are not set since we have TraverseChildren set to true
diff --git a/cobra.go b/cobra.go
index d01becc8f..d6cbfd719 100644
--- a/cobra.go
+++ b/cobra.go
@@ -19,6 +19,7 @@ package cobra
import (
"fmt"
"io"
+ "os"
"reflect"
"strconv"
"strings"
@@ -205,3 +206,17 @@ func stringInSlice(a string, list []string) bool {
}
return false
}
+
+// CheckErr prints the msg with the prefix 'Error:' and exits with error code 1. If the msg is nil, it does nothing.
+func CheckErr(msg interface{}) {
+ if msg != nil {
+ fmt.Fprintln(os.Stderr, "Error:", msg)
+ os.Exit(1)
+ }
+}
+
+// WriteStringAndCheck writes a string into a buffer, and checks if the error is not nil.
+func WriteStringAndCheck(b io.StringWriter, s string) {
+ _, err := b.WriteString(s)
+ CheckErr(err)
+}
diff --git a/cobra/cmd/add.go b/cobra/cmd/add.go
index 6645a755f..8377411ed 100644
--- a/cobra/cmd/add.go
+++ b/cobra/cmd/add.go
@@ -40,13 +40,11 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
- er("add needs a name for the command")
+ cobra.CheckErr(fmt.Errorf("add needs a name for the command"))
}
wd, err := os.Getwd()
- if err != nil {
- er(err)
- }
+ cobra.CheckErr(err)
commandName := validateCmdName(args[0])
command := &Command{
@@ -59,10 +57,7 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
},
}
- err = command.Create()
- if err != nil {
- er(err)
- }
+ cobra.CheckErr(command.Create())
fmt.Printf("%s created at %s\n", command.CmdName, command.AbsolutePath)
},
@@ -72,7 +67,7 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
func init() {
addCmd.Flags().StringVarP(&packageName, "package", "t", "", "target package name (e.g. github.com/spf13/hugo)")
addCmd.Flags().StringVarP(&parentName, "parent", "p", "rootCmd", "variable name of parent command for this command")
- addCmd.Flags().MarkDeprecated("package", "this operation has been removed.")
+ cobra.CheckErr(addCmd.Flags().MarkDeprecated("package", "this operation has been removed."))
}
// validateCmdName returns source without any dashes and underscore.
diff --git a/cobra/cmd/add_test.go b/cobra/cmd/add_test.go
index de92fcea6..0b32ca67e 100644
--- a/cobra/cmd/add_test.go
+++ b/cobra/cmd/add_test.go
@@ -14,10 +14,8 @@ func TestGoldenAddCmd(t *testing.T) {
}
defer os.RemoveAll(command.AbsolutePath)
- command.Project.Create()
- if err := command.Create(); err != nil {
- t.Fatal(err)
- }
+ assertNoErr(t, command.Project.Create())
+ assertNoErr(t, command.Create())
generatedFile := fmt.Sprintf("%s/cmd/%s.go", command.AbsolutePath, command.CmdName)
goldenFile := fmt.Sprintf("testdata/%s.go.golden", command.CmdName)
diff --git a/cobra/cmd/golden_test.go b/cobra/cmd/golden_test.go
index e4055f33f..832ea5334 100644
--- a/cobra/cmd/golden_test.go
+++ b/cobra/cmd/golden_test.go
@@ -3,14 +3,11 @@ package cmd
import (
"bytes"
"errors"
- "flag"
"fmt"
"io/ioutil"
"os/exec"
)
-var update = flag.Bool("update", false, "update .golden files")
-
func init() {
// Mute commands.
addCmd.SetOut(new(bytes.Buffer))
@@ -46,7 +43,7 @@ func compareFiles(pathA, pathB string) error {
// Don't execute diff if it can't be found.
return nil
}
- diffCmd := exec.Command(diffPath, "-u", pathA, pathB)
+ diffCmd := exec.Command(diffPath, "-u", "--strip-trailing-cr", pathA, pathB)
diffCmd.Stdout = output
diffCmd.Stderr = output
@@ -58,27 +55,3 @@ func compareFiles(pathA, pathB string) error {
}
return nil
}
-
-// checkLackFiles checks if all elements of expected are in got.
-func checkLackFiles(expected, got []string) error {
- lacks := make([]string, 0, len(expected))
- for _, ev := range expected {
- if !stringInStringSlice(ev, got) {
- lacks = append(lacks, ev)
- }
- }
- if len(lacks) > 0 {
- return fmt.Errorf("Lack %v file(s): %v", len(lacks), lacks)
- }
- return nil
-}
-
-// stringInStringSlice checks if s is an element of slice.
-func stringInStringSlice(s string, slice []string) bool {
- for _, v := range slice {
- if s == v {
- return true
- }
- }
- return false
-}
diff --git a/cobra/cmd/helpers.go b/cobra/cmd/helpers.go
index cd94b3e31..6a8047e38 100644
--- a/cobra/cmd/helpers.go
+++ b/cobra/cmd/helpers.go
@@ -14,14 +14,12 @@
package cmd
import (
- "bytes"
- "fmt"
- "io"
"os"
"os/exec"
"path/filepath"
"strings"
- "text/template"
+
+ "github.com/spf13/cobra"
)
var srcPaths []string
@@ -43,14 +41,12 @@ func init() {
}
out, err := exec.Command(goExecutable, "env", "GOPATH").Output()
- if err != nil {
- er(err)
- }
+ cobra.CheckErr(err)
toolchainGoPath := strings.TrimSpace(string(out))
goPaths = filepath.SplitList(toolchainGoPath)
if len(goPaths) == 0 {
- er("$GOPATH is not set")
+ cobra.CheckErr("$GOPATH is not set")
}
}
srcPaths = make([]string, 0, len(goPaths))
@@ -58,111 +54,3 @@ func init() {
srcPaths = append(srcPaths, filepath.Join(goPath, "src"))
}
}
-
-func er(msg interface{}) {
- fmt.Println("Error:", msg)
- os.Exit(1)
-}
-
-// isEmpty checks if a given path is empty.
-// Hidden files in path are ignored.
-func isEmpty(path string) bool {
- fi, err := os.Stat(path)
- if err != nil {
- er(err)
- }
-
- if !fi.IsDir() {
- return fi.Size() == 0
- }
-
- f, err := os.Open(path)
- if err != nil {
- er(err)
- }
- defer f.Close()
-
- names, err := f.Readdirnames(-1)
- if err != nil && err != io.EOF {
- er(err)
- }
-
- for _, name := range names {
- if len(name) > 0 && name[0] != '.' {
- return false
- }
- }
- return true
-}
-
-// exists checks if a file or directory exists.
-func exists(path string) bool {
- if path == "" {
- return false
- }
- _, err := os.Stat(path)
- if err == nil {
- return true
- }
- if !os.IsNotExist(err) {
- er(err)
- }
- return false
-}
-
-func executeTemplate(tmplStr string, data interface{}) (string, error) {
- tmpl, err := template.New("").Funcs(template.FuncMap{"comment": commentifyString}).Parse(tmplStr)
- if err != nil {
- return "", err
- }
-
- buf := new(bytes.Buffer)
- err = tmpl.Execute(buf, data)
- return buf.String(), err
-}
-
-func writeStringToFile(path string, s string) error {
- return writeToFile(path, strings.NewReader(s))
-}
-
-// writeToFile writes r to file with path only
-// if file/directory on given path doesn't exist.
-func writeToFile(path string, r io.Reader) error {
- if exists(path) {
- return fmt.Errorf("%v already exists", path)
- }
-
- dir := filepath.Dir(path)
- if dir != "" {
- if err := os.MkdirAll(dir, 0777); err != nil {
- return err
- }
- }
-
- file, err := os.Create(path)
- if err != nil {
- return err
- }
- defer file.Close()
-
- _, err = io.Copy(file, r)
- return err
-}
-
-// commentfyString comments every line of in.
-func commentifyString(in string) string {
- var newlines []string
- lines := strings.Split(in, "\n")
- for _, line := range lines {
- if strings.HasPrefix(line, "//") {
- newlines = append(newlines, line)
- } else {
- if line == "" {
- newlines = append(newlines, "//")
- } else {
- newlines = append(newlines, "// "+line)
- }
- }
- }
- return strings.Join(newlines, "\n")
-}
diff --git a/cobra/cmd/helpers_test.go b/cobra/cmd/helpers_test.go
new file mode 100644
index 000000000..c5d20026c
--- /dev/null
+++ b/cobra/cmd/helpers_test.go
@@ -0,0 +1,9 @@
+package cmd
+
+import "testing"
+
+func assertNoErr(t *testing.T, e error) {
+ if e != nil {
+ t.Error(e)
+ }
+}
diff --git a/cobra/cmd/init.go b/cobra/cmd/init.go
index 504a47850..8c0e617ab 100644
--- a/cobra/cmd/init.go
+++ b/cobra/cmd/init.go
@@ -39,9 +39,7 @@ and the appropriate structure for a Cobra-based CLI application.
Run: func(_ *cobra.Command, args []string) {
projectPath, err := initializeProject(args)
- if err != nil {
- er(err)
- }
+ cobra.CheckErr(err)
fmt.Printf("Your Cobra application is ready at\n%s\n", projectPath)
},
}
@@ -49,7 +47,7 @@ and the appropriate structure for a Cobra-based CLI application.
func init() {
initCmd.Flags().StringVar(&pkgName, "pkg-name", "", "fully qualified pkg name")
- initCmd.MarkFlagRequired("pkg-name")
+ cobra.CheckErr(initCmd.MarkFlagRequired("pkg-name"))
}
func initializeProject(args []string) (string, error) {
diff --git a/cobra/cmd/init_test.go b/cobra/cmd/init_test.go
index c4b3f09a2..6d21ef773 100644
--- a/cobra/cmd/init_test.go
+++ b/cobra/cmd/init_test.go
@@ -59,7 +59,7 @@ func TestGoldenInitCmd(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- initCmd.Flags().Set("pkg-name", tt.pkgName)
+ assertNoErr(t, initCmd.Flags().Set("pkg-name", tt.pkgName))
viper.Set("useViper", true)
projectPath, err := initializeProject(tt.args)
defer func() {
diff --git a/cobra/cmd/license_gpl_2.go b/cobra/cmd/license_gpl_2.go
index 03e05b3a7..a3c2f31cd 100644
--- a/cobra/cmd/license_gpl_2.go
+++ b/cobra/cmd/license_gpl_2.go
@@ -30,7 +30,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-You should have received a copy of the GNU Lesser General Public License
+You should have received a copy of the GNU General Public License
along with this program. If not, see .`,
Text: ` GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
diff --git a/cobra/cmd/licenses.go b/cobra/cmd/licenses.go
index a070134dd..2b3a42438 100644
--- a/cobra/cmd/licenses.go
+++ b/cobra/cmd/licenses.go
@@ -16,9 +16,11 @@
package cmd
import (
+ "fmt"
"strings"
"time"
+ "github.com/spf13/cobra"
"github.com/spf13/viper"
)
@@ -92,7 +94,7 @@ func copyrightLine() string {
func findLicense(name string) License {
found := matchLicense(name)
if found == "" {
- er("unknown license: " + name)
+ cobra.CheckErr(fmt.Errorf("unknown license: " + name))
}
return Licenses[found]
}
diff --git a/cobra/cmd/project.go b/cobra/cmd/project.go
index ecd783d03..bd68a31d7 100644
--- a/cobra/cmd/project.go
+++ b/cobra/cmd/project.go
@@ -5,6 +5,7 @@ import (
"os"
"text/template"
+ "github.com/spf13/cobra"
"github.com/spf13/cobra/cobra/tpl"
)
@@ -49,7 +50,7 @@ func (p *Project) Create() error {
// create cmd/root.go
if _, err = os.Stat(fmt.Sprintf("%s/cmd", p.AbsolutePath)); os.IsNotExist(err) {
- os.Mkdir(fmt.Sprintf("%s/cmd", p.AbsolutePath), 0751)
+ cobra.CheckErr(os.Mkdir(fmt.Sprintf("%s/cmd", p.AbsolutePath), 0751))
}
rootFile, err := os.Create(fmt.Sprintf("%s/cmd/root.go", p.AbsolutePath))
if err != nil {
diff --git a/cobra/cmd/root.go b/cobra/cmd/root.go
index 97f404bbb..aafbde207 100644
--- a/cobra/cmd/root.go
+++ b/cobra/cmd/root.go
@@ -15,8 +15,8 @@ package cmd
import (
"fmt"
+ "os"
- homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@@ -47,8 +47,8 @@ func init() {
rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution")
rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project")
rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration")
- viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
- viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
+ cobra.CheckErr(viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")))
+ cobra.CheckErr(viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")))
viper.SetDefault("author", "NAME HERE ")
viper.SetDefault("license", "apache")
@@ -62,10 +62,8 @@ func initConfig() {
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
- home, err := homedir.Dir()
- if err != nil {
- er(err)
- }
+ home, err := os.UserHomeDir()
+ cobra.CheckErr(err)
// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home)
diff --git a/cobra/cmd/testdata/root.go.golden b/cobra/cmd/testdata/root.go.golden
index 1db829c71..7df19f990 100644
--- a/cobra/cmd/testdata/root.go.golden
+++ b/cobra/cmd/testdata/root.go.golden
@@ -17,10 +17,9 @@ package cmd
import (
"fmt"
- "github.com/spf13/cobra"
"os"
+ "github.com/spf13/cobra"
- homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
@@ -38,16 +37,13 @@ This application is a tool to generate the needed files
to quickly create a Cobra application.`,
// Uncomment the following line if your bare application
// has an action associated with it:
- // Run: func(cmd *cobra.Command, args []string) { },
+ // Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
- if err := rootCmd.Execute(); err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
+ cobra.CheckErr(rootCmd.Execute())
}
func init() {
@@ -71,11 +67,8 @@ func initConfig() {
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
- home, err := homedir.Dir()
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
+ home, err := os.UserHomeDir()
+ cobra.CheckErr(err)
// Search config in home directory with name ".testproject" (without extension).
viper.AddConfigPath(home)
@@ -86,6 +79,6 @@ func initConfig() {
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
- fmt.Println("Using config file:", viper.ConfigFileUsed())
+ fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
}
diff --git a/cobra/tpl/main.go b/cobra/tpl/main.go
index 4348e5616..7d60fdd37 100644
--- a/cobra/tpl/main.go
+++ b/cobra/tpl/main.go
@@ -24,12 +24,10 @@ package cmd
import (
"fmt"
- "github.com/spf13/cobra"
"os"
+ "github.com/spf13/cobra"
{{ if .Viper }}
- homedir "github.com/mitchellh/go-homedir"
- "github.com/spf13/viper"
-{{ end -}}
+ "github.com/spf13/viper"{{ end }}
)
{{ if .Viper -}}
@@ -48,16 +46,13 @@ This application is a tool to generate the needed files
to quickly create a Cobra application.` + "`" + `,
// Uncomment the following line if your bare application
// has an action associated with it:
- // Run: func(cmd *cobra.Command, args []string) { },
+ // Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
- if err := rootCmd.Execute(); err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
+ cobra.CheckErr(rootCmd.Execute())
}
func init() {
@@ -85,11 +80,8 @@ func initConfig() {
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
- home, err := homedir.Dir()
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
+ home, err := os.UserHomeDir()
+ cobra.CheckErr(err)
// Search config in home directory with name ".{{ .AppName }}" (without extension).
viper.AddConfigPath(home)
@@ -100,7 +92,7 @@ func initConfig() {
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
- fmt.Println("Using config file:", viper.ConfigFileUsed())
+ fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
}
{{- end }}
diff --git a/cobra_test.go b/cobra_test.go
index 0d1755bdb..1219cc079 100644
--- a/cobra_test.go
+++ b/cobra_test.go
@@ -5,6 +5,12 @@ import (
"text/template"
)
+func assertNoErr(t *testing.T, e error) {
+ if e != nil {
+ t.Error(e)
+ }
+}
+
func TestAddTemplateFunctions(t *testing.T) {
AddTemplateFunc("t", func() bool { return true })
AddTemplateFuncs(template.FuncMap{
diff --git a/command.go b/command.go
index 77b399e02..44419f31c 100644
--- a/command.go
+++ b/command.go
@@ -84,9 +84,6 @@ type Command struct {
// Deprecated defines, if this command is deprecated and should print this string when used.
Deprecated string
- // Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
- Hidden bool
-
// Annotations are key/value pairs that can be used by applications to identify or
// group commands.
Annotations map[string]string
@@ -126,55 +123,6 @@ type Command struct {
// PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) error
- // SilenceErrors is an option to quiet errors down stream.
- SilenceErrors bool
-
- // SilenceUsage is an option to silence usage when an error occurs.
- SilenceUsage bool
-
- // DisableFlagParsing disables the flag parsing.
- // If this is true all flags will be passed to the command as arguments.
- DisableFlagParsing bool
-
- // DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
- // will be printed by generating docs for this command.
- DisableAutoGenTag bool
-
- // DisableFlagsInUseLine will disable the addition of [flags] to the usage
- // line of a command when printing help or generating docs
- DisableFlagsInUseLine bool
-
- // DisableSuggestions disables the suggestions based on Levenshtein distance
- // that go along with 'unknown command' messages.
- DisableSuggestions bool
- // SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
- // Must be > 0.
- SuggestionsMinimumDistance int
-
- // TraverseChildren parses flags on all parents before executing child command.
- TraverseChildren bool
-
- // FParseErrWhitelist flag parse errors to be ignored
- FParseErrWhitelist FParseErrWhitelist
-
- ctx context.Context
-
- // commands is the list of commands supported by this program.
- commands []*Command
- // parent is a parent command for this command.
- parent *Command
- // Max lengths of commands' string lengths for use in padding.
- commandsMaxUseLen int
- commandsMaxCommandPathLen int
- commandsMaxNameLen int
- // commandsAreSorted defines, if command slice are sorted or not.
- commandsAreSorted bool
- // commandCalledAs is the name or alias value used to call this command.
- commandCalledAs struct {
- name string
- called bool
- }
-
// args is actual args parsed from flags.
args []string
// flagErrorBuf contains all error messages from pflag.
@@ -216,6 +164,60 @@ type Command struct {
outWriter io.Writer
// errWriter is a writer defined by the user that replaces stderr
errWriter io.Writer
+
+ //FParseErrWhitelist flag parse errors to be ignored
+ FParseErrWhitelist FParseErrWhitelist
+
+ // commandsAreSorted defines, if command slice are sorted or not.
+ commandsAreSorted bool
+ // commandCalledAs is the name or alias value used to call this command.
+ commandCalledAs struct {
+ name string
+ called bool
+ }
+
+ ctx context.Context
+
+ // commands is the list of commands supported by this program.
+ commands []*Command
+ // parent is a parent command for this command.
+ parent *Command
+ // Max lengths of commands' string lengths for use in padding.
+ commandsMaxUseLen int
+ commandsMaxCommandPathLen int
+ commandsMaxNameLen int
+
+ // TraverseChildren parses flags on all parents before executing child command.
+ TraverseChildren bool
+
+ // Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
+ Hidden bool
+
+ // SilenceErrors is an option to quiet errors down stream.
+ SilenceErrors bool
+
+ // SilenceUsage is an option to silence usage when an error occurs.
+ SilenceUsage bool
+
+ // DisableFlagParsing disables the flag parsing.
+ // If this is true all flags will be passed to the command as arguments.
+ DisableFlagParsing bool
+
+ // DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
+ // will be printed by generating docs for this command.
+ DisableAutoGenTag bool
+
+ // DisableFlagsInUseLine will disable the addition of [flags] to the usage
+ // line of a command when printing help or generating docs
+ DisableFlagsInUseLine bool
+
+ // DisableSuggestions disables the suggestions based on Levenshtein distance
+ // that go along with 'unknown command' messages.
+ DisableSuggestions bool
+
+ // SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
+ // Must be > 0.
+ SuggestionsMinimumDistance int
}
// Context returns underlying command context. If command wasn't
@@ -418,7 +420,7 @@ func (c *Command) UsageString() string {
c.outWriter = bb
c.errWriter = bb
- c.Usage()
+ CheckErr(c.Usage())
// Setting things back to normal
c.outWriter = tmpOutput
@@ -979,10 +981,15 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
return cmd, err
}
+// ValidateArgs returns an error if any any positional args that are not in
+// the `ValidArgs` field of `Command`
func (c *Command) ValidateArgs(args []string) error {
if c.Args == nil {
return nil
}
+ if err := validateArgs(c, args); err != nil {
+ return err
+ }
return c.Args(c, args)
}
@@ -1087,10 +1094,10 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
cmd, _, e := c.Root().Find(args)
if cmd == nil || e != nil {
c.Printf("Unknown help topic %#q\n", args)
- c.Root().Usage()
+ CheckErr(c.Root().Usage())
} else {
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
- cmd.Help()
+ CheckErr(cmd.Help())
}
},
}
diff --git a/command_test.go b/command_test.go
index 3a47a81b3..9640fc5dc 100644
--- a/command_test.go
+++ b/command_test.go
@@ -58,6 +58,8 @@ func checkStringOmits(t *testing.T, got, expected string) {
}
}
+const onetwo = "one two"
+
func TestSingleCommand(t *testing.T) {
var rootCmdArgs []string
rootCmd := &Command{
@@ -78,9 +80,8 @@ func TestSingleCommand(t *testing.T) {
}
got := strings.Join(rootCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("rootCmdArgs expected: %q, got: %q", expected, got)
+ if got != onetwo {
+ t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
}
}
@@ -104,9 +105,8 @@ func TestChildCommand(t *testing.T) {
}
got := strings.Join(child1CmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("child1CmdArgs expected: %q, got: %q", expected, got)
+ if got != onetwo {
+ t.Errorf("child1CmdArgs expected: %q, got: %q", onetwo, got)
}
}
@@ -145,7 +145,7 @@ func TestSubcommandExecuteC(t *testing.T) {
}
if c.Name() != "child" {
- t.Errorf(`invalid command returned from ExecuteC: expected "child"', got %q`, c.Name())
+ t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
}
}
@@ -243,9 +243,8 @@ func TestCommandAlias(t *testing.T) {
}
got := strings.Join(timesCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
+ if got != onetwo {
+ t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
}
}
@@ -271,9 +270,8 @@ func TestEnablePrefixMatching(t *testing.T) {
}
got := strings.Join(aCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("aCmdArgs expected: %q, got: %q", expected, got)
+ if got != onetwo {
+ t.Errorf("aCmdArgs expected: %q, got: %q", onetwo, got)
}
EnablePrefixMatching = false
@@ -307,9 +305,8 @@ func TestAliasPrefixMatching(t *testing.T) {
}
got := strings.Join(timesCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
+ if got != onetwo {
+ t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
}
EnablePrefixMatching = false
@@ -338,9 +335,8 @@ func TestChildSameName(t *testing.T) {
}
got := strings.Join(fooCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
+ if got != onetwo {
+ t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
}
}
@@ -368,9 +364,8 @@ func TestGrandChildSameName(t *testing.T) {
}
got := strings.Join(fooCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
+ if got != onetwo {
+ t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
}
}
@@ -406,9 +401,8 @@ func TestFlagLong(t *testing.T) {
}
got := strings.Join(cArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("Expected arguments: %q, got %q", expected, got)
+ if got != onetwo {
+ t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
}
}
@@ -441,9 +435,8 @@ func TestFlagShort(t *testing.T) {
}
got := strings.Join(cArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("Expected arguments: %q, got %q", expected, got)
+ if got != onetwo {
+ t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
}
}
@@ -645,9 +638,8 @@ func TestPersistentFlagsOnSameCommand(t *testing.T) {
}
got := strings.Join(rootCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("rootCmdArgs expected: %q, got %q", expected, got)
+ if got != onetwo {
+ t.Errorf("rootCmdArgs expected: %q, got %q", onetwo, got)
}
if flagValue != 7 {
t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
@@ -731,9 +723,8 @@ func TestPersistentFlagsOnChild(t *testing.T) {
}
got := strings.Join(childCmdArgs, " ")
- expected := "one two"
- if got != expected {
- t.Errorf("childCmdArgs expected: %q, got %q", expected, got)
+ if got != onetwo {
+ t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
}
if parentFlagValue != 8 {
t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue)
@@ -746,9 +737,9 @@ func TestPersistentFlagsOnChild(t *testing.T) {
func TestRequiredFlags(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun}
c.Flags().String("foo1", "", "")
- c.MarkFlagRequired("foo1")
+ assertNoErr(t, c.MarkFlagRequired("foo1"))
c.Flags().String("foo2", "", "")
- c.MarkFlagRequired("foo2")
+ assertNoErr(t, c.MarkFlagRequired("foo2"))
c.Flags().String("bar", "", "")
expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2")
@@ -764,16 +755,16 @@ func TestRequiredFlags(t *testing.T) {
func TestPersistentRequiredFlags(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun}
parent.PersistentFlags().String("foo1", "", "")
- parent.MarkPersistentFlagRequired("foo1")
+ assertNoErr(t, parent.MarkPersistentFlagRequired("foo1"))
parent.PersistentFlags().String("foo2", "", "")
- parent.MarkPersistentFlagRequired("foo2")
+ assertNoErr(t, parent.MarkPersistentFlagRequired("foo2"))
parent.Flags().String("foo3", "", "")
child := &Command{Use: "child", Run: emptyRun}
child.Flags().String("bar1", "", "")
- child.MarkFlagRequired("bar1")
+ assertNoErr(t, child.MarkFlagRequired("bar1"))
child.Flags().String("bar2", "", "")
- child.MarkFlagRequired("bar2")
+ assertNoErr(t, child.MarkFlagRequired("bar2"))
child.Flags().String("bar3", "", "")
parent.AddCommand(child)
@@ -793,7 +784,7 @@ func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun}
parent.PersistentFlags().Bool("foo", false, "")
flag := parent.PersistentFlags().Lookup("foo")
- parent.MarkPersistentFlagRequired("foo")
+ assertNoErr(t, parent.MarkPersistentFlagRequired("foo"))
child := &Command{Use: "child", Run: emptyRun}
child.DisableFlagParsing = true
@@ -1299,20 +1290,19 @@ func TestHooks(t *testing.T) {
t.Errorf("Unexpected error: %v", err)
}
- if persPreArgs != "one two" {
- t.Errorf("Expected persPreArgs %q, got %q", "one two", persPreArgs)
- }
- if preArgs != "one two" {
- t.Errorf("Expected preArgs %q, got %q", "one two", preArgs)
- }
- if runArgs != "one two" {
- t.Errorf("Expected runArgs %q, got %q", "one two", runArgs)
- }
- if postArgs != "one two" {
- t.Errorf("Expected postArgs %q, got %q", "one two", postArgs)
- }
- if persPostArgs != "one two" {
- t.Errorf("Expected persPostArgs %q, got %q", "one two", persPostArgs)
+ for _, v := range []struct {
+ name string
+ got string
+ }{
+ {"persPreArgs", persPreArgs},
+ {"preArgs", preArgs},
+ {"runArgs", runArgs},
+ {"postArgs", postArgs},
+ {"persPostArgs", persPostArgs},
+ } {
+ if v.got != onetwo {
+ t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
+ }
}
}
@@ -1380,44 +1370,42 @@ func TestPersistentHooks(t *testing.T) {
t.Errorf("Unexpected error: %v", err)
}
- // TODO: currently PersistenPreRun* defined in parent does not
- // run if the matchin child subcommand has PersistenPreRun.
- // If the behavior changes (https://github.com/spf13/cobra/issues/252)
- // this test must be fixed.
- if parentPersPreArgs != "" {
- t.Errorf("Expected blank parentPersPreArgs, got %q", parentPersPreArgs)
- }
- if parentPreArgs != "" {
- t.Errorf("Expected blank parentPreArgs, got %q", parentPreArgs)
- }
- if parentRunArgs != "" {
- t.Errorf("Expected blank parentRunArgs, got %q", parentRunArgs)
- }
- if parentPostArgs != "" {
- t.Errorf("Expected blank parentPostArgs, got %q", parentPostArgs)
- }
- // TODO: currently PersistenPostRun* defined in parent does not
- // run if the matchin child subcommand has PersistenPostRun.
- // If the behavior changes (https://github.com/spf13/cobra/issues/252)
- // this test must be fixed.
- if parentPersPostArgs != "" {
- t.Errorf("Expected blank parentPersPostArgs, got %q", parentPersPostArgs)
+ for _, v := range []struct {
+ name string
+ got string
+ }{
+ // TODO: currently PersistenPreRun* defined in parent does not
+ // run if the matchin child subcommand has PersistenPreRun.
+ // If the behavior changes (https://github.com/spf13/cobra/issues/252)
+ // this test must be fixed.
+ {"parentPersPreArgs", parentPersPreArgs},
+ {"parentPreArgs", parentPreArgs},
+ {"parentRunArgs", parentRunArgs},
+ {"parentPostArgs", parentPostArgs},
+ // TODO: currently PersistenPostRun* defined in parent does not
+ // run if the matchin child subcommand has PersistenPostRun.
+ // If the behavior changes (https://github.com/spf13/cobra/issues/252)
+ // this test must be fixed.
+ {"parentPersPostArgs", parentPersPostArgs},
+ } {
+ if v.got != "" {
+ t.Errorf("Expected blank %s, got %q", v.name, v.got)
+ }
}
- if childPersPreArgs != "one two" {
- t.Errorf("Expected childPersPreArgs %q, got %q", "one two", childPersPreArgs)
- }
- if childPreArgs != "one two" {
- t.Errorf("Expected childPreArgs %q, got %q", "one two", childPreArgs)
- }
- if childRunArgs != "one two" {
- t.Errorf("Expected childRunArgs %q, got %q", "one two", childRunArgs)
- }
- if childPostArgs != "one two" {
- t.Errorf("Expected childPostArgs %q, got %q", "one two", childPostArgs)
- }
- if childPersPostArgs != "one two" {
- t.Errorf("Expected childPersPostArgs %q, got %q", "one two", childPersPostArgs)
+ for _, v := range []struct {
+ name string
+ got string
+ }{
+ {"childPersPreArgs", childPersPreArgs},
+ {"childPreArgs", childPreArgs},
+ {"childRunArgs", childRunArgs},
+ {"childPostArgs", childPostArgs},
+ {"childPersPostArgs", childPersPostArgs},
+ } {
+ if v.got != onetwo {
+ t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
+ }
}
}
@@ -1741,7 +1729,7 @@ func TestMergeCommandLineToFlags(t *testing.T) {
func TestUseDeprecatedFlags(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun}
c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
- c.Flags().MarkDeprecated("deprecated", "This flag is deprecated")
+ assertNoErr(t, c.Flags().MarkDeprecated("deprecated", "This flag is deprecated"))
output, err := executeCommand(c, "c", "-d")
if err != nil {
@@ -1868,7 +1856,6 @@ type calledAsTestcase struct {
call string
want string
epm bool
- tc bool
}
func (tc *calledAsTestcase) test(t *testing.T) {
@@ -1890,7 +1877,7 @@ func (tc *calledAsTestcase) test(t *testing.T) {
parent.SetOut(output)
parent.SetErr(output)
- parent.Execute()
+ _ = parent.Execute()
if called == nil {
if tc.call != "" {
@@ -1908,18 +1895,18 @@ func (tc *calledAsTestcase) test(t *testing.T) {
func TestCalledAs(t *testing.T) {
tests := map[string]calledAsTestcase{
- "find/no-args": {nil, "parent", "parent", false, false},
- "find/real-name": {[]string{"child1"}, "child1", "child1", false, false},
- "find/full-alias": {[]string{"that"}, "child2", "that", false, false},
- "find/part-no-prefix": {[]string{"thi"}, "", "", false, false},
- "find/part-alias": {[]string{"thi"}, "child1", "this", true, false},
- "find/conflict": {[]string{"th"}, "", "", true, false},
- "traverse/no-args": {nil, "parent", "parent", false, true},
- "traverse/real-name": {[]string{"child1"}, "child1", "child1", false, true},
- "traverse/full-alias": {[]string{"that"}, "child2", "that", false, true},
- "traverse/part-no-prefix": {[]string{"thi"}, "", "", false, true},
- "traverse/part-alias": {[]string{"thi"}, "child1", "this", true, true},
- "traverse/conflict": {[]string{"th"}, "", "", true, true},
+ "find/no-args": {nil, "parent", "parent", false},
+ "find/real-name": {[]string{"child1"}, "child1", "child1", false},
+ "find/full-alias": {[]string{"that"}, "child2", "that", false},
+ "find/part-no-prefix": {[]string{"thi"}, "", "", false},
+ "find/part-alias": {[]string{"thi"}, "child1", "this", true},
+ "find/conflict": {[]string{"th"}, "", "", true},
+ "traverse/no-args": {nil, "parent", "parent", false},
+ "traverse/real-name": {[]string{"child1"}, "child1", "child1", false},
+ "traverse/full-alias": {[]string{"that"}, "child2", "that", false},
+ "traverse/part-no-prefix": {[]string{"thi"}, "", "", false},
+ "traverse/part-alias": {[]string{"thi"}, "child1", "this", true},
+ "traverse/conflict": {[]string{"th"}, "", "", true},
}
for name, tc := range tests {
diff --git a/custom_completions.go b/custom_completions.go
index f9e88e081..fa060c147 100644
--- a/custom_completions.go
+++ b/custom_completions.go
@@ -527,13 +527,13 @@ func CompDebug(msg string, printToStdErr bool) {
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err == nil {
defer f.Close()
- f.WriteString(msg)
+ WriteStringAndCheck(f, msg)
}
}
if printToStdErr {
// Must print to stderr for this not to be read by the completion script.
- fmt.Fprintf(os.Stderr, msg)
+ fmt.Fprint(os.Stderr, msg)
}
}
diff --git a/custom_completions_test.go b/custom_completions_test.go
index 14ec5a9eb..ede809ed4 100644
--- a/custom_completions_test.go
+++ b/custom_completions_test.go
@@ -780,17 +780,17 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) {
rootCmd.AddCommand(childCmd)
rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag")
- rootCmd.MarkFlagRequired("requiredFlag")
+ assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag"))
requiredFlag := rootCmd.Flags().Lookup("requiredFlag")
rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent")
- rootCmd.MarkPersistentFlagRequired("requiredPersistent")
+ assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent"))
requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent")
rootCmd.Flags().StringP("release", "R", "", "Release name")
childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag")
- childCmd.MarkFlagRequired("subRequired")
+ assertNoErr(t, childCmd.MarkFlagRequired("subRequired"))
childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag")
// Test that a required flag is suggested even without the - prefix
@@ -964,19 +964,19 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) {
// No extensions. Should be ignored.
rootCmd.Flags().StringP("file", "f", "", "file flag")
- rootCmd.MarkFlagFilename("file")
+ assertNoErr(t, rootCmd.MarkFlagFilename("file"))
// Single extension
rootCmd.Flags().StringP("log", "l", "", "log flag")
- rootCmd.MarkFlagFilename("log", "log")
+ assertNoErr(t, rootCmd.MarkFlagFilename("log", "log"))
// Multiple extensions
rootCmd.Flags().StringP("yaml", "y", "", "yaml flag")
- rootCmd.MarkFlagFilename("yaml", "yaml", "yml")
+ assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml"))
// Directly using annotation
rootCmd.Flags().StringP("text", "t", "", "text flag")
- rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"})
+ assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"}))
// Test that the completion logic returns the proper info for the completion
// script to handle the file filtering
@@ -1086,15 +1086,15 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) {
// Filter directories
rootCmd.Flags().StringP("dir", "d", "", "dir flag")
- rootCmd.MarkFlagDirname("dir")
+ assertNoErr(t, rootCmd.MarkFlagDirname("dir"))
// Filter directories within a directory
rootCmd.Flags().StringP("subdir", "s", "", "subdir")
- rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"})
+ assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"}))
// Multiple directory specification get ignored
rootCmd.Flags().StringP("manydir", "m", "", "manydir")
- rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"})
+ assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"}))
// Test that the completion logic returns the proper info for the completion
// script to handle the directory filtering
@@ -1430,7 +1430,7 @@ func TestValidArgsFuncInBashScript(t *testing.T) {
rootCmd.AddCommand(child)
buf := new(bytes.Buffer)
- rootCmd.GenBashCompletion(buf)
+ assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String()
check(t, output, "has_completion_function=1")
@@ -1445,7 +1445,7 @@ func TestNoValidArgsFuncInBashScript(t *testing.T) {
rootCmd.AddCommand(child)
buf := new(bytes.Buffer)
- rootCmd.GenBashCompletion(buf)
+ assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String()
checkOmit(t, output, "has_completion_function=1")
@@ -1461,7 +1461,7 @@ func TestCompleteCmdInBashScript(t *testing.T) {
rootCmd.AddCommand(child)
buf := new(bytes.Buffer)
- rootCmd.GenBashCompletion(buf)
+ assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String()
check(t, output, ShellCompNoDescRequestCmd)
@@ -1477,7 +1477,7 @@ func TestCompleteNoDesCmdInZshScript(t *testing.T) {
rootCmd.AddCommand(child)
buf := new(bytes.Buffer)
- rootCmd.GenZshCompletionNoDesc(buf)
+ assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf))
output := buf.String()
check(t, output, ShellCompNoDescRequestCmd)
@@ -1493,7 +1493,7 @@ func TestCompleteCmdInZshScript(t *testing.T) {
rootCmd.AddCommand(child)
buf := new(bytes.Buffer)
- rootCmd.GenZshCompletion(buf)
+ assertNoErr(t, rootCmd.GenZshCompletion(buf))
output := buf.String()
check(t, output, ShellCompRequestCmd)
@@ -1506,7 +1506,7 @@ func TestFlagCompletionInGo(t *testing.T) {
Run: emptyRun,
}
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
- rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{}
for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
if strings.HasPrefix(comp, toComplete) {
@@ -1514,9 +1514,9 @@ func TestFlagCompletionInGo(t *testing.T) {
}
}
return completions, ShellCompDirectiveDefault
- })
+ }))
rootCmd.Flags().String("filename", "", "Enter a filename")
- rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{}
for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
if strings.HasPrefix(comp, toComplete) {
@@ -1524,7 +1524,7 @@ func TestFlagCompletionInGo(t *testing.T) {
}
}
return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
- })
+ }))
// Test completing an empty string
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "")
@@ -1703,7 +1703,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
Run: emptyRun,
}
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
- rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{}
for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
if strings.HasPrefix(comp, toComplete) {
@@ -1711,9 +1711,9 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
}
}
return completions, ShellCompDirectiveDefault
- })
+ }))
rootCmd.Flags().String("filename", "", "Enter a filename")
- rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{}
for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
if strings.HasPrefix(comp, toComplete) {
@@ -1721,7 +1721,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
}
}
return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
- })
+ }))
// Test completing an empty string
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")
diff --git a/doc/man_docs.go b/doc/man_docs.go
index b29a67786..b9b3e3e08 100644
--- a/doc/man_docs.go
+++ b/doc/man_docs.go
@@ -139,25 +139,25 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
return nil
}
-func manPreamble(buf *bytes.Buffer, header *GenManHeader, cmd *cobra.Command, dashedName string) {
+func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command, dashedName string) {
description := cmd.Long
if len(description) == 0 {
description = cmd.Short
}
- buf.WriteString(fmt.Sprintf(`%% %s(%s)%s
+ cobra.WriteStringAndCheck(buf, fmt.Sprintf(`%% %s(%s)%s
%% %s
%% %s
# NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual))
- buf.WriteString(fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
- buf.WriteString("# SYNOPSIS\n")
- buf.WriteString(fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
- buf.WriteString("# DESCRIPTION\n")
- buf.WriteString(description + "\n\n")
+ cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
+ cobra.WriteStringAndCheck(buf, "# SYNOPSIS\n")
+ cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
+ cobra.WriteStringAndCheck(buf, "# DESCRIPTION\n")
+ cobra.WriteStringAndCheck(buf, description+"\n\n")
}
-func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
+func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden {
return
@@ -181,22 +181,22 @@ func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
format += "]"
}
format += "\n\t%s\n\n"
- buf.WriteString(fmt.Sprintf(format, flag.DefValue, flag.Usage))
+ cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, flag.Usage))
})
}
-func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) {
+func manPrintOptions(buf io.StringWriter, command *cobra.Command) {
flags := command.NonInheritedFlags()
if flags.HasAvailableFlags() {
- buf.WriteString("# OPTIONS\n")
+ cobra.WriteStringAndCheck(buf, "# OPTIONS\n")
manPrintFlags(buf, flags)
- buf.WriteString("\n")
+ cobra.WriteStringAndCheck(buf, "\n")
}
flags = command.InheritedFlags()
if flags.HasAvailableFlags() {
- buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n")
+ cobra.WriteStringAndCheck(buf, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
manPrintFlags(buf, flags)
- buf.WriteString("\n")
+ cobra.WriteStringAndCheck(buf, "\n")
}
}
diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go
index ee9b87535..aa3f5f2a1 100644
--- a/doc/man_docs_test.go
+++ b/doc/man_docs_test.go
@@ -13,6 +13,12 @@ import (
"github.com/spf13/cobra"
)
+func assertNoErr(t *testing.T, e error) {
+ if e != nil {
+ t.Error(e)
+ }
+}
+
func translate(in string) string {
return strings.Replace(in, "-", "\\-", -1)
}
@@ -133,7 +139,7 @@ func TestGenManSeeAlso(t *testing.T) {
func TestManPrintFlagsHidesShortDeperecated(t *testing.T) {
c := &cobra.Command{}
c.Flags().StringP("foo", "f", "default", "Foo flag")
- c.Flags().MarkShorthandDeprecated("foo", "don't use it no more")
+ assertNoErr(t, c.Flags().MarkShorthandDeprecated("foo", "don't use it no more"))
buf := new(bytes.Buffer)
manPrintFlags(buf, c.Flags())
diff --git a/doc/man_examples_test.go b/doc/man_examples_test.go
index db6604268..e20a34c38 100644
--- a/doc/man_examples_test.go
+++ b/doc/man_examples_test.go
@@ -17,7 +17,7 @@ func ExampleGenManTree() {
Title: "MINE",
Section: "3",
}
- doc.GenManTree(cmd, header, "/tmp")
+ cobra.CheckErr(doc.GenManTree(cmd, header, "/tmp"))
}
func ExampleGenMan() {
@@ -30,6 +30,6 @@ func ExampleGenMan() {
Section: "3",
}
out := new(bytes.Buffer)
- doc.GenMan(cmd, header, out)
+ cobra.CheckErr(doc.GenMan(cmd, header, out))
fmt.Print(out.String())
}
diff --git a/fish_completions.go b/fish_completions.go
index 66a6357cb..bea92b92f 100644
--- a/fish_completions.go
+++ b/fish_completions.go
@@ -8,7 +8,7 @@ import (
"strings"
)
-func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
+func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
// Variables should not contain a '-' or ':' character
nameForVar := name
nameForVar = strings.Replace(nameForVar, "-", "_", -1)
@@ -18,8 +18,8 @@ func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
if !includeDesc {
compCmd = ShellCompNoDescRequestCmd
}
- buf.WriteString(fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
- buf.WriteString(fmt.Sprintf(`
+ WriteStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
+ WriteStringAndCheck(buf, fmt.Sprintf(`
function __%[1]s_debug
set file "$BASH_COMP_DEBUG_FILE"
if test -n "$file"
diff --git a/fish_completions_test.go b/fish_completions_test.go
index 532b6055a..a3171e481 100644
--- a/fish_completions_test.go
+++ b/fish_completions_test.go
@@ -15,7 +15,7 @@ func TestCompleteNoDesCmdInFishScript(t *testing.T) {
rootCmd.AddCommand(child)
buf := new(bytes.Buffer)
- rootCmd.GenFishCompletion(buf, false)
+ assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
output := buf.String()
check(t, output, ShellCompNoDescRequestCmd)
@@ -31,7 +31,7 @@ func TestCompleteCmdInFishScript(t *testing.T) {
rootCmd.AddCommand(child)
buf := new(bytes.Buffer)
- rootCmd.GenFishCompletion(buf, true)
+ assertNoErr(t, rootCmd.GenFishCompletion(buf, true))
output := buf.String()
check(t, output, ShellCompRequestCmd)
@@ -41,7 +41,7 @@ func TestCompleteCmdInFishScript(t *testing.T) {
func TestProgWithDash(t *testing.T) {
rootCmd := &Command{Use: "root-dash", Args: NoArgs, Run: emptyRun}
buf := new(bytes.Buffer)
- rootCmd.GenFishCompletion(buf, false)
+ assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
output := buf.String()
// Functions name should have replace the '-'
@@ -56,7 +56,7 @@ func TestProgWithDash(t *testing.T) {
func TestProgWithColon(t *testing.T) {
rootCmd := &Command{Use: "root:colon", Args: NoArgs, Run: emptyRun}
buf := new(bytes.Buffer)
- rootCmd.GenFishCompletion(buf, false)
+ assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
output := buf.String()
// Functions name should have replace the ':'
diff --git a/powershell_completions_test.go b/powershell_completions_test.go
index 29b609de0..30b354b4b 100644
--- a/powershell_completions_test.go
+++ b/powershell_completions_test.go
@@ -109,7 +109,7 @@ func TestPowerShellCompletion(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
buf := new(bytes.Buffer)
- tc.root.GenPowerShellCompletion(buf)
+ assertNoErr(t, tc.root.GenPowerShellCompletion(buf))
output := buf.String()
for _, expectedExpression := range tc.expectedExpressions {
diff --git a/shell_completions.md b/shell_completions.md
index d8416ab1d..91ce7724c 100644
--- a/shell_completions.md
+++ b/shell_completions.md
@@ -91,8 +91,7 @@ cmd := &cobra.Command{
Long: get_long,
Example: get_example,
Run: func(cmd *cobra.Command, args []string) {
- err := RunGet(f, out, cmd, args)
- util.CheckErr(err)
+ cobra.CheckErr(RunGet(f, out, cmd, args))
},
ValidArgs: validArgs,
}
@@ -124,7 +123,7 @@ the completion algorithm if entered manually, e.g. in:
```bash
$ kubectl get rc [tab][tab]
-backend frontend database
+backend frontend database
```
Note that without declaring `rc` as an alias, the completion algorithm would not know to show the list of
@@ -246,7 +245,7 @@ and you'll get something like
```bash
$ kubectl exec [tab][tab]
--c --container= -p --pod=
+-c --container= -p --pod=
```
### Specify dynamic flag completion
diff --git a/zsh_completions.go b/zsh_completions.go
index c25ce680c..b04cd4253 100644
--- a/zsh_completions.go
+++ b/zsh_completions.go
@@ -70,12 +70,12 @@ func (c *Command) genZshCompletion(w io.Writer, includeDesc bool) error {
return err
}
-func genZshComp(buf *bytes.Buffer, name string, includeDesc bool) {
+func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
compCmd := ShellCompRequestCmd
if !includeDesc {
compCmd = ShellCompNoDescRequestCmd
}
- buf.WriteString(fmt.Sprintf(`#compdef _%[1]s %[1]s
+ WriteStringAndCheck(buf, fmt.Sprintf(`#compdef _%[1]s %[1]s
# zsh completion for %-36[1]s -*- shell-script -*-