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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,14 +3,11 @@ package cmd
import (
"bytes"
"errors"
"flag"
"fmt"
"io/ioutil"
"os/exec"
)
var update = flag.Bool("update", false, "update .golden files")
func init() {
// Mute commands.
addCmd.SetOut(new(bytes.Buffer))
@ -58,27 +55,3 @@ func compareFiles(pathA, pathB string) error {
}
return nil
}
// checkLackFiles checks if all elements of expected are in got.
func checkLackFiles(expected, got []string) error {
lacks := make([]string, 0, len(expected))
for _, ev := range expected {
if !stringInStringSlice(ev, got) {
lacks = append(lacks, ev)
}
}
if len(lacks) > 0 {
return fmt.Errorf("Lack %v file(s): %v", len(lacks), lacks)
}
return nil
}
// stringInStringSlice checks if s is an element of slice.
func stringInStringSlice(s string, slice []string) bool {
for _, v := range slice {
if s == v {
return true
}
}
return false
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -84,9 +84,6 @@ type Command struct {
// Deprecated defines, if this command is deprecated and should print this string when used.
Deprecated string
// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
Hidden bool
// Annotations are key/value pairs that can be used by applications to identify or
// group commands.
Annotations map[string]string
@ -126,55 +123,6 @@ type Command struct {
// PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) error
// SilenceErrors is an option to quiet errors down stream.
SilenceErrors bool
// SilenceUsage is an option to silence usage when an error occurs.
SilenceUsage bool
// DisableFlagParsing disables the flag parsing.
// If this is true all flags will be passed to the command as arguments.
DisableFlagParsing bool
// DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
// will be printed by generating docs for this command.
DisableAutoGenTag bool
// DisableFlagsInUseLine will disable the addition of [flags] to the usage
// line of a command when printing help or generating docs
DisableFlagsInUseLine bool
// DisableSuggestions disables the suggestions based on Levenshtein distance
// that go along with 'unknown command' messages.
DisableSuggestions bool
// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0.
SuggestionsMinimumDistance int
// TraverseChildren parses flags on all parents before executing child command.
TraverseChildren bool
// FParseErrWhitelist flag parse errors to be ignored
FParseErrWhitelist FParseErrWhitelist
ctx context.Context
// commands is the list of commands supported by this program.
commands []*Command
// parent is a parent command for this command.
parent *Command
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
// commandsAreSorted defines, if command slice are sorted or not.
commandsAreSorted bool
// commandCalledAs is the name or alias value used to call this command.
commandCalledAs struct {
name string
called bool
}
// args is actual args parsed from flags.
args []string
// flagErrorBuf contains all error messages from pflag.
@ -216,6 +164,60 @@ type Command struct {
outWriter io.Writer
// errWriter is a writer defined by the user that replaces stderr
errWriter io.Writer
//FParseErrWhitelist flag parse errors to be ignored
FParseErrWhitelist FParseErrWhitelist
// commandsAreSorted defines, if command slice are sorted or not.
commandsAreSorted bool
// commandCalledAs is the name or alias value used to call this command.
commandCalledAs struct {
name string
called bool
}
ctx context.Context
// commands is the list of commands supported by this program.
commands []*Command
// parent is a parent command for this command.
parent *Command
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
// TraverseChildren parses flags on all parents before executing child command.
TraverseChildren bool
// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
Hidden bool
// SilenceErrors is an option to quiet errors down stream.
SilenceErrors bool
// SilenceUsage is an option to silence usage when an error occurs.
SilenceUsage bool
// DisableFlagParsing disables the flag parsing.
// If this is true all flags will be passed to the command as arguments.
DisableFlagParsing bool
// DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
// will be printed by generating docs for this command.
DisableAutoGenTag bool
// DisableFlagsInUseLine will disable the addition of [flags] to the usage
// line of a command when printing help or generating docs
DisableFlagsInUseLine bool
// DisableSuggestions disables the suggestions based on Levenshtein distance
// that go along with 'unknown command' messages.
DisableSuggestions bool
// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0.
SuggestionsMinimumDistance int
}
// Context returns underlying command context. If command wasn't
@ -418,7 +420,7 @@ func (c *Command) UsageString() string {
c.outWriter = bb
c.errWriter = bb
c.Usage()
CheckErr(c.Usage())
// Setting things back to normal
c.outWriter = tmpOutput
@ -1087,10 +1089,10 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
cmd, _, e := c.Root().Find(args)
if cmd == nil || e != nil {
c.Printf("Unknown help topic %#q\n", args)
c.Root().Usage()
CheckErr(c.Root().Usage())
} else {
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
cmd.Help()
CheckErr(cmd.Help())
}
},
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,12 +10,12 @@ import (
"os"
)
func genPowerShellComp(buf *bytes.Buffer, name string, includeDesc bool) {
func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
compCmd := ShellCompRequestCmd
if !includeDesc {
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 {
if ($env:BASH_COMP_DEBUG_FILE) {

View File

@ -99,8 +99,7 @@ cmd := &cobra.Command{
Long: get_long,
Example: get_example,
Run: func(cmd *cobra.Command, args []string) {
err := RunGet(f, out, cmd, args)
util.CheckErr(err)
cobra.CheckErr(RunGet(f, out, cmd, args))
},
ValidArgs: validArgs,
}

View File

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