From 20ef273e28f801fba470500c5fc7d0abaf2da3a3 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 7 Jun 2019 18:33:29 +0200 Subject: [PATCH] use golangci-lint (#876) (#1044) --- .gitattributes | 1 + .golangci.yml | 25 ++++ .travis.yml | 7 +- Makefile | 18 +-- README.md | 4 +- bash_completions.go | 129 +++++++++---------- bash_completions_test.go | 37 +++--- cobra.go | 16 +++ cobra/cmd/add.go | 4 +- cobra/cmd/add_test.go | 6 +- cobra/cmd/golden_test.go | 27 ---- cobra/cmd/helpers.go | 110 +---------------- cobra/cmd/init.go | 2 +- cobra/cmd/init_test.go | 4 +- cobra/cmd/licenses.go | 3 +- cobra/cmd/project.go | 2 +- cobra/cmd/root.go | 8 +- cobra/cmd/testdata/root.go.golden | 7 +- cobra/tpl/main.go | 10 +- command.go | 112 ++++++++--------- command_test.go | 197 ++++++++++++++---------------- custom_completions.go | 4 +- custom_completions_test.go | 26 ++-- doc/man_docs.go | 28 ++--- doc/man_docs_test.go | 2 +- doc/man_examples_test.go | 8 +- doc/util.go | 9 ++ fish_completions.go | 6 +- go.mod | 1 - go.sum | 2 - powershell_completions_test.go | 2 +- zsh_completions_test.go | 28 ++--- 32 files changed, 376 insertions(+), 469 deletions(-) create mode 100644 .gitattributes create mode 100644 .golangci.yml 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/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..8450df644 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,25 @@ +run: + deadline: 5m + +linters: + enable: + - deadcode + - errcheck + - gas + - goconst + - goimports + - golint + - govet + - ineffassign + - interfacer + - maligned + - megacheck + - structcheck + - unconvert + - varcheck + enable-all: false + disable-all: true +# presets: +# - bugs +# - unused + fast: false diff --git a/.travis.yml b/.travis.yml index 9543eee99..35b3500ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: go stages: - - diff - test - build @@ -10,9 +9,12 @@ 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://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin latest env: - GO111MODULE=on @@ -21,9 +23,6 @@ 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 diff --git a/Makefile b/Makefile index e9740d1e1..dd2ccd601 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 lint 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 c5c5fc662..fbc31d8fb 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,6 @@ import ( "fmt" "os" - homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -267,7 +266,7 @@ func initConfig() { viper.SetConfigFile(cfgFile) } else { // Find home directory. - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { er(err) } @@ -276,7 +275,6 @@ func initConfig() { viper.AddConfigPath(home) viper.SetConfigName(".cobra") } - viper.AutomaticEnv() if err := viper.ReadInConfig(); err == nil { diff --git a/bash_completions.go b/bash_completions.go index 1e27188c3..8f2866af4 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) { + WrStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) + WrStringAndCheck(buf, fmt.Sprintf(` __%[1]s_debug() { if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then @@ -347,10 +347,10 @@ __%[1]s_handle_word() `, name, ShellCompNoDescRequestCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp)) } -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(`{ + WrStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name)) + WrStringAndCheck(buf, fmt.Sprintf(`{ local cur prev words cword declare -A flaghash 2>/dev/null || : declare -A aliashash 2>/dev/null || : @@ -377,33 +377,33 @@ func writePostscript(buf *bytes.Buffer, name string) { } `, name)) - buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then + WrStringAndCheck(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") + WrStringAndCheck(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) { + WrStringAndCheck(buf, " commands=()\n") for _, c := range cmd.Commands() { if !c.IsAvailableCommand() || c == cmd.helpCommand { continue } - buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) + WrStringAndCheck(buf, fmt.Sprintf(" commands+=(%q)\n", c.Name())) writeCmdAliases(buf, c) } - buf.WriteString("\n") + WrStringAndCheck(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)) + WrStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) var ext string if len(value) > 0 { @@ -411,17 +411,18 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s } else { ext = "_filedir" } - buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) + WrStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext)) case BashCompCustom: - buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + WrStringAndCheck(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)) + WrStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) } else { - buf.WriteString(" flags_completion+=(:)\n") + WrStringAndCheck(buf, " flags_completion+=(:)\n") } case BashCompSubdirsInDir: - buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + WrStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) var ext string if len(value) == 1 { @@ -429,45 +430,47 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s } else { ext = "_filedir -d" } - buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) + WrStringAndCheck(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 + WrStringAndCheck(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 + WrStringAndCheck(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 + WrStringAndCheck(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+=(\"--%s" if len(flag.NoOptDefVal) == 0 { format += "=" } - format += "\")\n" - buf.WriteString(fmt.Sprintf(format, name)) + format += cbn + WrStringAndCheck(buf, fmt.Sprintf(format, name)) } // Setup annotations for go completions for registered flags @@ -484,9 +487,9 @@ func prepareCustomAnnotationsForFlags(cmd *Command) { } } -func writeFlags(buf *bytes.Buffer, cmd *Command) { +func writeFlags(buf io.StringWriter, cmd *Command) { prepareCustomAnnotationsForFlags(cmd) - buf.WriteString(` flags=() + WrStringAndCheck(buf, ` flags=() two_word_flags=() local_nonpersistent_flags=() flags_with_completion=() @@ -516,11 +519,11 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) { } }) - buf.WriteString("\n") + WrStringAndCheck(buf, "\n") } -func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { - buf.WriteString(" must_have_one_flag=()\n") +func writeRequiredFlag(buf io.StringWriter, cmd *Command) { + WrStringAndCheck(buf, " must_have_one_flag=()\n") flags := cmd.NonInheritedFlags() flags.VisitAll(func(flag *pflag.Flag) { if nonCompletableFlag(flag) { @@ -533,55 +536,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 + WrStringAndCheck(buf, fmt.Sprintf(format, flag.Name)) if len(flag.Shorthand) > 0 { - buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)) + WrStringAndCheck(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) { + WrStringAndCheck(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)) + WrStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) } if cmd.ValidArgsFunction != nil { - buf.WriteString(" has_completion_function=1\n") + WrStringAndCheck(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")) + WrStringAndCheck(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())) + WrStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value)) + WrStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) } - buf.WriteString(` fi`) - buf.WriteString("\n") + WrStringAndCheck(buf, ` fi`) + WrStringAndCheck(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) { + WrStringAndCheck(buf, " noun_aliases=()\n") + sort.Strings(cmd.ArgAliases) for _, value := range cmd.ArgAliases { - buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value)) + WrStringAndCheck(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 @@ -593,22 +596,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)) + WrStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName)) } else { - buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName)) + WrStringAndCheck(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") + WrStringAndCheck(buf, fmt.Sprintf(" last_command=%q\n", commandName)) + WrStringAndCheck(buf, "\n") + WrStringAndCheck(buf, " command_aliases=()\n") + WrStringAndCheck(buf, "\n") writeCommands(buf, cmd) writeFlags(buf, cmd) writeRequiredFlag(buf, cmd) writeRequiredNouns(buf, cmd) writeArgAliases(buf, cmd) - buf.WriteString("}\n\n") + WrStringAndCheck(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 55b56f8af..b9e031778 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)) + er(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") + er(rootCmd.MarkFlagRequired("introot")) // Filename. rootCmd.Flags().String("filename", "", "Enter a filename") - rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml") + er(rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml")) // Persistent filename. rootCmd.PersistentFlags().String("persistent-filename", "", "Enter a filename") - rootCmd.MarkPersistentFlagFilename("persistent-filename") - rootCmd.MarkPersistentFlagRequired("persistent-filename") + er(rootCmd.MarkPersistentFlagFilename("persistent-filename")) + er(rootCmd.MarkPersistentFlagRequired("persistent-filename")) // Filename extensions. rootCmd.Flags().String("filename-ext", "", "Enter a filename (extension limited)") - rootCmd.MarkFlagFilename("filename-ext") + er(rootCmd.MarkFlagFilename("filename-ext")) rootCmd.Flags().String("custom", "", "Enter a filename (extension limited)") - rootCmd.MarkFlagCustom("custom", "__complete_custom") + er(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"}) + er(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") + er(echoCmd.MarkFlagFilename("filename", "json", "yaml", "yml")) echoCmd.Flags().String("config", "", "config to use (located in /config/PROFILE/)") - echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"}) + er(echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"})) printCmd := &Command{ Use: "print [string to print]", @@ -149,7 +150,7 @@ func TestBashCompletions(t *testing.T) { rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd) buf := new(bytes.Buffer) - rootCmd.GenBashCompletion(buf) + er(rootCmd.GenBashCompletion(buf)) output := buf.String() check(t, output, "_root") @@ -209,10 +210,10 @@ func TestBashCompletionHiddenFlag(t *testing.T) { const flagName = "hiddenFlag" c.Flags().Bool(flagName, false, "") - c.Flags().MarkHidden(flagName) + er(c.Flags().MarkHidden(flagName)) buf := new(bytes.Buffer) - c.GenBashCompletion(buf) + er(c.GenBashCompletion(buf)) output := buf.String() if strings.Contains(output, flagName) { @@ -225,10 +226,10 @@ func TestBashCompletionDeprecatedFlag(t *testing.T) { const flagName = "deprecated-flag" c.Flags().Bool(flagName, false, "") - c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead") + er(c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead")) buf := new(bytes.Buffer) - c.GenBashCompletion(buf) + er(c.GenBashCompletion(buf)) output := buf.String() if strings.Contains(output, flagName) { diff --git a/cobra.go b/cobra.go index d01becc8f..00183ad7e 100644 --- a/cobra.go +++ b/cobra.go @@ -19,6 +19,7 @@ package cobra import ( "fmt" "io" + "os" "reflect" "strconv" "strings" @@ -205,3 +206,18 @@ func stringInSlice(a string, list []string) bool { } return false } + +func er(msg interface{}) { + if msg != nil { + fmt.Println("Error:", msg) + os.Exit(1) + } +} + +// Er prints the msg and exits with error code 1, unless the msg is nil +func Er(msg interface{}) { er(msg) } + +func WrStringAndCheck(b io.StringWriter, s string) { + _, err := b.WriteString(s) + er(err) +} diff --git a/cobra/cmd/add.go b/cobra/cmd/add.go index 6645a755f..f8c294718 100644 --- a/cobra/cmd/add.go +++ b/cobra/cmd/add.go @@ -40,7 +40,7 @@ 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") + er(fmt.Errorf("add needs a name for the command")) } wd, err := os.Getwd() @@ -72,7 +72,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.") + er(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..2847a0649 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) - } + er(command.Project.Create()) + er(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 2cf9308e6..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)) @@ -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..0f7c33ee5 100644 --- a/cobra/cmd/helpers.go +++ b/cobra/cmd/helpers.go @@ -14,14 +14,11 @@ package cmd import ( - "bytes" "fmt" - "io" "os" "os/exec" "path/filepath" "strings" - "text/template" ) var srcPaths []string @@ -60,109 +57,8 @@ func init() { } 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) - } - } + if msg != nil { + fmt.Println("Error:", msg) + os.Exit(1) } - return strings.Join(newlines, "\n") } diff --git a/cobra/cmd/init.go b/cobra/cmd/init.go index edddc2cf3..5a1f81067 100644 --- a/cobra/cmd/init.go +++ b/cobra/cmd/init.go @@ -74,5 +74,5 @@ Init will not use an existing directory with contents.`, func init() { initCmd.Flags().StringVar(&pkgName, "pkg-name", "", "fully qualified pkg name") - initCmd.MarkFlagRequired("pkg-name") + er(initCmd.MarkFlagRequired("pkg-name")) } diff --git a/cobra/cmd/init_test.go b/cobra/cmd/init_test.go index 372dad024..0e09d90aa 100644 --- a/cobra/cmd/init_test.go +++ b/cobra/cmd/init_test.go @@ -34,9 +34,7 @@ func TestGoldenInitCmd(t *testing.T) { project := getProject() defer os.RemoveAll(project.AbsolutePath) - if err := project.Create(); err != nil { - t.Fatal(err) - } + er(project.Create()) expectedFiles := []string{"LICENSE", "main.go", "cmd/root.go"} for _, f := range expectedFiles { diff --git a/cobra/cmd/licenses.go b/cobra/cmd/licenses.go index 5f5abc89a..e4e2ffeb8 100644 --- a/cobra/cmd/licenses.go +++ b/cobra/cmd/licenses.go @@ -16,6 +16,7 @@ package cmd import ( + "fmt" "strings" "time" @@ -94,7 +95,7 @@ func copyrightLine() string { func findLicense(name string) License { found := matchLicense(name) if found == "" { - er("unknown license: " + name) + er(fmt.Errorf("unknown license: " + name)) } return Licenses[found] } diff --git a/cobra/cmd/project.go b/cobra/cmd/project.go index b0c12e726..108ff52ad 100644 --- a/cobra/cmd/project.go +++ b/cobra/cmd/project.go @@ -49,7 +49,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) + er(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..27c7ac2a4 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")) + er(viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))) + er(viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))) viper.SetDefault("author", "NAME HERE ") viper.SetDefault("license", "apache") @@ -62,7 +62,7 @@ func initConfig() { viper.SetConfigFile(cfgFile) } else { // Find home directory. - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { er(err) } diff --git a/cobra/cmd/testdata/root.go.golden b/cobra/cmd/testdata/root.go.golden index 6a54a7837..618d8449c 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,7 +37,7 @@ 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. @@ -71,7 +70,7 @@ func initConfig() { viper.SetConfigFile(cfgFile) } else { // Find home directory. - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/cobra/tpl/main.go b/cobra/tpl/main.go index b5e2e1aeb..adc9b70b3 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,7 +46,7 @@ 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. @@ -85,7 +83,7 @@ func initConfig() { viper.SetConfigFile(cfgFile) } else { // Find home directory. - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/command.go b/command.go index 50fb58d65..03cdfe20e 100644 --- a/command.go +++ b/command.go @@ -76,9 +76,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 @@ -118,55 +115,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. @@ -208,6 +156,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 @@ -410,7 +412,7 @@ func (c *Command) UsageString() string { c.outWriter = bb c.errWriter = bb - c.Usage() + er(c.Usage()) // Setting things back to normal c.outWriter = tmpOutput @@ -1062,10 +1064,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() + er(c.Root().Usage()) } else { cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown - cmd.Help() + er(cmd.Help()) } }, } diff --git a/command_test.go b/command_test.go index ec8e2aef9..ef48e0780 100644 --- a/command_test.go +++ b/command_test.go @@ -57,6 +57,8 @@ func checkStringOmits(t *testing.T, got, expected string) { } } +const onetwo = "one two" + func TestSingleCommand(t *testing.T) { var rootCmdArgs []string rootCmd := &Command{ @@ -77,9 +79,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) } } @@ -103,9 +104,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) } } @@ -144,7 +144,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()) } } @@ -242,9 +242,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) } } @@ -270,9 +269,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 @@ -306,9 +304,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 @@ -337,9 +334,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) } } @@ -367,9 +363,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) } } @@ -405,9 +400,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) } } @@ -440,9 +434,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) } } @@ -644,9 +637,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) @@ -730,9 +722,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) @@ -745,9 +736,9 @@ func TestPersistentFlagsOnChild(t *testing.T) { func TestRequiredFlags(t *testing.T) { c := &Command{Use: "c", Run: emptyRun} c.Flags().String("foo1", "", "") - c.MarkFlagRequired("foo1") + er(c.MarkFlagRequired("foo1")) c.Flags().String("foo2", "", "") - c.MarkFlagRequired("foo2") + er(c.MarkFlagRequired("foo2")) c.Flags().String("bar", "", "") expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2") @@ -763,16 +754,16 @@ func TestRequiredFlags(t *testing.T) { func TestPersistentRequiredFlags(t *testing.T) { parent := &Command{Use: "parent", Run: emptyRun} parent.PersistentFlags().String("foo1", "", "") - parent.MarkPersistentFlagRequired("foo1") + er(parent.MarkPersistentFlagRequired("foo1")) parent.PersistentFlags().String("foo2", "", "") - parent.MarkPersistentFlagRequired("foo2") + er(parent.MarkPersistentFlagRequired("foo2")) parent.Flags().String("foo3", "", "") child := &Command{Use: "child", Run: emptyRun} child.Flags().String("bar1", "", "") - child.MarkFlagRequired("bar1") + er(child.MarkFlagRequired("bar1")) child.Flags().String("bar2", "", "") - child.MarkFlagRequired("bar2") + er(child.MarkFlagRequired("bar2")) child.Flags().String("bar3", "", "") parent.AddCommand(child) @@ -1267,20 +1258,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) + } } } @@ -1348,44 +1338,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) + } } } @@ -1668,7 +1656,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") + er(c.Flags().MarkDeprecated("deprecated", "This flag is deprecated")) output, err := executeCommand(c, "c", "-d") if err != nil { @@ -1795,7 +1783,6 @@ type calledAsTestcase struct { call string want string epm bool - tc bool } func (tc *calledAsTestcase) test(t *testing.T) { @@ -1817,7 +1804,7 @@ func (tc *calledAsTestcase) test(t *testing.T) { parent.SetOut(output) parent.SetErr(output) - parent.Execute() + _ = parent.Execute() if called == nil { if tc.call != "" { @@ -1835,18 +1822,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 ba57327c1..825275d03 100644 --- a/custom_completions.go +++ b/custom_completions.go @@ -354,13 +354,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) + WrStringAndCheck(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 861548392..f2cbd55ea 100644 --- a/custom_completions_test.go +++ b/custom_completions_test.go @@ -270,7 +270,7 @@ func TestValidArgsFuncInBashScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - rootCmd.GenBashCompletion(buf) + er(rootCmd.GenBashCompletion(buf)) output := buf.String() check(t, output, "has_completion_function=1") @@ -285,7 +285,7 @@ func TestNoValidArgsFuncInBashScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - rootCmd.GenBashCompletion(buf) + er(rootCmd.GenBashCompletion(buf)) output := buf.String() checkOmit(t, output, "has_completion_function=1") @@ -301,7 +301,7 @@ func TestCompleteCmdInBashScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - rootCmd.GenBashCompletion(buf) + er(rootCmd.GenBashCompletion(buf)) output := buf.String() check(t, output, ShellCompNoDescRequestCmd) @@ -317,7 +317,7 @@ func TestCompleteNoDesCmdInFishScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - rootCmd.GenFishCompletion(buf, false) + er(rootCmd.GenFishCompletion(buf, false)) output := buf.String() check(t, output, ShellCompNoDescRequestCmd) @@ -333,7 +333,7 @@ func TestCompleteCmdInFishScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - rootCmd.GenFishCompletion(buf, true) + er(rootCmd.GenFishCompletion(buf, true)) output := buf.String() check(t, output, ShellCompRequestCmd) @@ -346,7 +346,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) { + er(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) { @@ -354,9 +354,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) { + er(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) { @@ -364,7 +364,7 @@ func TestFlagCompletionInGo(t *testing.T) { } } return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp - }) + })) // Test completing an empty string output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "") @@ -543,7 +543,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) { + er(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) { @@ -551,9 +551,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) { + er(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) { @@ -561,7 +561,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 8c7fba44a..817070f9e 100644 --- a/doc/man_docs.go +++ b/doc/man_docs.go @@ -139,25 +139,25 @@ func fillHeader(header *GenManHeader, name string) 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 + wrStringAndCheck(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") + wrStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short)) + wrStringAndCheck(buf, "# SYNOPSIS\n") + wrStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine())) + wrStringAndCheck(buf, "# DESCRIPTION\n") + wrStringAndCheck(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)) + wrStringAndCheck(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") + wrStringAndCheck(buf, "# OPTIONS\n") manPrintFlags(buf, flags) - buf.WriteString("\n") + wrStringAndCheck(buf, "\n") } flags = command.InheritedFlags() if flags.HasAvailableFlags() { - buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n") + wrStringAndCheck(buf, "# OPTIONS INHERITED FROM PARENT COMMANDS\n") manPrintFlags(buf, flags) - buf.WriteString("\n") + wrStringAndCheck(buf, "\n") } } diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go index 2c400f5df..609afdc6e 100644 --- a/doc/man_docs_test.go +++ b/doc/man_docs_test.go @@ -131,7 +131,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") + er(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..58dee8a8c 100644 --- a/doc/man_examples_test.go +++ b/doc/man_examples_test.go @@ -8,6 +8,10 @@ import ( "github.com/spf13/cobra/doc" ) +func er(msg interface{}) { + cobra.Er(msg) +} + func ExampleGenManTree() { cmd := &cobra.Command{ Use: "test", @@ -17,7 +21,7 @@ func ExampleGenManTree() { Title: "MINE", Section: "3", } - doc.GenManTree(cmd, header, "/tmp") + er(doc.GenManTree(cmd, header, "/tmp")) } func ExampleGenMan() { @@ -30,6 +34,6 @@ func ExampleGenMan() { Section: "3", } out := new(bytes.Buffer) - doc.GenMan(cmd, header, out) + er(doc.GenMan(cmd, header, out)) fmt.Print(out.String()) } diff --git a/doc/util.go b/doc/util.go index 8d3dbecec..584780694 100644 --- a/doc/util.go +++ b/doc/util.go @@ -14,6 +14,7 @@ package doc import ( + "io" "strings" "github.com/spf13/cobra" @@ -49,3 +50,11 @@ type byName []*cobra.Command func (s byName) Len() int { return len(s) } func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } + +func er(msg interface{}) { + cobra.Er(msg) +} + +func wrStringAndCheck(b io.StringWriter, s string) { + cobra.WrStringAndCheck(b, s) +} diff --git a/fish_completions.go b/fish_completions.go index c83609c83..23ee2fc2c 100644 --- a/fish_completions.go +++ b/fish_completions.go @@ -7,13 +7,13 @@ import ( "os" ) -func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) { +func genFishComp(buf io.StringWriter, name string, includeDesc bool) { compCmd := ShellCompRequestCmd if !includeDesc { compCmd = ShellCompNoDescRequestCmd } - buf.WriteString(fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name)) - buf.WriteString(fmt.Sprintf(` + WrStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name)) + WrStringAndCheck(buf, fmt.Sprintf(` function __%[1]s_debug set file "$BASH_COMP_DEBUG_FILE" if test -n "$file" diff --git a/go.mod b/go.mod index 1197beefd..16306039d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.12 require ( github.com/cpuguy83/go-md2man/v2 v2.0.0 github.com/inconshreveable/mousetrap v1.0.0 - github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.6.3 gopkg.in/yaml.v2 v2.2.8 diff --git a/go.sum b/go.sum index f256ec132..09bf50dc7 100644 --- a/go.sum +++ b/go.sum @@ -65,8 +65,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/powershell_completions_test.go b/powershell_completions_test.go index 29b609de0..0a8c837df 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) + er(tc.root.GenPowerShellCompletion(buf)) output := buf.String() for _, expectedExpression := range tc.expectedExpressions { diff --git a/zsh_completions_test.go b/zsh_completions_test.go index e53fa886e..dc6571b4e 100644 --- a/zsh_completions_test.go +++ b/zsh_completions_test.go @@ -90,9 +90,9 @@ func TestGenZshCompletion(t *testing.T) { Run: emptyRun, } r.Flags().StringVarP(&file, "config", "c", file, "config file") - r.MarkFlagFilename("config") + er(r.MarkFlagFilename("config")) r.Flags().String("output", "", "output file") - r.MarkFlagFilename("output", "*.log", "*.txt") + er(r.MarkFlagFilename("output", "*.log", "*.txt")) return r }(), expectedExpressions: []string{ @@ -161,8 +161,8 @@ func TestGenZshCompletion(t *testing.T) { name: "argument completion for file with and without patterns", root: func() *Command { r := genTestCommand("root", true) - r.MarkZshCompPositionalArgumentFile(1, "*.log") - r.MarkZshCompPositionalArgumentFile(2) + er(r.MarkZshCompPositionalArgumentFile(1, "*.log")) + er(r.MarkZshCompPositionalArgumentFile(2)) return r }(), expectedExpressions: []string{ @@ -173,7 +173,7 @@ func TestGenZshCompletion(t *testing.T) { name: "argument zsh completion for words", root: func() *Command { r := genTestCommand("root", true) - r.MarkZshCompPositionalArgumentWords(1, "word1", "word2") + er(r.MarkZshCompPositionalArgumentWords(1, "word1", "word2")) return r }(), expectedExpressions: []string{ @@ -184,7 +184,7 @@ func TestGenZshCompletion(t *testing.T) { name: "argument completion for words with spaces", root: func() *Command { r := genTestCommand("root", true) - r.MarkZshCompPositionalArgumentWords(1, "single", "multiple words") + er(r.MarkZshCompPositionalArgumentWords(1, "single", "multiple words")) return r }(), expectedExpressions: []string{ @@ -207,7 +207,7 @@ func TestGenZshCompletion(t *testing.T) { root: func() *Command { r := genTestCommand("root", true) r.ValidArgs = []string{"word1", "word2"} - r.MarkZshCompPositionalArgumentFile(2) + er(r.MarkZshCompPositionalArgumentFile(2)) return r }(), expectedExpressions: []string{ @@ -220,8 +220,8 @@ func TestGenZshCompletion(t *testing.T) { r := genTestCommand("root", true) r.Flags().String("test", "", "test") r.PersistentFlags().String("ptest", "", "ptest") - r.MarkFlagDirname("test") - r.MarkPersistentFlagDirname("ptest") + er(r.MarkFlagDirname("test")) + er(r.MarkPersistentFlagDirname("ptest")) return r }(), expectedExpressions: []string{ @@ -237,7 +237,7 @@ func TestGenZshCompletion(t *testing.T) { t.Skip(tc.skip) } tc.root.Root().SetArgs(tc.invocationArgs) - tc.root.Execute() + _ = tc.root.Execute() buf := new(bytes.Buffer) if err := tc.root.GenZshCompletion(buf); err != nil { t.Error(err) @@ -311,11 +311,9 @@ func TestGenZshCompletionHidden(t *testing.T) { for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - tc.root.Execute() + _ = tc.root.Execute() buf := new(bytes.Buffer) - if err := tc.root.GenZshCompletion(buf); err != nil { - t.Error(err) - } + er(tc.root.GenZshCompletion(buf)) output := buf.String() for _, expr := range tc.expectedExpressions { @@ -457,7 +455,7 @@ func constructLargeCommandHierarchy() *Command { s1.AddCommand(s1_1, s1_2, s1_3) s2.AddCommand(s2_1, s2_2, s2_3, s2_4, s2_5) r.AddCommand(s1, s2, s3) - r.Execute() + er(r.Execute()) return r }