diff --git a/.circleci/config.yml b/.circleci/config.yml index 819446439..533542dfa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,7 @@ references: run: name: "All Commands" command: | + export GO111MODULE=on mkdir -p bin curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.6/shellcheck chmod +x bin/shellcheck @@ -29,13 +30,6 @@ jobs: - run: name: "Check formatting" command: diff -u <(echo -n) <(gofmt -d -s .) - go-previous: - docker: - - image: circleci/golang:1.11 - working_directory: *workspace - steps: - - checkout - - *run_tests go-latest: docker: - image: circleci/golang:latest @@ -49,5 +43,4 @@ workflows: main: jobs: - go-current - - go-previous - go-latest 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/.gitignore b/.gitignore index 208e7d741..40405ad07 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ cobra.test .idea/ *.iml + +# test-generated files +cobra/cmd/testproject 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 f2022f2f1..b91301f35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,27 @@ language: go stages: - - diff + - lint - test go: - - 1.11.x - 1.12.x - 1.13.x - tip -env: - - GO111MODULE=on - -matrix: - allow_failures: - - go: tip - include: - - stage: diff - go: 1.13.x - script: diff -u <(echo -n) <(gofmt -d -s .) +env: GO111MODULE=on before_install: go get -u github.com/kyoh86/richgo script: - richgo test -v ./... - go build - - if [ -z $NOVET ]; then - diff -u <(echo -n) <(go vet . 2>&1 | grep -vE 'ExampleCommand|bash_completions.*Fprint'); - fi + +matrix: + allow_failures: + - go: tip + include: + - stage: lint + go: 1.13.x + before_install: go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1 + script: golangci-lint run -v diff --git a/README.md b/README.md index 1586e8c8e..10794ea93 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,6 @@ import ( "fmt" "os" - homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -241,7 +240,7 @@ func initConfig() { viper.SetConfigFile(cfgFile) } else { // Find home directory. - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { fmt.Println(err) os.Exit(1) diff --git a/bash_completions.go b/bash_completions.go index 03ddda27a..7087f47b1 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 @@ -281,10 +281,10 @@ __%[1]s_handle_word() `, name)) } -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 || : @@ -310,33 +310,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 { @@ -344,17 +344,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 { @@ -362,49 +363,51 @@ 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)) } -func writeFlags(buf *bytes.Buffer, cmd *Command) { - buf.WriteString(` flags=() +func writeFlags(buf io.StringWriter, cmd *Command) { + WrStringAndCheck(buf, ` flags=() two_word_flags=() local_nonpersistent_flags=() flags_with_completion=() @@ -434,11 +437,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) { @@ -451,49 +454,49 @@ 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 { - buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) + WrStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) } } -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 @@ -505,22 +508,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..d689a9439 100644 --- a/bash_completions_test.go +++ b/bash_completions_test.go @@ -52,7 +52,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 +76,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 +111,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 +151,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 +211,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 +227,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 c030c6ab5..852ec4efe 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.SetOutput(new(bytes.Buffer)) @@ -56,27 +53,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 dcf5ada4f..c982ea4d1 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 8ee391061..2047f5348 100644 --- a/cobra/cmd/init_test.go +++ b/cobra/cmd/init_test.go @@ -23,9 +23,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 a070134dd..bb3048465 100644 --- a/cobra/cmd/licenses.go +++ b/cobra/cmd/licenses.go @@ -16,6 +16,7 @@ package cmd import ( + "fmt" "strings" "time" @@ -92,7 +93,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 ecd783d03..304e9880f 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 938933c27..c2cf1a9b3 100644 --- a/cobra/cmd/testdata/root.go.golden +++ b/cobra/cmd/testdata/root.go.golden @@ -19,10 +19,7 @@ import ( "fmt" "os" "github.com/spf13/cobra" - - homedir "github.com/mitchellh/go-homedir" "github.com/spf13/viper" - ) @@ -41,7 +38,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. @@ -76,7 +73,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 2eab47eaa..643da9449 100644 --- a/cobra/tpl/main.go +++ b/cobra/tpl/main.go @@ -27,7 +27,6 @@ import ( "os" "github.com/spf13/cobra" {{ if .Viper }} - homedir "github.com/mitchellh/go-homedir" "github.com/spf13/viper" {{ end }} ) @@ -48,7 +47,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. @@ -86,7 +85,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 8db41c5dc..8fd4c3dfa 100644 --- a/command.go +++ b/command.go @@ -74,9 +74,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 @@ -115,53 +112,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 - - // 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. @@ -203,6 +153,58 @@ 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 + } + + // 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 } // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden @@ -397,7 +399,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 @@ -1031,10 +1033,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 b26bd4abe..ee4cae2f4 100644 --- a/command_test.go +++ b/command_test.go @@ -38,11 +38,7 @@ func checkStringContains(t *testing.T, got, expected string) { } } -func checkStringOmits(t *testing.T, got, expected string) { - if strings.Contains(got, expected) { - t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got) - } -} +const onetwo = "one two" func TestSingleCommand(t *testing.T) { var rootCmdArgs []string @@ -64,9 +60,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) } } @@ -90,9 +85,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) } } @@ -131,7 +125,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()) } } @@ -173,9 +167,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) } } @@ -201,9 +194,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 @@ -237,9 +229,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 @@ -268,9 +259,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) } } @@ -298,9 +288,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) } } @@ -336,9 +325,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) } } @@ -371,9 +359,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) } } @@ -575,9 +562,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) @@ -661,9 +647,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) @@ -676,9 +661,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") @@ -694,16 +679,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) @@ -1083,20 +1068,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) + } } } @@ -1164,44 +1148,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) + } } } @@ -1484,7 +1466,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 { @@ -1611,7 +1593,6 @@ type calledAsTestcase struct { call string want string epm bool - tc bool } func (tc *calledAsTestcase) test(t *testing.T) { @@ -1632,7 +1613,7 @@ func (tc *calledAsTestcase) test(t *testing.T) { output := new(bytes.Buffer) parent.SetOutput(output) - parent.Execute() + _ = parent.Execute() if called == nil { if tc.call != "" { @@ -1650,18 +1631,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/doc/man_docs.go b/doc/man_docs.go index 4a0623393..9bd845e52 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 dac814008..cc73d29f1 100644 --- a/fish_completions.go +++ b/fish_completions.go @@ -20,12 +20,12 @@ func (c *Command) GenFishCompletion(w io.Writer) error { return err } -func writeFishPreamble(cmd *Command, buf *bytes.Buffer) { +func writeFishPreamble(cmd *Command, buf io.StringWriter) { subCommandNames := []string{} rangeCommands(cmd, func(subCmd *Command) { subCommandNames = append(subCommandNames, subCmd.Name()) }) - buf.WriteString(fmt.Sprintf(` + WrStringAndCheck(buf, fmt.Sprintf(` function __fish_%s_no_subcommand --description 'Test if %s has yet to be given the subcommand' for i in (commandline -opc) if contains -- $i %s @@ -65,17 +65,24 @@ end `, cmd.Name(), cmd.Name(), strings.Join(subCommandNames, " "), cmd.Name())) } -func writeFishCommandCompletion(rootCmd, cmd *Command, buf *bytes.Buffer) { +func writeFishCommandCompletion(rootCmd, cmd *Command, buf io.StringWriter) { rangeCommands(cmd, func(subCmd *Command) { condition := commandCompletionCondition(rootCmd, cmd) escapedDescription := strings.Replace(subCmd.Short, "'", "\\'", -1) - buf.WriteString(fmt.Sprintf("complete -c %s -f %s -a %s -d '%s'\n", rootCmd.Name(), condition, subCmd.Name(), escapedDescription)) + WrStringAndCheck(buf, fmt.Sprintf("complete -c %s -f %s -a %s -d '%s'\n", rootCmd.Name(), condition, subCmd.Name(), escapedDescription)) }) for _, validArg := range append(cmd.ValidArgs, cmd.ArgAliases...) { condition := commandCompletionCondition(rootCmd, cmd) - buf.WriteString( - fmt.Sprintf("complete -c %s -f %s -a %s -d '%s'\n", - rootCmd.Name(), condition, validArg, fmt.Sprintf("Positional Argument to %s", cmd.Name()))) + WrStringAndCheck( + buf, + fmt.Sprintf( + "complete -c %s -f %s -a %s -d '%s'\n", + rootCmd.Name(), + condition, + validArg, + fmt.Sprintf("Positional Argument to %s", cmd.Name()), + ), + ) } writeCommandFlagsCompletion(rootCmd, cmd, buf) rangeCommands(cmd, func(subCmd *Command) { @@ -83,7 +90,7 @@ func writeFishCommandCompletion(rootCmd, cmd *Command, buf *bytes.Buffer) { }) } -func writeCommandFlagsCompletion(rootCmd, cmd *Command, buf *bytes.Buffer) { +func writeCommandFlagsCompletion(rootCmd, cmd *Command, buf io.StringWriter) { cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { if nonCompletableFlag(flag) { return @@ -98,14 +105,14 @@ func writeCommandFlagsCompletion(rootCmd, cmd *Command, buf *bytes.Buffer) { }) } -func writeCommandFlagCompletion(rootCmd, cmd *Command, buf *bytes.Buffer, flag *pflag.Flag) { +func writeCommandFlagCompletion(rootCmd, cmd *Command, buf io.StringWriter, flag *pflag.Flag) { shortHandPortion := "" if len(flag.Shorthand) > 0 { shortHandPortion = fmt.Sprintf("-s %s", flag.Shorthand) } condition := completionCondition(rootCmd, cmd) escapedUsage := strings.Replace(flag.Usage, "'", "\\'", -1) - buf.WriteString(fmt.Sprintf("complete -c %s -f %s %s %s -l %s -d '%s'\n", + WrStringAndCheck(buf, fmt.Sprintf("complete -c %s -f %s %s %s -l %s -d '%s'\n", rootCmd.Name(), condition, flagRequiresArgumentCompletion(flag), shortHandPortion, flag.Name, escapedUsage)) } diff --git a/fish_completions_test.go b/fish_completions_test.go index e5f9a4e1c..ff1fc13ba 100644 --- a/fish_completions_test.go +++ b/fish_completions_test.go @@ -13,22 +13,22 @@ func TestFishCompletions(t *testing.T) { Run: emptyRun, } rootCmd.Flags().IntP("introot", "i", -1, "help's 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/)") @@ -43,7 +43,7 @@ func TestFishCompletions(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/)") printCmd := &Command{ @@ -82,7 +82,7 @@ func TestFishCompletions(t *testing.T) { rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd) buf := new(bytes.Buffer) - rootCmd.GenFishCompletion(buf) + er(rootCmd.GenFishCompletion(buf)) output := buf.String() // check for preamble helper functions diff --git a/go.mod b/go.mod index 60aa6f64f..b4f4b17cf 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.12 require ( github.com/cpuguy83/go-md2man v1.0.10 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.4.0 gopkg.in/yaml.v2 v2.2.3 diff --git a/go.sum b/go.sum index ccfe80d5f..d7727b579 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 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 }