Use golangci-lint (#1044)

Use golangci-lint. Repair warnings and errors resulting from linting.
This commit is contained in:
Unai Martinez-Corral 2021-02-08 00:08:50 +00:00 committed by GitHub
parent 1d71ff0270
commit 652c755d37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 437 additions and 525 deletions

48
.golangci.yml Normal file
View file

@ -0,0 +1,48 @@
run:
deadline: 5m
linters:
disable-all: true
enable:
#- bodyclose
- deadcode
#- depguard
#- dogsled
#- dupl
- errcheck
#- exhaustive
#- funlen
- gas
#- gochecknoinits
- goconst
#- gocritic
#- gocyclo
#- gofmt
- goimports
- golint
#- gomnd
#- goprintffuncname
#- gosec
#- gosimple
- govet
- ineffassign
- interfacer
#- lll
- maligned
- megacheck
#- misspell
#- nakedret
#- noctx
#- nolintlint
#- rowserrcheck
#- scopelint
#- staticcheck
- structcheck
#- stylecheck
#- typecheck
- unconvert
#- unparam
#- unused
- varcheck
#- whitespace
fast: false

View file

@ -1,7 +1,6 @@
language: go language: go
stages: stages:
- diff
- test - test
- build - build
@ -10,20 +9,20 @@ go:
- 1.15.x - 1.15.x
- tip - tip
env: GO111MODULE=on
before_install: before_install:
- go get -u github.com/kyoh86/richgo - go get -u github.com/kyoh86/richgo
- go get -u github.com/mitchellh/gox - go get -u github.com/mitchellh/gox
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest
matrix: matrix:
allow_failures: allow_failures:
- go: tip - go: tip
include: include:
- stage: diff
go: 1.14.x
script: make fmt
- stage: build - stage: build
go: 1.14.x go: 1.14.x
script: make cobra_generator script: make cobra_generator
script: script:
- make test - make test

View file

@ -1,21 +1,29 @@
BIN="./bin" BIN="./bin"
SRC=$(shell find . -name "*.go") 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)) ifeq (, $(shell which richgo))
$(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo") $(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo")
endif endif
.PHONY: fmt vet test cobra_generator install_deps clean .PHONY: fmt lint test cobra_generator install_deps clean
default: all default: all
all: fmt vet test cobra_generator all: fmt test cobra_generator
fmt: fmt:
$(info ******************** checking formatting ********************) $(info ******************** checking formatting ********************)
@test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1) @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 ********************) $(info ******************** running tests ********************)
richgo test -v ./... richgo test -v ./...
@ -28,9 +36,5 @@ install_deps:
$(info ******************** downloading dependencies ********************) $(info ******************** downloading dependencies ********************)
go get -v ./... go get -v ./...
vet:
$(info ******************** vetting ********************)
go vet ./...
clean: clean:
rm -rf $(BIN) rm -rf $(BIN)

View file

@ -234,11 +234,6 @@ func init() {
rootCmd.AddCommand(initCmd) rootCmd.AddCommand(initCmd)
} }
func er(msg interface{}) {
fmt.Println("Error:", msg)
os.Exit(1)
}
func initConfig() { func initConfig() {
if cfgFile != "" { if cfgFile != "" {
// Use config file from the flag. // Use config file from the flag.
@ -246,9 +241,7 @@ func initConfig() {
} else { } else {
// Find home directory. // Find home directory.
home, err := homedir.Dir() home, err := homedir.Dir()
if err != nil { cobra.CheckErr(err)
er(err)
}
// Search config in home directory with name ".cobra" (without extension). // Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home) viper.AddConfigPath(home)

View file

@ -19,9 +19,9 @@ const (
BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir"
) )
func writePreamble(buf *bytes.Buffer, name string) { func writePreamble(buf io.StringWriter, name string) {
buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
buf.WriteString(fmt.Sprintf(` WriteStringAndCheck(buf, fmt.Sprintf(`
__%[1]s_debug() __%[1]s_debug()
{ {
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
@ -380,10 +380,10 @@ __%[1]s_handle_word()
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
} }
func writePostscript(buf *bytes.Buffer, name string) { func writePostscript(buf io.StringWriter, name string) {
name = strings.Replace(name, ":", "__", -1) name = strings.Replace(name, ":", "__", -1)
buf.WriteString(fmt.Sprintf("__start_%s()\n", name)) WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))
buf.WriteString(fmt.Sprintf(`{ WriteStringAndCheck(buf, fmt.Sprintf(`{
local cur prev words cword local cur prev words cword
declare -A flaghash 2>/dev/null || : declare -A flaghash 2>/dev/null || :
declare -A aliashash 2>/dev/null || : declare -A aliashash 2>/dev/null || :
@ -410,33 +410,33 @@ func writePostscript(buf *bytes.Buffer, name string) {
} }
`, name)) `, name))
buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then WriteStringAndCheck(buf, fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
complete -o default -F __start_%s %s complete -o default -F __start_%s %s
else else
complete -o default -o nospace -F __start_%s %s complete -o default -o nospace -F __start_%s %s
fi fi
`, name, name, name, name)) `, name, name, name, name))
buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n") WriteStringAndCheck(buf, "# ex: ts=4 sw=4 et filetype=sh\n")
} }
func writeCommands(buf *bytes.Buffer, cmd *Command) { func writeCommands(buf io.StringWriter, cmd *Command) {
buf.WriteString(" commands=()\n") WriteStringAndCheck(buf, " commands=()\n")
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() && c != cmd.helpCommand { if !c.IsAvailableCommand() && c != cmd.helpCommand {
continue continue
} }
buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) WriteStringAndCheck(buf, fmt.Sprintf(" commands+=(%q)\n", c.Name()))
writeCmdAliases(buf, c) writeCmdAliases(buf, c)
} }
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
} }
func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) { func writeFlagHandler(buf io.StringWriter, name string, annotations map[string][]string, cmd *Command) {
for key, value := range annotations { for key, value := range annotations {
switch key { switch key {
case BashCompFilenameExt: case BashCompFilenameExt:
buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
var ext string var ext string
if len(value) > 0 { if len(value) > 0 {
@ -444,17 +444,18 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
} else { } else {
ext = "_filedir" ext = "_filedir"
} }
buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))
case BashCompCustom: case BashCompCustom:
buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
if len(value) > 0 { if len(value) > 0 {
handlers := strings.Join(value, "; ") handlers := strings.Join(value, "; ")
buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", handlers))
} else { } else {
buf.WriteString(" flags_completion+=(:)\n") WriteStringAndCheck(buf, " flags_completion+=(:)\n")
} }
case BashCompSubdirsInDir: case BashCompSubdirsInDir:
buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
var ext string var ext string
if len(value) == 1 { if len(value) == 1 {
@ -462,46 +463,48 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
} else { } else {
ext = "_filedir -d" ext = "_filedir -d"
} }
buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))
} }
} }
} }
func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { const cbn = "\")\n"
func writeShortFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
name := flag.Shorthand name := flag.Shorthand
format := " " format := " "
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format += "two_word_" format += "two_word_"
} }
format += "flags+=(\"-%s\")\n" format += "flags+=(\"-%s" + cbn
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
writeFlagHandler(buf, "-"+name, flag.Annotations, cmd) 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 name := flag.Name
format := " flags+=(\"--%s" format := " flags+=(\"--%s"
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format += "=" format += "="
} }
format += "\")\n" format += cbn
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format = " two_word_flags+=(\"--%s\")\n" format = " two_word_flags+=(\"--%s" + cbn
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
} }
writeFlagHandler(buf, "--"+name, flag.Annotations, cmd) 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 name := flag.Name
format := " local_nonpersistent_flags+=(\"--%[1]s\")\n" format := " local_nonpersistent_flags+=(\"--%[1]s" + cbn
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format += " local_nonpersistent_flags+=(\"--%[1]s=\")\n" format += " local_nonpersistent_flags+=(\"--%[1]s=" + cbn
} }
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 {
buf.WriteString(fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand)) WriteStringAndCheck(buf, fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))
} }
} }
@ -519,9 +522,9 @@ func prepareCustomAnnotationsForFlags(cmd *Command) {
} }
} }
func writeFlags(buf *bytes.Buffer, cmd *Command) { func writeFlags(buf io.StringWriter, cmd *Command) {
prepareCustomAnnotationsForFlags(cmd) prepareCustomAnnotationsForFlags(cmd)
buf.WriteString(` flags=() WriteStringAndCheck(buf, ` flags=()
two_word_flags=() two_word_flags=()
local_nonpersistent_flags=() local_nonpersistent_flags=()
flags_with_completion=() flags_with_completion=()
@ -553,11 +556,11 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
} }
}) })
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
} }
func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
buf.WriteString(" must_have_one_flag=()\n") WriteStringAndCheck(buf, " must_have_one_flag=()\n")
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
flags.VisitAll(func(flag *pflag.Flag) { flags.VisitAll(func(flag *pflag.Flag) {
if nonCompletableFlag(flag) { if nonCompletableFlag(flag) {
@ -570,55 +573,55 @@ func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
if flag.Value.Type() != "bool" { if flag.Value.Type() != "bool" {
format += "=" format += "="
} }
format += "\")\n" format += cbn
buf.WriteString(fmt.Sprintf(format, flag.Name)) WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 {
buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)) WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
} }
} }
} }
}) })
} }
func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) { func writeRequiredNouns(buf io.StringWriter, cmd *Command) {
buf.WriteString(" must_have_one_noun=()\n") WriteStringAndCheck(buf, " must_have_one_noun=()\n")
sort.Sort(sort.StringSlice(cmd.ValidArgs)) sort.Strings(cmd.ValidArgs)
for _, value := range cmd.ValidArgs { for _, value := range cmd.ValidArgs {
// Remove any description that may be included following a tab character. // Remove any description that may be included following a tab character.
// Descriptions are not supported by bash completion. // Descriptions are not supported by bash completion.
value = strings.Split(value, "\t")[0] value = strings.Split(value, "\t")[0]
buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))
} }
if cmd.ValidArgsFunction != nil { if cmd.ValidArgsFunction != nil {
buf.WriteString(" has_completion_function=1\n") WriteStringAndCheck(buf, " has_completion_function=1\n")
} }
} }
func writeCmdAliases(buf *bytes.Buffer, cmd *Command) { func writeCmdAliases(buf io.StringWriter, cmd *Command) {
if len(cmd.Aliases) == 0 { if len(cmd.Aliases) == 0 {
return return
} }
sort.Sort(sort.StringSlice(cmd.Aliases)) sort.Strings(cmd.Aliases)
buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n")) WriteStringAndCheck(buf, fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
for _, value := range cmd.Aliases { for _, value := range cmd.Aliases {
buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value)) WriteStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value))
buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) WriteStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name()))
} }
buf.WriteString(` fi`) WriteStringAndCheck(buf, ` fi`)
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
} }
func writeArgAliases(buf *bytes.Buffer, cmd *Command) { func writeArgAliases(buf io.StringWriter, cmd *Command) {
buf.WriteString(" noun_aliases=()\n") WriteStringAndCheck(buf, " noun_aliases=()\n")
sort.Sort(sort.StringSlice(cmd.ArgAliases)) sort.Strings(cmd.ArgAliases)
for _, value := range cmd.ArgAliases { for _, value := range cmd.ArgAliases {
buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value)) WriteStringAndCheck(buf, fmt.Sprintf(" noun_aliases+=(%q)\n", value))
} }
} }
func gen(buf *bytes.Buffer, cmd *Command) { func gen(buf io.StringWriter, cmd *Command) {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() && c != cmd.helpCommand { if !c.IsAvailableCommand() && c != cmd.helpCommand {
continue continue
@ -630,22 +633,22 @@ func gen(buf *bytes.Buffer, cmd *Command) {
commandName = strings.Replace(commandName, ":", "__", -1) commandName = strings.Replace(commandName, ":", "__", -1)
if cmd.Root() == cmd { if cmd.Root() == cmd {
buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName)) WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))
} else { } else {
buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName)) WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName))
} }
buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName)) WriteStringAndCheck(buf, fmt.Sprintf(" last_command=%q\n", commandName))
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
buf.WriteString(" command_aliases=()\n") WriteStringAndCheck(buf, " command_aliases=()\n")
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
writeCommands(buf, cmd) writeCommands(buf, cmd)
writeFlags(buf, cmd) writeFlags(buf, cmd)
writeRequiredFlag(buf, cmd) writeRequiredFlag(buf, cmd)
writeRequiredNouns(buf, cmd) writeRequiredNouns(buf, cmd)
writeArgAliases(buf, cmd) writeArgAliases(buf, cmd)
buf.WriteString("}\n\n") WriteStringAndCheck(buf, "}\n\n")
} }
// GenBashCompletion generates bash completion file and writes to the passed writer. // GenBashCompletion generates bash completion file and writes to the passed writer.

View file

@ -40,10 +40,9 @@ func checkRegex(t *testing.T, found, pattern string) {
} }
func runShellCheck(s string) error { func runShellCheck(s string) error {
excluded := []string{ cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e",
"SC2034", // PREFIX appears unused. Verify it or export it. "SC2034", // PREFIX appears unused. Verify it or export it.
} )
cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e", strings.Join(excluded, ","))
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@ -52,7 +51,9 @@ func runShellCheck(s string) error {
return err return err
} }
go func() { go func() {
stdin.Write([]byte(s)) _, err := stdin.Write([]byte(s))
CheckErr(err)
stdin.Close() stdin.Close()
}() }()
@ -74,26 +75,26 @@ func TestBashCompletions(t *testing.T) {
Run: emptyRun, Run: emptyRun,
} }
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
rootCmd.MarkFlagRequired("introot") assertNoErr(t, rootCmd.MarkFlagRequired("introot"))
// Filename. // Filename.
rootCmd.Flags().String("filename", "", "Enter a filename") rootCmd.Flags().String("filename", "", "Enter a filename")
rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml") assertNoErr(t, rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml"))
// Persistent filename. // Persistent filename.
rootCmd.PersistentFlags().String("persistent-filename", "", "Enter a filename") rootCmd.PersistentFlags().String("persistent-filename", "", "Enter a filename")
rootCmd.MarkPersistentFlagFilename("persistent-filename") assertNoErr(t, rootCmd.MarkPersistentFlagFilename("persistent-filename"))
rootCmd.MarkPersistentFlagRequired("persistent-filename") assertNoErr(t, rootCmd.MarkPersistentFlagRequired("persistent-filename"))
// Filename extensions. // Filename extensions.
rootCmd.Flags().String("filename-ext", "", "Enter a filename (extension limited)") rootCmd.Flags().String("filename-ext", "", "Enter a filename (extension limited)")
rootCmd.MarkFlagFilename("filename-ext") assertNoErr(t, rootCmd.MarkFlagFilename("filename-ext"))
rootCmd.Flags().String("custom", "", "Enter a filename (extension limited)") rootCmd.Flags().String("custom", "", "Enter a filename (extension limited)")
rootCmd.MarkFlagCustom("custom", "__complete_custom") assertNoErr(t, rootCmd.MarkFlagCustom("custom", "__complete_custom"))
// Subdirectories in a given directory. // Subdirectories in a given directory.
rootCmd.Flags().String("theme", "", "theme to use (located in /themes/THEMENAME/)") rootCmd.Flags().String("theme", "", "theme to use (located in /themes/THEMENAME/)")
rootCmd.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"}) assertNoErr(t, rootCmd.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"}))
// For two word flags check // For two word flags check
rootCmd.Flags().StringP("two", "t", "", "this is two word flags") 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.Flags().String("filename", "", "Enter a filename")
echoCmd.MarkFlagFilename("filename", "json", "yaml", "yml") assertNoErr(t, echoCmd.MarkFlagFilename("filename", "json", "yaml", "yml"))
echoCmd.Flags().String("config", "", "config to use (located in /config/PROFILE/)") echoCmd.Flags().String("config", "", "config to use (located in /config/PROFILE/)")
echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"}) assertNoErr(t, echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"}))
printCmd := &Command{ printCmd := &Command{
Use: "print [string to print]", Use: "print [string to print]",
@ -149,7 +150,7 @@ func TestBashCompletions(t *testing.T) {
rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd) rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenBashCompletion(buf) assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String() output := buf.String()
check(t, output, "_root") check(t, output, "_root")
@ -216,10 +217,10 @@ func TestBashCompletionHiddenFlag(t *testing.T) {
const flagName = "hiddenFlag" const flagName = "hiddenFlag"
c.Flags().Bool(flagName, false, "") c.Flags().Bool(flagName, false, "")
c.Flags().MarkHidden(flagName) assertNoErr(t, c.Flags().MarkHidden(flagName))
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
c.GenBashCompletion(buf) assertNoErr(t, c.GenBashCompletion(buf))
output := buf.String() output := buf.String()
if strings.Contains(output, flagName) { if strings.Contains(output, flagName) {
@ -232,10 +233,10 @@ func TestBashCompletionDeprecatedFlag(t *testing.T) {
const flagName = "deprecated-flag" const flagName = "deprecated-flag"
c.Flags().Bool(flagName, false, "") c.Flags().Bool(flagName, false, "")
c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead") assertNoErr(t, c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead"))
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
c.GenBashCompletion(buf) assertNoErr(t, c.GenBashCompletion(buf))
output := buf.String() output := buf.String()
if strings.Contains(output, flagName) { if strings.Contains(output, flagName) {
@ -250,7 +251,7 @@ func TestBashCompletionTraverseChildren(t *testing.T) {
c.Flags().BoolP("bool-flag", "b", false, "bool flag") c.Flags().BoolP("bool-flag", "b", false, "bool flag")
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
c.GenBashCompletion(buf) assertNoErr(t, c.GenBashCompletion(buf))
output := buf.String() output := buf.String()
// check that local nonpersistent flag are not set since we have TraverseChildren set to true // check that local nonpersistent flag are not set since we have TraverseChildren set to true

View file

@ -19,6 +19,7 @@ package cobra
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -205,3 +206,17 @@ func stringInSlice(a string, list []string) bool {
} }
return false return false
} }
// CheckErr prints the msg with the prefix 'Error:' and exits with error code 1. If the msg is nil, it does nothing.
func CheckErr(msg interface{}) {
if msg != nil {
fmt.Fprintln(os.Stderr, "Error:", msg)
os.Exit(1)
}
}
// WriteStringAndCheck writes a string into a buffer, and checks if the error is not nil.
func WriteStringAndCheck(b io.StringWriter, s string) {
_, err := b.WriteString(s)
CheckErr(err)
}

View file

@ -40,13 +40,11 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 { if len(args) < 1 {
er("add needs a name for the command") cobra.CheckErr(fmt.Errorf("add needs a name for the command"))
} }
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { cobra.CheckErr(err)
er(err)
}
commandName := validateCmdName(args[0]) commandName := validateCmdName(args[0])
command := &Command{ command := &Command{
@ -59,10 +57,7 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
}, },
} }
err = command.Create() cobra.CheckErr(command.Create())
if err != nil {
er(err)
}
fmt.Printf("%s created at %s\n", command.CmdName, command.AbsolutePath) fmt.Printf("%s created at %s\n", command.CmdName, command.AbsolutePath)
}, },
@ -72,7 +67,7 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
func init() { func init() {
addCmd.Flags().StringVarP(&packageName, "package", "t", "", "target package name (e.g. github.com/spf13/hugo)") 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().StringVarP(&parentName, "parent", "p", "rootCmd", "variable name of parent command for this command")
addCmd.Flags().MarkDeprecated("package", "this operation has been removed.") cobra.CheckErr(addCmd.Flags().MarkDeprecated("package", "this operation has been removed."))
} }
// validateCmdName returns source without any dashes and underscore. // validateCmdName returns source without any dashes and underscore.

View file

@ -14,10 +14,8 @@ func TestGoldenAddCmd(t *testing.T) {
} }
defer os.RemoveAll(command.AbsolutePath) defer os.RemoveAll(command.AbsolutePath)
command.Project.Create() assertNoErr(t, command.Project.Create())
if err := command.Create(); err != nil { assertNoErr(t, command.Create())
t.Fatal(err)
}
generatedFile := fmt.Sprintf("%s/cmd/%s.go", command.AbsolutePath, command.CmdName) generatedFile := fmt.Sprintf("%s/cmd/%s.go", command.AbsolutePath, command.CmdName)
goldenFile := fmt.Sprintf("testdata/%s.go.golden", command.CmdName) goldenFile := fmt.Sprintf("testdata/%s.go.golden", command.CmdName)

View file

@ -3,14 +3,11 @@ package cmd
import ( import (
"bytes" "bytes"
"errors" "errors"
"flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os/exec" "os/exec"
) )
var update = flag.Bool("update", false, "update .golden files")
func init() { func init() {
// Mute commands. // Mute commands.
addCmd.SetOut(new(bytes.Buffer)) addCmd.SetOut(new(bytes.Buffer))
@ -58,27 +55,3 @@ func compareFiles(pathA, pathB string) error {
} }
return nil 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
}

View file

@ -14,14 +14,12 @@
package cmd package cmd
import ( import (
"bytes"
"fmt"
"io"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"text/template"
"github.com/spf13/cobra"
) )
var srcPaths []string var srcPaths []string
@ -43,14 +41,12 @@ func init() {
} }
out, err := exec.Command(goExecutable, "env", "GOPATH").Output() out, err := exec.Command(goExecutable, "env", "GOPATH").Output()
if err != nil { cobra.CheckErr(err)
er(err)
}
toolchainGoPath := strings.TrimSpace(string(out)) toolchainGoPath := strings.TrimSpace(string(out))
goPaths = filepath.SplitList(toolchainGoPath) goPaths = filepath.SplitList(toolchainGoPath)
if len(goPaths) == 0 { if len(goPaths) == 0 {
er("$GOPATH is not set") cobra.CheckErr("$GOPATH is not set")
} }
} }
srcPaths = make([]string, 0, len(goPaths)) srcPaths = make([]string, 0, len(goPaths))
@ -58,111 +54,3 @@ func init() {
srcPaths = append(srcPaths, filepath.Join(goPath, "src")) srcPaths = append(srcPaths, filepath.Join(goPath, "src"))
} }
} }
func er(msg interface{}) {
fmt.Println("Error:", msg)
os.Exit(1)
}
// isEmpty checks if a given path is empty.
// Hidden files in path are ignored.
func isEmpty(path string) bool {
fi, err := os.Stat(path)
if err != nil {
er(err)
}
if !fi.IsDir() {
return fi.Size() == 0
}
f, err := os.Open(path)
if err != nil {
er(err)
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil && err != io.EOF {
er(err)
}
for _, name := range names {
if len(name) > 0 && name[0] != '.' {
return false
}
}
return true
}
// exists checks if a file or directory exists.
func exists(path string) bool {
if path == "" {
return false
}
_, err := os.Stat(path)
if err == nil {
return true
}
if !os.IsNotExist(err) {
er(err)
}
return false
}
func executeTemplate(tmplStr string, data interface{}) (string, error) {
tmpl, err := template.New("").Funcs(template.FuncMap{"comment": commentifyString}).Parse(tmplStr)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
err = tmpl.Execute(buf, data)
return buf.String(), err
}
func writeStringToFile(path string, s string) error {
return writeToFile(path, strings.NewReader(s))
}
// writeToFile writes r to file with path only
// if file/directory on given path doesn't exist.
func writeToFile(path string, r io.Reader) error {
if exists(path) {
return fmt.Errorf("%v already exists", path)
}
dir := filepath.Dir(path)
if dir != "" {
if err := os.MkdirAll(dir, 0777); err != nil {
return err
}
}
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, r)
return err
}
// commentfyString comments every line of in.
func commentifyString(in string) string {
var newlines []string
lines := strings.Split(in, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "//") {
newlines = append(newlines, line)
} else {
if line == "" {
newlines = append(newlines, "//")
} else {
newlines = append(newlines, "// "+line)
}
}
}
return strings.Join(newlines, "\n")
}

View file

@ -0,0 +1,9 @@
package cmd
import "testing"
func assertNoErr(t *testing.T, e error) {
if e != nil {
t.Error(e)
}
}

View file

@ -39,9 +39,7 @@ and the appropriate structure for a Cobra-based CLI application.
Run: func(_ *cobra.Command, args []string) { Run: func(_ *cobra.Command, args []string) {
projectPath, err := initializeProject(args) projectPath, err := initializeProject(args)
if err != nil { cobra.CheckErr(err)
er(err)
}
fmt.Printf("Your Cobra application is ready at\n%s\n", projectPath) fmt.Printf("Your Cobra application is ready at\n%s\n", projectPath)
}, },
} }
@ -49,7 +47,7 @@ and the appropriate structure for a Cobra-based CLI application.
func init() { func init() {
initCmd.Flags().StringVar(&pkgName, "pkg-name", "", "fully qualified pkg name") initCmd.Flags().StringVar(&pkgName, "pkg-name", "", "fully qualified pkg name")
initCmd.MarkFlagRequired("pkg-name") cobra.CheckErr(initCmd.MarkFlagRequired("pkg-name"))
} }
func initializeProject(args []string) (string, error) { func initializeProject(args []string) (string, error) {

View file

@ -59,7 +59,7 @@ func TestGoldenInitCmd(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
initCmd.Flags().Set("pkg-name", tt.pkgName) assertNoErr(t, initCmd.Flags().Set("pkg-name", tt.pkgName))
viper.Set("useViper", true) viper.Set("useViper", true)
projectPath, err := initializeProject(tt.args) projectPath, err := initializeProject(tt.args)
defer func() { defer func() {

View file

@ -16,9 +16,11 @@
package cmd package cmd
import ( import (
"fmt"
"strings" "strings"
"time" "time"
"github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -92,7 +94,7 @@ func copyrightLine() string {
func findLicense(name string) License { func findLicense(name string) License {
found := matchLicense(name) found := matchLicense(name)
if found == "" { if found == "" {
er("unknown license: " + name) cobra.CheckErr(fmt.Errorf("unknown license: " + name))
} }
return Licenses[found] return Licenses[found]
} }

View file

@ -5,6 +5,7 @@ import (
"os" "os"
"text/template" "text/template"
"github.com/spf13/cobra"
"github.com/spf13/cobra/cobra/tpl" "github.com/spf13/cobra/cobra/tpl"
) )
@ -49,7 +50,7 @@ func (p *Project) Create() error {
// create cmd/root.go // create cmd/root.go
if _, err = os.Stat(fmt.Sprintf("%s/cmd", p.AbsolutePath)); os.IsNotExist(err) { if _, err = os.Stat(fmt.Sprintf("%s/cmd", p.AbsolutePath)); os.IsNotExist(err) {
os.Mkdir(fmt.Sprintf("%s/cmd", p.AbsolutePath), 0751) cobra.CheckErr(os.Mkdir(fmt.Sprintf("%s/cmd", p.AbsolutePath), 0751))
} }
rootFile, err := os.Create(fmt.Sprintf("%s/cmd/root.go", p.AbsolutePath)) rootFile, err := os.Create(fmt.Sprintf("%s/cmd/root.go", p.AbsolutePath))
if err != nil { if err != nil {

View file

@ -47,8 +47,8 @@ func init() {
rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution") 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().StringVarP(&userLicense, "license", "l", "", "name of license for the project")
rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration") rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration")
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) cobra.CheckErr(viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")))
viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")) cobra.CheckErr(viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")))
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>") viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
viper.SetDefault("license", "apache") viper.SetDefault("license", "apache")
@ -63,9 +63,7 @@ func initConfig() {
} else { } else {
// Find home directory. // Find home directory.
home, err := homedir.Dir() home, err := homedir.Dir()
if err != nil { cobra.CheckErr(err)
er(err)
}
// Search config in home directory with name ".cobra" (without extension). // Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home) viper.AddConfigPath(home)

View file

@ -17,8 +17,8 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra"
"os" "os"
"github.com/spf13/cobra"
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -38,16 +38,13 @@ This application is a tool to generate the needed files
to quickly create a Cobra application.`, to quickly create a Cobra application.`,
// Uncomment the following line if your bare application // Uncomment the following line if your bare application
// has an action associated with it: // 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. // Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd. // This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() { func Execute() {
if err := rootCmd.Execute(); err != nil { cobra.CheckErr(rootCmd.Execute())
fmt.Println(err)
os.Exit(1)
}
} }
func init() { func init() {
@ -72,10 +69,7 @@ func initConfig() {
} else { } else {
// Find home directory. // Find home directory.
home, err := homedir.Dir() home, err := homedir.Dir()
if err != nil { cobra.CheckErr(err)
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".testproject" (without extension). // Search config in home directory with name ".testproject" (without extension).
viper.AddConfigPath(home) viper.AddConfigPath(home)
@ -86,6 +80,6 @@ func initConfig() {
// If a config file is found, read it in. // If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil { if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed()) fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
} }
} }

View file

@ -24,12 +24,11 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra"
"os" "os"
"github.com/spf13/cobra"
{{ if .Viper }} {{ if .Viper }}
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper" "github.com/spf13/viper"{{ end }}
{{ end -}}
) )
{{ if .Viper -}} {{ if .Viper -}}
@ -48,16 +47,13 @@ This application is a tool to generate the needed files
to quickly create a Cobra application.` + "`" + `, to quickly create a Cobra application.` + "`" + `,
// Uncomment the following line if your bare application // Uncomment the following line if your bare application
// has an action associated with it: // 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. // Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd. // This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() { func Execute() {
if err := rootCmd.Execute(); err != nil { cobra.CheckErr(rootCmd.Execute())
fmt.Println(err)
os.Exit(1)
}
} }
func init() { func init() {
@ -86,10 +82,7 @@ func initConfig() {
} else { } else {
// Find home directory. // Find home directory.
home, err := homedir.Dir() home, err := homedir.Dir()
if err != nil { cobra.CheckErr(err)
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".{{ .AppName }}" (without extension). // Search config in home directory with name ".{{ .AppName }}" (without extension).
viper.AddConfigPath(home) viper.AddConfigPath(home)
@ -100,7 +93,7 @@ func initConfig() {
// If a config file is found, read it in. // If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil { if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed()) fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
} }
} }
{{- end }} {{- end }}

View file

@ -5,6 +5,12 @@ import (
"text/template" "text/template"
) )
func assertNoErr(t *testing.T, e error) {
if e != nil {
t.Error(e)
}
}
func TestAddTemplateFunctions(t *testing.T) { func TestAddTemplateFunctions(t *testing.T) {
AddTemplateFunc("t", func() bool { return true }) AddTemplateFunc("t", func() bool { return true })
AddTemplateFuncs(template.FuncMap{ AddTemplateFuncs(template.FuncMap{

View file

@ -84,9 +84,6 @@ type Command struct {
// Deprecated defines, if this command is deprecated and should print this string when used. // Deprecated defines, if this command is deprecated and should print this string when used.
Deprecated string 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 // Annotations are key/value pairs that can be used by applications to identify or
// group commands. // group commands.
Annotations map[string]string Annotations map[string]string
@ -126,55 +123,6 @@ type Command struct {
// PersistentPostRunE: PersistentPostRun but returns an error. // PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) 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 is actual args parsed from flags.
args []string args []string
// flagErrorBuf contains all error messages from pflag. // flagErrorBuf contains all error messages from pflag.
@ -216,6 +164,60 @@ type Command struct {
outWriter io.Writer outWriter io.Writer
// errWriter is a writer defined by the user that replaces stderr // errWriter is a writer defined by the user that replaces stderr
errWriter io.Writer 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 // Context returns underlying command context. If command wasn't
@ -418,7 +420,7 @@ func (c *Command) UsageString() string {
c.outWriter = bb c.outWriter = bb
c.errWriter = bb c.errWriter = bb
c.Usage() CheckErr(c.Usage())
// Setting things back to normal // Setting things back to normal
c.outWriter = tmpOutput c.outWriter = tmpOutput
@ -1087,10 +1089,10 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
cmd, _, e := c.Root().Find(args) cmd, _, e := c.Root().Find(args)
if cmd == nil || e != nil { if cmd == nil || e != nil {
c.Printf("Unknown help topic %#q\n", args) c.Printf("Unknown help topic %#q\n", args)
c.Root().Usage() CheckErr(c.Root().Usage())
} else { } else {
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
cmd.Help() CheckErr(cmd.Help())
} }
}, },
} }

View file

@ -58,6 +58,8 @@ func checkStringOmits(t *testing.T, got, expected string) {
} }
} }
const onetwo = "one two"
func TestSingleCommand(t *testing.T) { func TestSingleCommand(t *testing.T) {
var rootCmdArgs []string var rootCmdArgs []string
rootCmd := &Command{ rootCmd := &Command{
@ -78,9 +80,8 @@ func TestSingleCommand(t *testing.T) {
} }
got := strings.Join(rootCmdArgs, " ") got := strings.Join(rootCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
t.Errorf("rootCmdArgs expected: %q, got: %q", expected, got)
} }
} }
@ -104,9 +105,8 @@ func TestChildCommand(t *testing.T) {
} }
got := strings.Join(child1CmdArgs, " ") got := strings.Join(child1CmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("child1CmdArgs expected: %q, got: %q", onetwo, got)
t.Errorf("child1CmdArgs expected: %q, got: %q", expected, got)
} }
} }
@ -145,7 +145,7 @@ func TestSubcommandExecuteC(t *testing.T) {
} }
if c.Name() != "child" { if c.Name() != "child" {
t.Errorf(`invalid command returned from ExecuteC: expected "child"', got %q`, c.Name()) t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
} }
} }
@ -243,9 +243,8 @@ func TestCommandAlias(t *testing.T) {
} }
got := strings.Join(timesCmdArgs, " ") got := strings.Join(timesCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
} }
} }
@ -271,9 +270,8 @@ func TestEnablePrefixMatching(t *testing.T) {
} }
got := strings.Join(aCmdArgs, " ") got := strings.Join(aCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("aCmdArgs expected: %q, got: %q", onetwo, got)
t.Errorf("aCmdArgs expected: %q, got: %q", expected, got)
} }
EnablePrefixMatching = false EnablePrefixMatching = false
@ -307,9 +305,8 @@ func TestAliasPrefixMatching(t *testing.T) {
} }
got := strings.Join(timesCmdArgs, " ") got := strings.Join(timesCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
} }
EnablePrefixMatching = false EnablePrefixMatching = false
@ -338,9 +335,8 @@ func TestChildSameName(t *testing.T) {
} }
got := strings.Join(fooCmdArgs, " ") got := strings.Join(fooCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
} }
} }
@ -368,9 +364,8 @@ func TestGrandChildSameName(t *testing.T) {
} }
got := strings.Join(fooCmdArgs, " ") got := strings.Join(fooCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
} }
} }
@ -406,9 +401,8 @@ func TestFlagLong(t *testing.T) {
} }
got := strings.Join(cArgs, " ") got := strings.Join(cArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
t.Errorf("Expected arguments: %q, got %q", expected, got)
} }
} }
@ -441,9 +435,8 @@ func TestFlagShort(t *testing.T) {
} }
got := strings.Join(cArgs, " ") got := strings.Join(cArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
t.Errorf("Expected arguments: %q, got %q", expected, got)
} }
} }
@ -645,9 +638,8 @@ func TestPersistentFlagsOnSameCommand(t *testing.T) {
} }
got := strings.Join(rootCmdArgs, " ") got := strings.Join(rootCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("rootCmdArgs expected: %q, got %q", onetwo, got)
t.Errorf("rootCmdArgs expected: %q, got %q", expected, got)
} }
if flagValue != 7 { if flagValue != 7 {
t.Errorf("flagValue expected: %v, got %v", 7, flagValue) t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
@ -731,9 +723,8 @@ func TestPersistentFlagsOnChild(t *testing.T) {
} }
got := strings.Join(childCmdArgs, " ") got := strings.Join(childCmdArgs, " ")
expected := "one two" if got != onetwo {
if got != expected { t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
t.Errorf("childCmdArgs expected: %q, got %q", expected, got)
} }
if parentFlagValue != 8 { if parentFlagValue != 8 {
t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue) t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue)
@ -746,9 +737,9 @@ func TestPersistentFlagsOnChild(t *testing.T) {
func TestRequiredFlags(t *testing.T) { func TestRequiredFlags(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun} c := &Command{Use: "c", Run: emptyRun}
c.Flags().String("foo1", "", "") c.Flags().String("foo1", "", "")
c.MarkFlagRequired("foo1") assertNoErr(t, c.MarkFlagRequired("foo1"))
c.Flags().String("foo2", "", "") c.Flags().String("foo2", "", "")
c.MarkFlagRequired("foo2") assertNoErr(t, c.MarkFlagRequired("foo2"))
c.Flags().String("bar", "", "") c.Flags().String("bar", "", "")
expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2") expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2")
@ -764,16 +755,16 @@ func TestRequiredFlags(t *testing.T) {
func TestPersistentRequiredFlags(t *testing.T) { func TestPersistentRequiredFlags(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun} parent := &Command{Use: "parent", Run: emptyRun}
parent.PersistentFlags().String("foo1", "", "") parent.PersistentFlags().String("foo1", "", "")
parent.MarkPersistentFlagRequired("foo1") assertNoErr(t, parent.MarkPersistentFlagRequired("foo1"))
parent.PersistentFlags().String("foo2", "", "") parent.PersistentFlags().String("foo2", "", "")
parent.MarkPersistentFlagRequired("foo2") assertNoErr(t, parent.MarkPersistentFlagRequired("foo2"))
parent.Flags().String("foo3", "", "") parent.Flags().String("foo3", "", "")
child := &Command{Use: "child", Run: emptyRun} child := &Command{Use: "child", Run: emptyRun}
child.Flags().String("bar1", "", "") child.Flags().String("bar1", "", "")
child.MarkFlagRequired("bar1") assertNoErr(t, child.MarkFlagRequired("bar1"))
child.Flags().String("bar2", "", "") child.Flags().String("bar2", "", "")
child.MarkFlagRequired("bar2") assertNoErr(t, child.MarkFlagRequired("bar2"))
child.Flags().String("bar3", "", "") child.Flags().String("bar3", "", "")
parent.AddCommand(child) parent.AddCommand(child)
@ -793,7 +784,7 @@ func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun} parent := &Command{Use: "parent", Run: emptyRun}
parent.PersistentFlags().Bool("foo", false, "") parent.PersistentFlags().Bool("foo", false, "")
flag := parent.PersistentFlags().Lookup("foo") flag := parent.PersistentFlags().Lookup("foo")
parent.MarkPersistentFlagRequired("foo") assertNoErr(t, parent.MarkPersistentFlagRequired("foo"))
child := &Command{Use: "child", Run: emptyRun} child := &Command{Use: "child", Run: emptyRun}
child.DisableFlagParsing = true child.DisableFlagParsing = true
@ -1299,20 +1290,19 @@ func TestHooks(t *testing.T) {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
if persPreArgs != "one two" { for _, v := range []struct {
t.Errorf("Expected persPreArgs %q, got %q", "one two", persPreArgs) name string
} got string
if preArgs != "one two" { }{
t.Errorf("Expected preArgs %q, got %q", "one two", preArgs) {"persPreArgs", persPreArgs},
} {"preArgs", preArgs},
if runArgs != "one two" { {"runArgs", runArgs},
t.Errorf("Expected runArgs %q, got %q", "one two", runArgs) {"postArgs", postArgs},
} {"persPostArgs", persPostArgs},
if postArgs != "one two" { } {
t.Errorf("Expected postArgs %q, got %q", "one two", postArgs) if v.got != onetwo {
} t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
if persPostArgs != "one two" { }
t.Errorf("Expected persPostArgs %q, got %q", "one two", persPostArgs)
} }
} }
@ -1380,44 +1370,42 @@ func TestPersistentHooks(t *testing.T) {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
// TODO: currently PersistenPreRun* defined in parent does not for _, v := range []struct {
// run if the matchin child subcommand has PersistenPreRun. name string
// If the behavior changes (https://github.com/spf13/cobra/issues/252) got string
// this test must be fixed. }{
if parentPersPreArgs != "" { // TODO: currently PersistenPreRun* defined in parent does not
t.Errorf("Expected blank parentPersPreArgs, got %q", parentPersPreArgs) // run if the matchin child subcommand has PersistenPreRun.
} // If the behavior changes (https://github.com/spf13/cobra/issues/252)
if parentPreArgs != "" { // this test must be fixed.
t.Errorf("Expected blank parentPreArgs, got %q", parentPreArgs) {"parentPersPreArgs", parentPersPreArgs},
} {"parentPreArgs", parentPreArgs},
if parentRunArgs != "" { {"parentRunArgs", parentRunArgs},
t.Errorf("Expected blank parentRunArgs, got %q", parentRunArgs) {"parentPostArgs", parentPostArgs},
} // TODO: currently PersistenPostRun* defined in parent does not
if parentPostArgs != "" { // run if the matchin child subcommand has PersistenPostRun.
t.Errorf("Expected blank parentPostArgs, got %q", parentPostArgs) // If the behavior changes (https://github.com/spf13/cobra/issues/252)
} // this test must be fixed.
// TODO: currently PersistenPostRun* defined in parent does not {"parentPersPostArgs", parentPersPostArgs},
// run if the matchin child subcommand has PersistenPostRun. } {
// If the behavior changes (https://github.com/spf13/cobra/issues/252) if v.got != "" {
// this test must be fixed. t.Errorf("Expected blank %s, got %q", v.name, v.got)
if parentPersPostArgs != "" { }
t.Errorf("Expected blank parentPersPostArgs, got %q", parentPersPostArgs)
} }
if childPersPreArgs != "one two" { for _, v := range []struct {
t.Errorf("Expected childPersPreArgs %q, got %q", "one two", childPersPreArgs) name string
} got string
if childPreArgs != "one two" { }{
t.Errorf("Expected childPreArgs %q, got %q", "one two", childPreArgs) {"childPersPreArgs", childPersPreArgs},
} {"childPreArgs", childPreArgs},
if childRunArgs != "one two" { {"childRunArgs", childRunArgs},
t.Errorf("Expected childRunArgs %q, got %q", "one two", childRunArgs) {"childPostArgs", childPostArgs},
} {"childPersPostArgs", childPersPostArgs},
if childPostArgs != "one two" { } {
t.Errorf("Expected childPostArgs %q, got %q", "one two", childPostArgs) if v.got != onetwo {
} t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
if childPersPostArgs != "one two" { }
t.Errorf("Expected childPersPostArgs %q, got %q", "one two", childPersPostArgs)
} }
} }
@ -1741,7 +1729,7 @@ func TestMergeCommandLineToFlags(t *testing.T) {
func TestUseDeprecatedFlags(t *testing.T) { func TestUseDeprecatedFlags(t *testing.T) {
c := &Command{Use: "c", Run: emptyRun} c := &Command{Use: "c", Run: emptyRun}
c.Flags().BoolP("deprecated", "d", false, "deprecated flag") c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
c.Flags().MarkDeprecated("deprecated", "This flag is deprecated") assertNoErr(t, c.Flags().MarkDeprecated("deprecated", "This flag is deprecated"))
output, err := executeCommand(c, "c", "-d") output, err := executeCommand(c, "c", "-d")
if err != nil { if err != nil {
@ -1868,7 +1856,6 @@ type calledAsTestcase struct {
call string call string
want string want string
epm bool epm bool
tc bool
} }
func (tc *calledAsTestcase) test(t *testing.T) { func (tc *calledAsTestcase) test(t *testing.T) {
@ -1890,7 +1877,7 @@ func (tc *calledAsTestcase) test(t *testing.T) {
parent.SetOut(output) parent.SetOut(output)
parent.SetErr(output) parent.SetErr(output)
parent.Execute() _ = parent.Execute()
if called == nil { if called == nil {
if tc.call != "" { if tc.call != "" {
@ -1908,18 +1895,18 @@ func (tc *calledAsTestcase) test(t *testing.T) {
func TestCalledAs(t *testing.T) { func TestCalledAs(t *testing.T) {
tests := map[string]calledAsTestcase{ tests := map[string]calledAsTestcase{
"find/no-args": {nil, "parent", "parent", false, false}, "find/no-args": {nil, "parent", "parent", false},
"find/real-name": {[]string{"child1"}, "child1", "child1", false, false}, "find/real-name": {[]string{"child1"}, "child1", "child1", false},
"find/full-alias": {[]string{"that"}, "child2", "that", false, false}, "find/full-alias": {[]string{"that"}, "child2", "that", false},
"find/part-no-prefix": {[]string{"thi"}, "", "", false, false}, "find/part-no-prefix": {[]string{"thi"}, "", "", false},
"find/part-alias": {[]string{"thi"}, "child1", "this", true, false}, "find/part-alias": {[]string{"thi"}, "child1", "this", true},
"find/conflict": {[]string{"th"}, "", "", true, false}, "find/conflict": {[]string{"th"}, "", "", true},
"traverse/no-args": {nil, "parent", "parent", false, true}, "traverse/no-args": {nil, "parent", "parent", false},
"traverse/real-name": {[]string{"child1"}, "child1", "child1", false, true}, "traverse/real-name": {[]string{"child1"}, "child1", "child1", false},
"traverse/full-alias": {[]string{"that"}, "child2", "that", false, true}, "traverse/full-alias": {[]string{"that"}, "child2", "that", false},
"traverse/part-no-prefix": {[]string{"thi"}, "", "", false, true}, "traverse/part-no-prefix": {[]string{"thi"}, "", "", false},
"traverse/part-alias": {[]string{"thi"}, "child1", "this", true, true}, "traverse/part-alias": {[]string{"thi"}, "child1", "this", true},
"traverse/conflict": {[]string{"th"}, "", "", true, true}, "traverse/conflict": {[]string{"th"}, "", "", true},
} }
for name, tc := range tests { for name, tc := range tests {

View file

@ -527,13 +527,13 @@ func CompDebug(msg string, printToStdErr bool) {
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err == nil { if err == nil {
defer f.Close() defer f.Close()
f.WriteString(msg) WriteStringAndCheck(f, msg)
} }
} }
if printToStdErr { if printToStdErr {
// Must print to stderr for this not to be read by the completion script. // Must print to stderr for this not to be read by the completion script.
fmt.Fprintf(os.Stderr, msg) fmt.Fprint(os.Stderr, msg)
} }
} }

View file

@ -780,17 +780,17 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) {
rootCmd.AddCommand(childCmd) rootCmd.AddCommand(childCmd)
rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag") rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag")
rootCmd.MarkFlagRequired("requiredFlag") assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag"))
requiredFlag := rootCmd.Flags().Lookup("requiredFlag") requiredFlag := rootCmd.Flags().Lookup("requiredFlag")
rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent") rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent")
rootCmd.MarkPersistentFlagRequired("requiredPersistent") assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent"))
requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent") requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent")
rootCmd.Flags().StringP("release", "R", "", "Release name") rootCmd.Flags().StringP("release", "R", "", "Release name")
childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag") childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag")
childCmd.MarkFlagRequired("subRequired") assertNoErr(t, childCmd.MarkFlagRequired("subRequired"))
childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag") childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag")
// Test that a required flag is suggested even without the - prefix // Test that a required flag is suggested even without the - prefix
@ -964,19 +964,19 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) {
// No extensions. Should be ignored. // No extensions. Should be ignored.
rootCmd.Flags().StringP("file", "f", "", "file flag") rootCmd.Flags().StringP("file", "f", "", "file flag")
rootCmd.MarkFlagFilename("file") assertNoErr(t, rootCmd.MarkFlagFilename("file"))
// Single extension // Single extension
rootCmd.Flags().StringP("log", "l", "", "log flag") rootCmd.Flags().StringP("log", "l", "", "log flag")
rootCmd.MarkFlagFilename("log", "log") assertNoErr(t, rootCmd.MarkFlagFilename("log", "log"))
// Multiple extensions // Multiple extensions
rootCmd.Flags().StringP("yaml", "y", "", "yaml flag") rootCmd.Flags().StringP("yaml", "y", "", "yaml flag")
rootCmd.MarkFlagFilename("yaml", "yaml", "yml") assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml"))
// Directly using annotation // Directly using annotation
rootCmd.Flags().StringP("text", "t", "", "text flag") rootCmd.Flags().StringP("text", "t", "", "text flag")
rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"}) assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"}))
// Test that the completion logic returns the proper info for the completion // Test that the completion logic returns the proper info for the completion
// script to handle the file filtering // script to handle the file filtering
@ -1086,15 +1086,15 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) {
// Filter directories // Filter directories
rootCmd.Flags().StringP("dir", "d", "", "dir flag") rootCmd.Flags().StringP("dir", "d", "", "dir flag")
rootCmd.MarkFlagDirname("dir") assertNoErr(t, rootCmd.MarkFlagDirname("dir"))
// Filter directories within a directory // Filter directories within a directory
rootCmd.Flags().StringP("subdir", "s", "", "subdir") rootCmd.Flags().StringP("subdir", "s", "", "subdir")
rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"}) assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"}))
// Multiple directory specification get ignored // Multiple directory specification get ignored
rootCmd.Flags().StringP("manydir", "m", "", "manydir") rootCmd.Flags().StringP("manydir", "m", "", "manydir")
rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"}) assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"}))
// Test that the completion logic returns the proper info for the completion // Test that the completion logic returns the proper info for the completion
// script to handle the directory filtering // script to handle the directory filtering
@ -1430,7 +1430,7 @@ func TestValidArgsFuncInBashScript(t *testing.T) {
rootCmd.AddCommand(child) rootCmd.AddCommand(child)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenBashCompletion(buf) assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String() output := buf.String()
check(t, output, "has_completion_function=1") check(t, output, "has_completion_function=1")
@ -1445,7 +1445,7 @@ func TestNoValidArgsFuncInBashScript(t *testing.T) {
rootCmd.AddCommand(child) rootCmd.AddCommand(child)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenBashCompletion(buf) assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String() output := buf.String()
checkOmit(t, output, "has_completion_function=1") checkOmit(t, output, "has_completion_function=1")
@ -1461,7 +1461,7 @@ func TestCompleteCmdInBashScript(t *testing.T) {
rootCmd.AddCommand(child) rootCmd.AddCommand(child)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenBashCompletion(buf) assertNoErr(t, rootCmd.GenBashCompletion(buf))
output := buf.String() output := buf.String()
check(t, output, ShellCompNoDescRequestCmd) check(t, output, ShellCompNoDescRequestCmd)
@ -1477,7 +1477,7 @@ func TestCompleteNoDesCmdInZshScript(t *testing.T) {
rootCmd.AddCommand(child) rootCmd.AddCommand(child)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenZshCompletionNoDesc(buf) assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf))
output := buf.String() output := buf.String()
check(t, output, ShellCompNoDescRequestCmd) check(t, output, ShellCompNoDescRequestCmd)
@ -1493,7 +1493,7 @@ func TestCompleteCmdInZshScript(t *testing.T) {
rootCmd.AddCommand(child) rootCmd.AddCommand(child)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenZshCompletion(buf) assertNoErr(t, rootCmd.GenZshCompletion(buf))
output := buf.String() output := buf.String()
check(t, output, ShellCompRequestCmd) check(t, output, ShellCompRequestCmd)
@ -1506,7 +1506,7 @@ func TestFlagCompletionInGo(t *testing.T) {
Run: emptyRun, Run: emptyRun,
} }
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{} completions := []string{}
for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
if strings.HasPrefix(comp, toComplete) { if strings.HasPrefix(comp, toComplete) {
@ -1514,9 +1514,9 @@ func TestFlagCompletionInGo(t *testing.T) {
} }
} }
return completions, ShellCompDirectiveDefault return completions, ShellCompDirectiveDefault
}) }))
rootCmd.Flags().String("filename", "", "Enter a filename") rootCmd.Flags().String("filename", "", "Enter a filename")
rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{} completions := []string{}
for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
if strings.HasPrefix(comp, toComplete) { if strings.HasPrefix(comp, toComplete) {
@ -1524,7 +1524,7 @@ func TestFlagCompletionInGo(t *testing.T) {
} }
} }
return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
}) }))
// Test completing an empty string // Test completing an empty string
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "") output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "")
@ -1703,7 +1703,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
Run: emptyRun, Run: emptyRun,
} }
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{} completions := []string{}
for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
if strings.HasPrefix(comp, toComplete) { if strings.HasPrefix(comp, toComplete) {
@ -1711,9 +1711,9 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
} }
} }
return completions, ShellCompDirectiveDefault return completions, ShellCompDirectiveDefault
}) }))
rootCmd.Flags().String("filename", "", "Enter a filename") rootCmd.Flags().String("filename", "", "Enter a filename")
rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
completions := []string{} completions := []string{}
for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
if strings.HasPrefix(comp, toComplete) { if strings.HasPrefix(comp, toComplete) {
@ -1721,7 +1721,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
} }
} }
return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
}) }))
// Test completing an empty string // Test completing an empty string
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "") output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")

View file

@ -139,23 +139,23 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
return nil 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 description := cmd.Long
if len(description) == 0 { if len(description) == 0 {
description = cmd.Short description = cmd.Short
} }
buf.WriteString(fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s" cobra.WriteStringAndCheck(buf, fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s"
# NAME # NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual)) `, header.Title, header.Section, header.date, header.Source, header.Manual))
buf.WriteString(fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short)) cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
buf.WriteString("# SYNOPSIS\n") cobra.WriteStringAndCheck(buf, "# SYNOPSIS\n")
buf.WriteString(fmt.Sprintf("**%s**\n\n", cmd.UseLine())) cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
buf.WriteString("# DESCRIPTION\n") cobra.WriteStringAndCheck(buf, "# DESCRIPTION\n")
buf.WriteString(description + "\n\n") cobra.WriteStringAndCheck(buf, description+"\n\n")
} }
func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) { func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) { flags.VisitAll(func(flag *pflag.Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden { if len(flag.Deprecated) > 0 || flag.Hidden {
return return
@ -179,22 +179,22 @@ func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
format += "]" format += "]"
} }
format += "\n\t%s\n\n" format += "\n\t%s\n\n"
buf.WriteString(fmt.Sprintf(format, flag.DefValue, flag.Usage)) cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, flag.Usage))
}) })
} }
func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) { func manPrintOptions(buf io.StringWriter, command *cobra.Command) {
flags := command.NonInheritedFlags() flags := command.NonInheritedFlags()
if flags.HasAvailableFlags() { if flags.HasAvailableFlags() {
buf.WriteString("# OPTIONS\n") cobra.WriteStringAndCheck(buf, "# OPTIONS\n")
manPrintFlags(buf, flags) manPrintFlags(buf, flags)
buf.WriteString("\n") cobra.WriteStringAndCheck(buf, "\n")
} }
flags = command.InheritedFlags() flags = command.InheritedFlags()
if flags.HasAvailableFlags() { if flags.HasAvailableFlags() {
buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n") cobra.WriteStringAndCheck(buf, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
manPrintFlags(buf, flags) manPrintFlags(buf, flags)
buf.WriteString("\n") cobra.WriteStringAndCheck(buf, "\n")
} }
} }

View file

@ -13,6 +13,12 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func assertNoErr(t *testing.T, e error) {
if e != nil {
t.Error(e)
}
}
func translate(in string) string { func translate(in string) string {
return strings.Replace(in, "-", "\\-", -1) return strings.Replace(in, "-", "\\-", -1)
} }
@ -133,7 +139,7 @@ func TestGenManSeeAlso(t *testing.T) {
func TestManPrintFlagsHidesShortDeperecated(t *testing.T) { func TestManPrintFlagsHidesShortDeperecated(t *testing.T) {
c := &cobra.Command{} c := &cobra.Command{}
c.Flags().StringP("foo", "f", "default", "Foo flag") c.Flags().StringP("foo", "f", "default", "Foo flag")
c.Flags().MarkShorthandDeprecated("foo", "don't use it no more") assertNoErr(t, c.Flags().MarkShorthandDeprecated("foo", "don't use it no more"))
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
manPrintFlags(buf, c.Flags()) manPrintFlags(buf, c.Flags())

View file

@ -17,7 +17,7 @@ func ExampleGenManTree() {
Title: "MINE", Title: "MINE",
Section: "3", Section: "3",
} }
doc.GenManTree(cmd, header, "/tmp") cobra.CheckErr(doc.GenManTree(cmd, header, "/tmp"))
} }
func ExampleGenMan() { func ExampleGenMan() {
@ -30,6 +30,6 @@ func ExampleGenMan() {
Section: "3", Section: "3",
} }
out := new(bytes.Buffer) out := new(bytes.Buffer)
doc.GenMan(cmd, header, out) cobra.CheckErr(doc.GenMan(cmd, header, out))
fmt.Print(out.String()) fmt.Print(out.String())
} }

View file

@ -8,7 +8,7 @@ import (
"strings" "strings"
) )
func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) { func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
// Variables should not contain a '-' or ':' character // Variables should not contain a '-' or ':' character
nameForVar := name nameForVar := name
nameForVar = strings.Replace(nameForVar, "-", "_", -1) nameForVar = strings.Replace(nameForVar, "-", "_", -1)
@ -18,8 +18,8 @@ func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
if !includeDesc { if !includeDesc {
compCmd = ShellCompNoDescRequestCmd compCmd = ShellCompNoDescRequestCmd
} }
buf.WriteString(fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name)) WriteStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
buf.WriteString(fmt.Sprintf(` WriteStringAndCheck(buf, fmt.Sprintf(`
function __%[1]s_debug function __%[1]s_debug
set file "$BASH_COMP_DEBUG_FILE" set file "$BASH_COMP_DEBUG_FILE"
if test -n "$file" if test -n "$file"

View file

@ -15,7 +15,7 @@ func TestCompleteNoDesCmdInFishScript(t *testing.T) {
rootCmd.AddCommand(child) rootCmd.AddCommand(child)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenFishCompletion(buf, false) assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
output := buf.String() output := buf.String()
check(t, output, ShellCompNoDescRequestCmd) check(t, output, ShellCompNoDescRequestCmd)
@ -31,7 +31,7 @@ func TestCompleteCmdInFishScript(t *testing.T) {
rootCmd.AddCommand(child) rootCmd.AddCommand(child)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenFishCompletion(buf, true) assertNoErr(t, rootCmd.GenFishCompletion(buf, true))
output := buf.String() output := buf.String()
check(t, output, ShellCompRequestCmd) check(t, output, ShellCompRequestCmd)
@ -41,7 +41,7 @@ func TestCompleteCmdInFishScript(t *testing.T) {
func TestProgWithDash(t *testing.T) { func TestProgWithDash(t *testing.T) {
rootCmd := &Command{Use: "root-dash", Args: NoArgs, Run: emptyRun} rootCmd := &Command{Use: "root-dash", Args: NoArgs, Run: emptyRun}
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenFishCompletion(buf, false) assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
output := buf.String() output := buf.String()
// Functions name should have replace the '-' // Functions name should have replace the '-'
@ -56,7 +56,7 @@ func TestProgWithDash(t *testing.T) {
func TestProgWithColon(t *testing.T) { func TestProgWithColon(t *testing.T) {
rootCmd := &Command{Use: "root:colon", Args: NoArgs, Run: emptyRun} rootCmd := &Command{Use: "root:colon", Args: NoArgs, Run: emptyRun}
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenFishCompletion(buf, false) assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
output := buf.String() output := buf.String()
// Functions name should have replace the ':' // Functions name should have replace the ':'

View file

@ -10,12 +10,12 @@ import (
"os" "os"
) )
func genPowerShellComp(buf *bytes.Buffer, name string, includeDesc bool) { func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
compCmd := ShellCompRequestCmd compCmd := ShellCompRequestCmd
if !includeDesc { if !includeDesc {
compCmd = ShellCompNoDescRequestCmd compCmd = ShellCompNoDescRequestCmd
} }
buf.WriteString(fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*- WriteStringAndCheck(buf, fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*-
function __%[1]s_debug { function __%[1]s_debug {
if ($env:BASH_COMP_DEBUG_FILE) { if ($env:BASH_COMP_DEBUG_FILE) {
@ -46,12 +46,12 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
# We need to trigger completion from the $CursorPosition location, so we need # We need to trigger completion from the $CursorPosition location, so we need
# to truncate the command-line ($Command) up to the $CursorPosition location. # to truncate the command-line ($Command) up to the $CursorPosition location.
# Make sure the $Command is longer then the $CursorPosition before we truncate. # Make sure the $Command is longer then the $CursorPosition before we truncate.
# This happens because the $Command does not include the last space. # This happens because the $Command does not include the last space.
if ($Command.Length -gt $CursorPosition) { if ($Command.Length -gt $CursorPosition) {
$Command=$Command.Substring(0,$CursorPosition) $Command=$Command.Substring(0,$CursorPosition)
} }
__%[1]s_debug "Truncated command: $Command" __%[1]s_debug "Truncated command: $Command"
$ShellCompDirectiveError=%[3]d $ShellCompDirectiveError=%[3]d
$ShellCompDirectiveNoSpace=%[4]d $ShellCompDirectiveNoSpace=%[4]d
$ShellCompDirectiveNoFileComp=%[5]d $ShellCompDirectiveNoFileComp=%[5]d
@ -64,7 +64,7 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
$RequestComp="$Program %[2]s $Arguments" $RequestComp="$Program %[2]s $Arguments"
__%[1]s_debug "RequestComp: $RequestComp" __%[1]s_debug "RequestComp: $RequestComp"
# we cannot use $WordToComplete because it # we cannot use $WordToComplete because it
# has the wrong values if the cursor was moved # has the wrong values if the cursor was moved
# so use the last argument # so use the last argument
if ($WordToComplete -ne "" ) { if ($WordToComplete -ne "" ) {
@ -102,7 +102,7 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
$Directive = 0 $Directive = 0
} }
__%[1]s_debug "The completion directive is: $Directive" __%[1]s_debug "The completion directive is: $Directive"
# remove directive (last element) from out # remove directive (last element) from out
$Out = $Out | Where-Object { $_ -ne $Out[-1] } $Out = $Out | Where-Object { $_ -ne $Out[-1] }
__%[1]s_debug "The completions are: $Out" __%[1]s_debug "The completions are: $Out"
@ -142,18 +142,18 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
__%[1]s_debug "ShellCompDirectiveNoFileComp is called" __%[1]s_debug "ShellCompDirectiveNoFileComp is called"
if ($Values.Length -eq 0) { if ($Values.Length -eq 0) {
# Just print an empty string here so the # Just print an empty string here so the
# shell does not start to complete paths. # shell does not start to complete paths.
# We cannot use CompletionResult here because # We cannot use CompletionResult here because
# it does not accept an empty string as argument. # it does not accept an empty string as argument.
"" ""
return return
} }
} }
if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or
(($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) { (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) {
__%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" __%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported"
@ -170,7 +170,7 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
__%[1]s_debug "Join the equal sign flag back to the completion value" __%[1]s_debug "Join the equal sign flag back to the completion value"
$_.Name = $Flag + "=" + $_.Name $_.Name = $Flag + "=" + $_.Name
} }
} }
# Get the current mode # Get the current mode
$Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function
@ -224,14 +224,14 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
# zsh like # zsh like
"MenuComplete" { "MenuComplete" {
# insert space after value # insert space after value
# MenuComplete will automatically show the ToolTip of # MenuComplete will automatically show the ToolTip of
# the highlighted value at the bottom of the suggestions. # the highlighted value at the bottom of the suggestions.
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
} }
# TabCompleteNext and in case we get something unknown # TabCompleteNext and in case we get something unknown
Default { Default {
# Like MenuComplete but we don't want to add a space here because # Like MenuComplete but we don't want to add a space here because
# the user need to press space anyway to get the completion. # the user need to press space anyway to get the completion.
# Description will not be shown because thats not possible with TabCompleteNext # Description will not be shown because thats not possible with TabCompleteNext
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")

View file

@ -99,8 +99,7 @@ cmd := &cobra.Command{
Long: get_long, Long: get_long,
Example: get_example, Example: get_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunGet(f, out, cmd, args) cobra.CheckErr(RunGet(f, out, cmd, args))
util.CheckErr(err)
}, },
ValidArgs: validArgs, ValidArgs: validArgs,
} }
@ -132,7 +131,7 @@ the completion algorithm if entered manually, e.g. in:
```bash ```bash
$ kubectl get rc [tab][tab] $ kubectl get rc [tab][tab]
backend frontend database backend frontend database
``` ```
Note that without declaring `rc` as an alias, the completion algorithm would not know to show the list of Note that without declaring `rc` as an alias, the completion algorithm would not know to show the list of
@ -254,7 +253,7 @@ and you'll get something like
```bash ```bash
$ kubectl exec [tab][tab] $ kubectl exec [tab][tab]
-c --container= -p --pod= -c --container= -p --pod=
``` ```
### Specify dynamic flag completion ### Specify dynamic flag completion

View file

@ -70,12 +70,12 @@ func (c *Command) genZshCompletion(w io.Writer, includeDesc bool) error {
return err return err
} }
func genZshComp(buf *bytes.Buffer, name string, includeDesc bool) { func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
compCmd := ShellCompRequestCmd compCmd := ShellCompRequestCmd
if !includeDesc { if !includeDesc {
compCmd = ShellCompNoDescRequestCmd compCmd = ShellCompNoDescRequestCmd
} }
buf.WriteString(fmt.Sprintf(`#compdef _%[1]s %[1]s WriteStringAndCheck(buf, fmt.Sprintf(`#compdef _%[1]s %[1]s
# zsh completion for %-36[1]s -*- shell-script -*- # zsh completion for %-36[1]s -*- shell-script -*-