Update spf13/cobra to the latest

This commit is contained in:
Eric Paris 2015-04-12 14:48:54 -04:00
parent f96ef20e24
commit ef815836d2
13 changed files with 1151 additions and 70 deletions

2
Godeps/Godeps.json generated
View File

@ -396,7 +396,7 @@
}, },
{ {
"ImportPath": "github.com/spf13/cobra", "ImportPath": "github.com/spf13/cobra",
"Rev": "79bd93d369fb73d640179208d4e2b1a748915567" "Rev": "c0da825198c75814463e1b3018e42e438b771a5b"
}, },
{ {
"ImportPath": "github.com/spf13/pflag", "ImportPath": "github.com/spf13/pflag",

View File

@ -1,6 +1,8 @@
language: go language: go
go: go:
- 1.1 - 1.3
- 1.4.2
- tip
script: script:
- go test ./... - go test ./...
- go build - go build

View File

@ -2,7 +2,7 @@
A Commander for modern go CLI interactions A Commander for modern go CLI interactions
[![Build Status](https://travis-ci.org/spf13/cobra.png)](https://travis-ci.org/spf13/cobra) [![Build Status](https://travis-ci.org/spf13/cobra.svg)](https://travis-ci.org/spf13/cobra)
## Overview ## Overview
@ -143,6 +143,15 @@ A flag can also be assigned locally which will only apply to that specific comma
HugoCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") HugoCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
### Remove a command from its parent
Removing a command is not a common action is simple program but it allows 3rd parties to customize an existing command tree.
In this exemple, we remove the existing `VersionCmd` command of an existing root command, and we replace it by our own version.
mainlib.RootCmd.RemoveCommand(mainlib.VersionCmd)
mainlib.RootCmd.AddCommand(versionCmd)
### Once all commands and flags are defined, Execute the commands ### Once all commands and flags are defined, Execute the commands
Execute should be run on the root for clarity, though it can be called on any command. Execute should be run on the root for clarity, though it can be called on any command.
@ -339,6 +348,13 @@ Like help the function and template are over ridable through public methods.
command.SetUsageTemplate(s string) command.SetUsageTemplate(s string)
## Generating markdown formatted documentation for your command
Cobra can generate a markdown formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](md_docs.md)
## Generating bash completions for your command
Cobra can generate a bash completions file. If you add more information to your command these completions can be amazingly powerful and flexible. Read more about [Bash Completions](bash_completions.md)
## Debugging ## Debugging

View File

@ -0,0 +1,343 @@
package cobra
import (
"bytes"
"fmt"
"os"
"strings"
"github.com/spf13/pflag"
)
const (
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extentions"
BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
)
func preamble(out *bytes.Buffer) {
fmt.Fprintf(out, `#!/bin/bash
__debug()
{
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
echo "$*" >> ${BASH_COMP_DEBUG_FILE}
fi
}
__index_of_word()
{
local w word=$1
shift
index=0
for w in "$@"; do
[[ $w = "$word" ]] && return
index=$((index+1))
done
index=-1
}
__contains_word()
{
local w word=$1; shift
for w in "$@"; do
[[ $w = "$word" ]] && return
done
return 1
}
__handle_reply()
{
__debug "${FUNCNAME}"
case $cur in
-*)
compopt -o nospace
local allflags
if [ ${#must_have_one_flag[@]} -ne 0 ]; then
allflags=("${must_have_one_flag[@]}")
else
allflags=("${flags[*]} ${two_word_flags[*]}")
fi
COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") )
[[ $COMPREPLY == *= ]] || compopt +o nospace
return 0;
;;
esac
# check if we are handling a flag with special work handling
local index
__index_of_word "${prev}" "${flags_with_completion[@]}"
if [[ ${index} -ge 0 ]]; then
${flags_completion[${index}]}
return
fi
# we are parsing a flag and don't have a special handler, no completion
if [[ ${cur} != "${words[cword]}" ]]; then
return
fi
local completions
if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
completions=("${must_have_one_flag[@]}")
elif [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
completions=("${must_have_one_noun[@]}")
else
completions=("${commands[@]}")
fi
COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
declare -F __custom_func >/dev/null && __custom_func
fi
}
__handle_flag()
{
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
# if a command required a flag, and we found it, unset must_have_one_flag()
local flagname=${words[c]}
# if the word contained an =
if [[ ${words[c]} == *"="* ]]; then
flagname=${flagname%%=*} # strip everything after the =
flagname="${flagname}=" # but put the = back
fi
__debug "${FUNCNAME}: looking for ${flagname}"
if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then
must_have_one_flag=()
fi
# skip the argument to a two word flag
if __contains_word "${words[c]}" "${two_word_flags[@]}"; then
c=$((c+1))
# if we are looking for a flags value, don't show commands
if [[ $c -eq $cword ]]; then
commands=()
fi
fi
# skip the flag itself
c=$((c+1))
}
__handle_noun()
{
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
must_have_one_noun=()
fi
nouns+=("${words[c]}")
c=$((c+1))
}
__handle_command()
{
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
local next_command
if [[ -n ${last_command} ]]; then
next_command="_${last_command}_${words[c]}"
else
next_command="_${words[c]}"
fi
c=$((c+1))
__debug "${FUNCNAME}: looking for ${next_command}"
declare -F $next_command >/dev/null && $next_command
}
__handle_word()
{
if [[ $c -ge $cword ]]; then
__handle_reply
return
fi
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
if [[ "${words[c]}" == -* ]]; then
__handle_flag
elif __contains_word "${words[c]}" "${commands[@]}"; then
__handle_command
else
__handle_noun
fi
__handle_word
}
`)
}
func postscript(out *bytes.Buffer, name string) {
fmt.Fprintf(out, "__start_%s()\n", name)
fmt.Fprintf(out, `{
local cur prev words cword split
_init_completion -s || return
local completions_func
local c=0
local flags=()
local two_word_flags=()
local flags_with_completion=()
local flags_completion=()
local commands=("%s")
local must_have_one_flag=()
local must_have_one_noun=()
local last_command
local nouns=()
__handle_word
}
`, name)
fmt.Fprintf(out, "complete -F __start_%s %s\n", name, name)
fmt.Fprintf(out, "# ex: ts=4 sw=4 et filetype=sh\n")
}
func writeCommands(cmd *Command, out *bytes.Buffer) {
fmt.Fprintf(out, " commands=()\n")
for _, c := range cmd.Commands() {
fmt.Fprintf(out, " commands+=(%q)\n", c.Name())
}
fmt.Fprintf(out, "\n")
}
func writeFlagHandler(name string, annotations map[string][]string, out *bytes.Buffer) {
for key, value := range annotations {
switch key {
case BashCompFilenameExt:
fmt.Fprintf(out, " flags_with_completion+=(%q)\n", name)
ext := strings.Join(value, "|")
ext = "_filedir '@(" + ext + ")'"
fmt.Fprintf(out, " flags_completion+=(%q)\n", ext)
}
}
}
func writeShortFlag(flag *pflag.Flag, out *bytes.Buffer) {
b := (flag.Value.Type() == "bool")
name := flag.Shorthand
format := " "
if !b {
format += "two_word_"
}
format += "flags+=(\"-%s\")\n"
fmt.Fprintf(out, format, name)
writeFlagHandler("-"+name, flag.Annotations, out)
}
func writeFlag(flag *pflag.Flag, out *bytes.Buffer) {
b := (flag.Value.Type() == "bool")
name := flag.Name
format := " flags+=(\"--%s"
if !b {
format += "="
}
format += "\")\n"
fmt.Fprintf(out, format, name)
writeFlagHandler("--"+name, flag.Annotations, out)
}
func writeFlags(cmd *Command, out *bytes.Buffer) {
fmt.Fprintf(out, ` flags=()
two_word_flags=()
flags_with_completion=()
flags_completion=()
`)
cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
writeFlag(flag, out)
if len(flag.Shorthand) > 0 {
writeShortFlag(flag, out)
}
})
fmt.Fprintf(out, "\n")
}
func writeRequiredFlag(cmd *Command, out *bytes.Buffer) {
fmt.Fprintf(out, " must_have_one_flag=()\n")
flags := cmd.NonInheritedFlags()
flags.VisitAll(func(flag *pflag.Flag) {
for key, _ := range flag.Annotations {
switch key {
case BashCompOneRequiredFlag:
format := " must_have_one_flag+=(\"--%s"
b := (flag.Value.Type() == "bool")
if !b {
format += "="
}
format += "\")\n"
fmt.Fprintf(out, format, flag.Name)
if len(flag.Shorthand) > 0 {
fmt.Fprintf(out, " must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)
}
}
}
})
}
func writeRequiredNoun(cmd *Command, out *bytes.Buffer) {
fmt.Fprintf(out, " must_have_one_noun=()\n")
for _, value := range cmd.ValidArgs {
fmt.Fprintf(out, " must_have_one_noun+=(%q)\n", value)
}
}
func gen(cmd *Command, out *bytes.Buffer) {
for _, c := range cmd.Commands() {
gen(c, out)
}
commandName := cmd.CommandPath()
commandName = strings.Replace(commandName, " ", "_", -1)
fmt.Fprintf(out, "_%s()\n{\n", commandName)
fmt.Fprintf(out, " last_command=%q\n", commandName)
writeCommands(cmd, out)
writeFlags(cmd, out)
writeRequiredFlag(cmd, out)
writeRequiredNoun(cmd, out)
fmt.Fprintf(out, "}\n\n")
}
func (cmd *Command) GenBashCompletion(out *bytes.Buffer) {
preamble(out)
if len(cmd.BashCompletionFunction) > 0 {
fmt.Fprintf(out, "%s\n", cmd.BashCompletionFunction)
}
gen(cmd, out)
postscript(out, cmd.Name())
}
func (cmd *Command) GenBashCompletionFile(filename string) error {
out := new(bytes.Buffer)
cmd.GenBashCompletion(out)
outFile, err := os.Create(filename)
if err != nil {
return err
}
defer outFile.Close()
_, err = outFile.Write(out.Bytes())
if err != nil {
return err
}
return nil
}
func (cmd *Command) MarkFlagRequired(name string) {
flag := cmd.Flags().Lookup(name)
if flag == nil {
return
}
if flag.Annotations == nil {
flag.Annotations = make(map[string][]string)
}
annotation := make([]string, 1)
annotation[0] = "true"
flag.Annotations[BashCompOneRequiredFlag] = annotation
}

View File

@ -0,0 +1,146 @@
# Generating Bash Completions For Your Own cobra.Command
Generating bash completions from a cobra command is incredibly easy. An actual program which does so for the kubernetes kubectl binary is as follows:
```go
package main
import (
"io/ioutil"
"os"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
)
func main() {
kubectl := cmd.NewFactory(nil).NewKubectlCommand(os.Stdin, ioutil.Discard, ioutil.Discard)
kubectl.GenBashCompletionFile("out.sh")
}
```
That will get you completions of subcommands and flags. If you make additional annotations to your code, you can get even more intelligent and flexible behavior.
## Creating your own custom functions
Some more actual code that works in kubernetes:
```bash
const (
bash_completion_func = `__kubectl_parse_get()
{
local kubectl_output out
if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then
out=($(echo "${kubectl_output}" | awk '{print $1}'))
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__kubectl_get_resource()
{
if [[ ${#nouns[@]} -eq 0 ]]; then
return 1
fi
__kubectl_parse_get ${nouns[${#nouns[@]} -1]}
if [[ $? -eq 0 ]]; then
return 0
fi
}
__custom_func() {
case ${last_command} in
kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop)
__kubectl_get_resource
return
;;
*)
;;
esac
}
`)
```
And then I set that in my command definition:
```go
cmds := &cobra.Command{
Use: "kubectl",
Short: "kubectl controls the Kubernetes cluster manager",
Long: `kubectl controls the Kubernetes cluster manager.
Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
Run: runHelp,
BashCompletionFunction: bash_completion_func,
}
```
The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__custom_func()` to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods!
## Have the completions code complete your 'nouns'
In the above example "pod" was assumed to already be typed. But if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like:
```go
validArgs []string = { "pods", "nodes", "services", "replicationControllers" }
cmd := &cobra.Command{
Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
Short: "Display one or many resources",
Long: get_long,
Example: get_example,
Run: func(cmd *cobra.Command, args []string) {
err := RunGet(f, out, cmd, args)
util.CheckErr(err)
},
ValidArgs: validArgs,
}
```
Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like
```bash
# kubectl get [tab][tab]
nodes pods replicationControllers services
```
## Mark flags as required
Most of the time completions will only show subcommands. But if a flag is required to make a subcommand work, you probably want it to show up when the user types [tab][tab]. Marking a flag as 'Required' is incredibly easy.
```go
cmd.MarkFlagRequired("pod")
cmd.MarkFlagRequired("container")
```
and you'll get something like
```bash
# kubectl exec [tab][tab][tab]
-c --container= -p --pod=
```
# Specify valid filename extentions for flags that take a filename
In this example we use --filename= and expect to get a json or yaml file as the argument. To make this easier we annotate the --filename flag with valid filename extensions.
```go
annotations := make([]string, 3)
annotations[0] = "json"
annotations[1] = "yaml"
annotations[2] = "yml"
annotation := make(map[string][]string)
annotation[cobra.BashCompFilenameExt] = annotations
flag := &pflag.Flag{"filename", "f", usage, value, value.String(), false, annotation}
cmd.Flags().AddFlag(flag)
```
Now when you run a command with this filename flag you'll get something like
```bash
# kubectl create -f
test/ example/ rpmbuild/
hello.yml test.json
```
So while there are many other files in the CWD it only shows me subdirs and those with valid extensions.

View File

@ -0,0 +1,74 @@
package cobra
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
)
var _ = fmt.Println
var _ = os.Stderr
func check(t *testing.T, found, expected string) {
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
}
// World worst custom function, just keep telling you to enter hello!
const (
bash_completion_func = `__custom_func() {
COMPREPLY=( "hello" )
}
`
)
func TestBashCompletions(t *testing.T) {
c := initializeWithRootCmd()
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdEcho, cmdPrint)
// custom completion function
c.BashCompletionFunction = bash_completion_func
// required flag
c.MarkFlagRequired("introot")
// valid nounds
validArgs := []string{"pods", "nodes", "services", "replicationControllers"}
c.ValidArgs = validArgs
// filename extentions
annotations := make([]string, 3)
annotations[0] = "json"
annotations[1] = "yaml"
annotations[2] = "yml"
annotation := make(map[string][]string)
annotation[BashCompFilenameExt] = annotations
var flagval string
c.Flags().StringVar(&flagval, "filename", "", "Enter a filename")
flag := c.Flags().Lookup("filename")
flag.Annotations = annotation
out := new(bytes.Buffer)
c.GenBashCompletion(out)
str := out.String()
check(t, str, "_cobra-test")
check(t, str, "_cobra-test_echo")
check(t, str, "_cobra-test_echo_times")
check(t, str, "_cobra-test_print")
// check for required flags
check(t, str, `must_have_one_flag+=("--introot=")`)
// check for custom completion function
check(t, str, `COMPREPLY=( "hello" )`)
// check for required nouns
check(t, str, `must_have_one_noun+=("pods")`)
// check for filename extention flags
check(t, str, `flags_completion+=("_filedir '@(json|yaml|yml)'")`)
}

View File

@ -31,6 +31,14 @@ var initializers []func()
// Set this to true to enable it // Set this to true to enable it
var EnablePrefixMatching bool = false var EnablePrefixMatching bool = false
// enables an information splash screen on Windows if the CLI is started from explorer.exe.
var EnableWindowsMouseTrap bool = true
var MousetrapHelpText string = `This is a command line tool
You need to open cmd.exe and run it from there.
`
//OnInitialize takes a series of func() arguments and appends them to a slice of func(). //OnInitialize takes a series of func() arguments and appends them to a slice of func().
func OnInitialize(y ...func()) { func OnInitialize(y ...func()) {
for _, x := range y { for _, x := range y {

View File

@ -3,11 +3,14 @@ package cobra
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"os"
"runtime"
"strings" "strings"
"testing" "testing"
) )
var _ = fmt.Println var _ = fmt.Println
var _ = os.Stderr
var tp, te, tt, t1 []string var tp, te, tt, t1 []string
var flagb1, flagb2, flagb3, flagbr, flagbp bool var flagb1, flagb2, flagb3, flagbr, flagbp bool
@ -15,6 +18,7 @@ var flags1, flags2a, flags2b, flags3 string
var flagi1, flagi2, flagi3, flagir int var flagi1, flagi2, flagi3, flagir int
var globalFlag1 bool var globalFlag1 bool
var flagEcho, rootcalled bool var flagEcho, rootcalled bool
var versionUsed int
const strtwoParentHelp = "help message for parent flag strtwo" const strtwoParentHelp = "help message for parent flag strtwo"
const strtwoChildHelp = "help message for child flag strtwo" const strtwoChildHelp = "help message for child flag strtwo"
@ -22,7 +26,7 @@ const strtwoChildHelp = "help message for child flag strtwo"
var cmdPrint = &Command{ var cmdPrint = &Command{
Use: "print [string to print]", Use: "print [string to print]",
Short: "Print anything to the screen", Short: "Print anything to the screen",
Long: `an utterly useless command for testing.`, Long: `an absolutely utterly useless command for testing.`,
Run: func(cmd *Command, args []string) { Run: func(cmd *Command, args []string) {
tp = args tp = args
}, },
@ -33,15 +37,24 @@ var cmdEcho = &Command{
Aliases: []string{"say"}, Aliases: []string{"say"},
Short: "Echo anything to the screen", Short: "Echo anything to the screen",
Long: `an utterly useless command for testing.`, Long: `an utterly useless command for testing.`,
Example: "Just run cobra-test echo",
Run: func(cmd *Command, args []string) { Run: func(cmd *Command, args []string) {
te = args te = args
}, },
} }
var cmdEchoSub = &Command{
Use: "echosub [string to print]",
Short: "second sub command for echo",
Long: `an absolutely utterly useless command for testing gendocs!.`,
Run: func(cmd *Command, args []string) {
},
}
var cmdTimes = &Command{ var cmdTimes = &Command{
Use: "times [# times] [string to echo]", Use: "times [# times] [string to echo]",
Short: "Echo anything to the screen more times", Short: "Echo anything to the screen more times",
Long: `an slightly useless command for testing.`, Long: `a slightly useless command for testing.`,
Run: func(cmd *Command, args []string) { Run: func(cmd *Command, args []string) {
tt = args tt = args
}, },
@ -68,6 +81,30 @@ var cmdRootWithRun = &Command{
}, },
} }
var cmdSubNoRun = &Command{
Use: "subnorun",
Short: "A subcommand without a Run function",
Long: "A long output about a subcommand without a Run function",
}
var cmdVersion1 = &Command{
Use: "version",
Short: "Print the version number",
Long: `First version of the version command`,
Run: func(cmd *Command, args []string) {
versionUsed = 1
},
}
var cmdVersion2 = &Command{
Use: "version",
Short: "Print the version number",
Long: `Second version of the version command`,
Run: func(cmd *Command, args []string) {
versionUsed = 2
},
}
func flagInit() { func flagInit() {
cmdEcho.ResetFlags() cmdEcho.ResetFlags()
cmdPrint.ResetFlags() cmdPrint.ResetFlags()
@ -75,6 +112,7 @@ func flagInit() {
cmdRootNoRun.ResetFlags() cmdRootNoRun.ResetFlags()
cmdRootSameName.ResetFlags() cmdRootSameName.ResetFlags()
cmdRootWithRun.ResetFlags() cmdRootWithRun.ResetFlags()
cmdSubNoRun.ResetFlags()
cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp) cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp)
cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone") cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone")
cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo") cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo")
@ -86,6 +124,8 @@ func flagInit() {
cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone") cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone")
cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo") cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo")
cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree") cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree")
cmdVersion1.ResetFlags()
cmdVersion2.ResetFlags()
} }
func commandInit() { func commandInit() {
@ -95,6 +135,7 @@ func commandInit() {
cmdRootNoRun.ResetCommands() cmdRootNoRun.ResetCommands()
cmdRootSameName.ResetCommands() cmdRootSameName.ResetCommands()
cmdRootWithRun.ResetCommands() cmdRootWithRun.ResetCommands()
cmdSubNoRun.ResetCommands()
} }
func initialize() *Command { func initialize() *Command {
@ -164,7 +205,7 @@ func fullTester(c *Command, input string) resulter {
// Testing flag with invalid input // Testing flag with invalid input
c.SetOutput(buf) c.SetOutput(buf)
cmdEcho.AddCommand(cmdTimes) cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho) c.AddCommand(cmdPrint, cmdEcho, cmdSubNoRun)
c.SetArgs(strings.Split(input, " ")) c.SetArgs(strings.Split(input, " "))
err := c.Execute() err := c.Execute()
@ -173,15 +214,26 @@ func fullTester(c *Command, input string) resulter {
return resulter{err, output, c} return resulter{err, output, c}
} }
func logErr(t *testing.T, found, expected string) {
out := new(bytes.Buffer)
_, _, line, ok := runtime.Caller(2)
if ok {
fmt.Fprintf(out, "Line: %d ", line)
}
fmt.Fprintf(out, "Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
t.Errorf(out.String())
}
func checkResultContains(t *testing.T, x resulter, check string) { func checkResultContains(t *testing.T, x resulter, check string) {
if !strings.Contains(x.Output, check) { if !strings.Contains(x.Output, check) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", check, x.Output) logErr(t, x.Output, check)
} }
} }
func checkResultOmits(t *testing.T, x resulter, check string) { func checkResultOmits(t *testing.T, x resulter, check string) {
if strings.Contains(x.Output, check) { if strings.Contains(x.Output, check) {
t.Errorf("Unexpected response.\nExpecting to omit: \n %q\nGot:\n %q\n", check, x.Output) logErr(t, x.Output, check)
} }
} }
@ -191,7 +243,7 @@ func checkOutputContains(t *testing.T, c *Command, check string) {
c.Execute() c.Execute()
if !strings.Contains(buf.String(), check) { if !strings.Contains(buf.String(), check) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", check, buf.String()) logErr(t, buf.String(), check)
} }
} }
@ -417,6 +469,20 @@ func TestTrailingCommandFlags(t *testing.T) {
} }
} }
func TestInvalidSubCommandFlags(t *testing.T) {
cmd := initializeWithRootCmd()
cmd.AddCommand(cmdTimes)
result := simpleTester(cmd, "times --inttwo=2 --badflag=bar")
checkResultContains(t, result, "unknown flag: --badflag")
if strings.Contains(result.Output, "unknown flag: --inttwo") {
t.Errorf("invalid --badflag flag shouldn't fail on 'unknown' --inttwo flag")
}
}
func TestPersistentFlags(t *testing.T) { func TestPersistentFlags(t *testing.T) {
fullSetupTest("echo -s something -p more here") fullSetupTest("echo -s something -p more here")
@ -451,11 +517,14 @@ func TestPersistentFlags(t *testing.T) {
} }
func TestHelpCommand(t *testing.T) { func TestHelpCommand(t *testing.T) {
c := fullSetupTest("help echo") x := fullSetupTest("help")
checkResultContains(t, c, cmdEcho.Long) checkResultContains(t, x, cmdRootWithRun.Long)
r := fullSetupTest("help echo times") x = fullSetupTest("help echo")
checkResultContains(t, r, cmdTimes.Long) checkResultContains(t, x, cmdEcho.Long)
x = fullSetupTest("help echo times")
checkResultContains(t, x, cmdTimes.Long)
} }
func TestChildCommandHelp(t *testing.T) { func TestChildCommandHelp(t *testing.T) {
@ -465,6 +534,11 @@ func TestChildCommandHelp(t *testing.T) {
checkResultContains(t, r, strtwoChildHelp) checkResultContains(t, r, strtwoChildHelp)
} }
func TestNonRunChildHelp(t *testing.T) {
x := noRRSetupTest("subnorun")
checkResultContains(t, x, cmdSubNoRun.Long)
}
func TestRunnableRootCommand(t *testing.T) { func TestRunnableRootCommand(t *testing.T) {
fullSetupTest("") fullSetupTest("")
@ -473,6 +547,53 @@ func TestRunnableRootCommand(t *testing.T) {
} }
} }
func TestRunnableRootCommandNilInput(t *testing.T) {
empty_arg := make([]string, 0)
c := initializeWithRootCmd()
buf := new(bytes.Buffer)
// Testing flag with invalid input
c.SetOutput(buf)
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho)
c.SetArgs(empty_arg)
c.Execute()
if rootcalled != true {
t.Errorf("Root Function was not called")
}
}
func TestRunnableRootCommandEmptyInput(t *testing.T) {
args := make([]string, 3)
args[0] = ""
args[1] = "--introot=12"
args[2] = ""
c := initializeWithRootCmd()
buf := new(bytes.Buffer)
// Testing flag with invalid input
c.SetOutput(buf)
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho)
c.SetArgs(args)
c.Execute()
if rootcalled != true {
t.Errorf("Root Function was not called.\n\nOutput was:\n\n%s\n", buf)
}
}
func TestInvalidSubcommandWhenArgsAllowed(t *testing.T) {
fullSetupTest("echo invalid-sub")
if te[0] != "invalid-sub" {
t.Errorf("Subcommand didn't work...")
}
}
func TestRootFlags(t *testing.T) { func TestRootFlags(t *testing.T) {
fullSetupTest("-i 17 -b") fullSetupTest("-i 17 -b")
@ -534,6 +655,24 @@ func TestFlagAccess(t *testing.T) {
} }
} }
func TestNoNRunnableRootCommandNilInput(t *testing.T) {
args := make([]string, 0)
c := initialize()
buf := new(bytes.Buffer)
// Testing flag with invalid input
c.SetOutput(buf)
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho)
c.SetArgs(args)
c.Execute()
if !strings.Contains(buf.String(), cmdRootNoRun.Long) {
t.Errorf("Expected to get help output, Got: \n %s", buf)
}
}
func TestRootNoCommandHelp(t *testing.T) { func TestRootNoCommandHelp(t *testing.T) {
x := rootOnlySetupTest("--help") x := rootOnlySetupTest("--help")
@ -554,6 +693,15 @@ func TestRootNoCommandHelp(t *testing.T) {
} }
} }
func TestRootUnknownCommand(t *testing.T) {
r := noRRSetupTest("bogus")
s := "Error: unknown command \"bogus\"\nRun 'cobra-test help' for usage.\n"
if r.Output != s {
t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output)
}
}
func TestFlagsBeforeCommand(t *testing.T) { func TestFlagsBeforeCommand(t *testing.T) {
// short without space // short without space
x := fullSetupTest("-i10 echo") x := fullSetupTest("-i10 echo")
@ -607,3 +755,34 @@ func TestFlagsBeforeCommand(t *testing.T) {
} }
} }
func TestRemoveCommand(t *testing.T) {
versionUsed = 0
c := initializeWithRootCmd()
c.AddCommand(cmdVersion1)
c.RemoveCommand(cmdVersion1)
x := fullTester(c, "version")
if x.Error == nil {
t.Errorf("Removed command should not have been called\n")
return
}
}
func TestReplaceCommandWithRemove(t *testing.T) {
versionUsed = 0
c := initializeWithRootCmd()
c.AddCommand(cmdVersion1)
c.RemoveCommand(cmdVersion1)
c.AddCommand(cmdVersion2)
x := fullTester(c, "version")
if x.Error != nil {
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
return
}
if versionUsed == 1 {
t.Errorf("Removed command shouldn't be called\n")
}
if versionUsed != 2 {
t.Errorf("Replacing command should have been called but didn't\n")
}
}

View File

@ -18,11 +18,13 @@ package cobra
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/inconshreveable/mousetrap"
flag "github.com/spf13/pflag"
"io" "io"
"os" "os"
"runtime"
"strings" "strings"
"time"
flag "github.com/spf13/pflag"
) )
// Command is just that, a command for your application. // Command is just that, a command for your application.
@ -42,6 +44,10 @@ type Command struct {
Long string Long string
// Examples of how to use the command // Examples of how to use the command
Example string Example string
// List of all valid non-flag arguments, used for bash completions *TODO* actually validate these
ValidArgs []string
// Custom functions used by the bash autocompletion generator
BashCompletionFunction string
// Full set of flags // Full set of flags
flags *flag.FlagSet flags *flag.FlagSet
// Set of flags childrens of this command will inherit // Set of flags childrens of this command will inherit
@ -223,7 +229,7 @@ Aliases:
Examples: Examples:
{{ .Example }} {{ .Example }}
{{end}}{{ if .HasSubCommands}} {{end}}{{ if .HasRunnableSubCommands}}
Available Commands: {{range .Commands}}{{if .Runnable}} Available Commands: {{range .Commands}}{{if .Runnable}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}
@ -231,8 +237,9 @@ Available Commands: {{range .Commands}}{{if .Runnable}}
{{ if .HasLocalFlags}}Flags: {{ if .HasLocalFlags}}Flags:
{{.LocalFlags.FlagUsages}}{{end}} {{.LocalFlags.FlagUsages}}{{end}}
{{ if .HasInheritedFlags}}Global Flags: {{ if .HasInheritedFlags}}Global Flags:
{{.InheritedFlags.FlagUsages}}{{end}}{{if .HasParent}}{{if and (gt .Commands 0) (gt .Parent.Commands 1) }} {{.InheritedFlags.FlagUsages}}{{end}}{{if or (.HasHelpSubCommands) (.HasRunnableSiblings)}}
Additional help topics: {{if gt .Commands 0 }}{{range .Commands}}{{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if gt .Parent.Commands 1 }}{{range .Parent.Commands}}{{if .Runnable}}{{if not (eq .Name $cmd.Name) }}{{end}} Additional help topics:
{{if .HasHelpSubCommands}}{{range .Commands}}{{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasRunnableSiblings }}{{range .Parent.Commands}}{{if .Runnable}}{{if not (eq .Name $cmd.Name) }}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}}
{{end}}{{ if .HasSubCommands }} {{end}}{{ if .HasSubCommands }}
Use "{{.Root.Name}} help [command]" for more information about a command. Use "{{.Root.Name}} help [command]" for more information about a command.
@ -305,6 +312,8 @@ func stripFlags(args []string, c *Command) []string {
inFlag = true inFlag = true
case inFlag: case inFlag:
inFlag = false inFlag = false
case y == "":
// strip empty commands, as the go tests expect this to be ok....
case !strings.HasPrefix(y, "-"): case !strings.HasPrefix(y, "-"):
commands = append(commands, y) commands = append(commands, y)
inFlag = false inFlag = false
@ -375,10 +384,9 @@ func (c *Command) Find(arrs []string) (*Command, []string, error) {
commandFound, a := innerfind(c, arrs) commandFound, a := innerfind(c, arrs)
// if commander returned and the first argument (if it exists) doesn't // If we matched on the root, but we asked for a subcommand, return an error
// match the command name, return nil & error if commandFound.Name() == c.Name() && len(stripFlags(arrs, c)) > 0 && commandFound.Name() != arrs[0] {
if commandFound.Name() == c.Name() && len(arrs[0]) > 0 && commandFound.Name() != arrs[0] { return nil, a, fmt.Errorf("unknown command %q", a[0])
return nil, a, fmt.Errorf("unknown command %q\nRun 'help' for usage.\n", a[0])
} }
return commandFound, a, nil return commandFound, a, nil
@ -398,16 +406,6 @@ func (c *Command) Root() *Command {
return findRoot(c) return findRoot(c)
} }
// execute the command determined by args and the command tree
func (c *Command) findAndExecute(args []string) (err error) {
cmd, a, e := c.Find(args)
if e != nil {
return e
}
return cmd.execute(a)
}
func (c *Command) execute(a []string) (err error) { func (c *Command) execute(a []string) (err error) {
if c == nil { if c == nil {
return fmt.Errorf("Called Execute() on a nil Command") return fmt.Errorf("Called Execute() on a nil Command")
@ -474,6 +472,14 @@ func (c *Command) Execute() (err error) {
return c.Root().Execute() return c.Root().Execute()
} }
if EnableWindowsMouseTrap && runtime.GOOS == "windows" {
if mousetrap.StartedByExplorer() {
c.Print(MousetrapHelpText)
time.Sleep(5 * time.Second)
os.Exit(1)
}
}
// initialize help as the last point possible to allow for user // initialize help as the last point possible to allow for user
// overriding // overriding
c.initHelp() c.initHelp()
@ -494,55 +500,21 @@ func (c *Command) Execute() (err error) {
c.Help() c.Help()
} }
} else { } else {
err = c.findAndExecute(args) cmd, flags, e := c.Find(args)
}
// Now handle the case where the root is runnable and only flags are provided
if err != nil && c.Runnable() {
// This is pretty much a custom version of the *Command.execute method
// with a few differences because it's the final command (no fall back)
e := c.ParseFlags(args)
if e != nil { if e != nil {
// Flags parsing had an error.
// If an error happens here, we have to report it to the user
c.Println(e.Error())
// If an error happens search also for subcommand info about that
if c.cmdErrorBuf != nil && c.cmdErrorBuf.Len() > 0 {
c.Println(c.cmdErrorBuf.String())
} else {
c.Usage()
}
err = e err = e
return
} else { } else {
// If help is called, regardless of other flags, we print that err = cmd.execute(flags)
if c.helpFlagVal {
c.Help()
return nil
}
argWoFlags := c.Flags().Args()
if len(argWoFlags) > 0 {
// If there are arguments (not flags) one of the earlier
// cases should have caught it.. It means invalid usage
// print the usage
c.Usage()
} else {
// Only flags left... Call root.Run
c.preRun()
c.Run(c, argWoFlags)
err = nil
}
} }
} }
if err != nil { if err != nil {
if err == flag.ErrHelp { if err == flag.ErrHelp {
c.Help() c.Help()
} else { } else {
c.Println("Error:", err.Error()) c.Println("Error:", err.Error())
c.Printf("%v: invalid command %#q\n", c.Root().Name(), os.Args[1:]) c.Printf("Run '%v help' for usage.\n", c.Root().Name())
c.Printf("Run '%v help' for usage\n", c.Root().Name())
} }
} }
@ -787,6 +759,37 @@ func (c *Command) HasSubCommands() bool {
return len(c.commands) > 0 return len(c.commands) > 0
} }
func (c *Command) HasRunnableSiblings() bool {
if !c.HasParent() {
return false
}
for _, sub := range c.parent.commands {
if sub.Runnable() {
return true
}
}
return false
}
func (c *Command) HasHelpSubCommands() bool {
for _, sub := range c.commands {
if !sub.Runnable() {
return true
}
}
return false
}
// Determine if the command has runnable children commands
func (c *Command) HasRunnableSubCommands() bool {
for _, sub := range c.commands {
if sub.Runnable() {
return true
}
}
return false
}
// Determine if the command is a child command // Determine if the command is a child command
func (c *Command) HasParent() bool { func (c *Command) HasParent() bool {
return c.parent != nil return c.parent != nil

View File

@ -0,0 +1,90 @@
package cobra
import (
"reflect"
"testing"
)
func TestStripFlags(t *testing.T) {
tests := []struct {
input []string
output []string
}{
{
[]string{"foo", "bar"},
[]string{"foo", "bar"},
},
{
[]string{"foo", "--bar", "-b"},
[]string{"foo"},
},
{
[]string{"-b", "foo", "--bar", "bar"},
[]string{},
},
{
[]string{"-i10", "echo"},
[]string{"echo"},
},
{
[]string{"-i=10", "echo"},
[]string{"echo"},
},
{
[]string{"--int=100", "echo"},
[]string{"echo"},
},
{
[]string{"-ib", "echo", "-bfoo", "baz"},
[]string{"echo", "baz"},
},
{
[]string{"-i=baz", "bar", "-i", "foo", "blah"},
[]string{"bar", "blah"},
},
{
[]string{"--int=baz", "-bbar", "-i", "foo", "blah"},
[]string{"blah"},
},
{
[]string{"--cat", "bar", "-i", "foo", "blah"},
[]string{"bar", "blah"},
},
{
[]string{"-c", "bar", "-i", "foo", "blah"},
[]string{"bar", "blah"},
},
{
[]string{"--persist", "bar"},
[]string{"bar"},
},
{
[]string{"-p", "bar"},
[]string{"bar"},
},
}
cmdPrint := &Command{
Use: "print [string to print]",
Short: "Print anything to the screen",
Long: `an utterly useless command for testing.`,
Run: func(cmd *Command, args []string) {
tp = args
},
}
var flagi int
var flagstr string
var flagbool bool
cmdPrint.PersistentFlags().BoolVarP(&flagbool, "persist", "p", false, "help for persistent one")
cmdPrint.Flags().IntVarP(&flagi, "int", "i", 345, "help message for flag int")
cmdPrint.Flags().StringVarP(&flagstr, "bar", "b", "bar", "help message for flag string")
cmdPrint.Flags().BoolVarP(&flagbool, "cat", "c", false, "help message for flag bool")
for _, test := range tests {
output := stripFlags(test.input, cmdPrint)
if !reflect.DeepEqual(test.output, output) {
t.Errorf("expected: %v, got: %v", test.output, output)
}
}
}

121
Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go generated vendored Normal file
View File

@ -0,0 +1,121 @@
//Copyright 2015 Red Hat Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cobra
import (
"bytes"
"fmt"
"os"
"sort"
"strings"
"time"
)
func printOptions(out *bytes.Buffer, cmd *Command, name string) {
flags := cmd.NonInheritedFlags()
flags.SetOutput(out)
if flags.HasFlags() {
fmt.Fprintf(out, "### Options\n\n```\n")
flags.PrintDefaults()
fmt.Fprintf(out, "```\n\n")
}
parentFlags := cmd.InheritedFlags()
parentFlags.SetOutput(out)
if parentFlags.HasFlags() {
fmt.Fprintf(out, "### Options inherrited from parent commands\n\n```\n")
parentFlags.PrintDefaults()
fmt.Fprintf(out, "```\n\n")
}
}
type byName []*Command
func (s byName) Len() int { return len(s) }
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
func GenMarkdown(cmd *Command, out *bytes.Buffer) {
name := cmd.CommandPath()
short := cmd.Short
long := cmd.Long
if len(long) == 0 {
long = short
}
fmt.Fprintf(out, "## %s\n\n", name)
fmt.Fprintf(out, "%s\n\n", short)
fmt.Fprintf(out, "### Synopsis\n\n")
fmt.Fprintf(out, "\n%s\n\n", long)
if cmd.Runnable() {
fmt.Fprintf(out, "```\n%s\n```\n\n", cmd.UseLine())
}
if len(cmd.Example) > 0 {
fmt.Fprintf(out, "### Examples\n\n")
fmt.Fprintf(out, "```\n%s\n```\n\n", cmd.Example)
}
printOptions(out, cmd, name)
if len(cmd.Commands()) > 0 || cmd.HasParent() {
fmt.Fprintf(out, "### SEE ALSO\n")
if cmd.HasParent() {
parent := cmd.Parent()
pname := parent.CommandPath()
link := pname + ".md"
link = strings.Replace(link, " ", "_", -1)
fmt.Fprintf(out, "* [%s](%s)\t - %s\n", pname, link, parent.Short)
}
children := cmd.Commands()
sort.Sort(byName(children))
for _, child := range children {
cname := name + " " + child.Name()
link := cname + ".md"
link = strings.Replace(link, " ", "_", -1)
fmt.Fprintf(out, "* [%s](%s)\t - %s\n", cname, link, child.Short)
}
fmt.Fprintf(out, "\n")
}
fmt.Fprintf(out, "###### Auto generated by spf13/cobra at %s\n", time.Now().UTC())
}
func GenMarkdownTree(cmd *Command, dir string) {
for _, c := range cmd.Commands() {
GenMarkdownTree(c, dir)
}
out := new(bytes.Buffer)
GenMarkdown(cmd, out)
filename := cmd.CommandPath()
filename = dir + strings.Replace(filename, " ", "_", -1) + ".md"
outFile, err := os.Create(filename)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer outFile.Close()
_, err = outFile.Write(out.Bytes())
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -0,0 +1,35 @@
# Generating Markdown Docs For Your Own cobra.Command
## Generate markdown docs for the entire command tree
This program can actually generate docs for the kubectl command in the kubernetes project
```go
package main
import (
"io/ioutil"
"os"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
"github.com/spf13/cobra"
)
func main() {
kubectl := cmd.NewFactory(nil).NewKubectlCommand(os.Stdin, ioutil.Discard, ioutil.Discard)
cobra.GenMarkdownTree(kubectl, "./")
}
```
This will generate a whole series of files, one for each command in the tree, in the directory specified (in this case "./")
## Generate markdown docs for a single command
You may wish to have more control over the output, or only generate for a single command, instead of the entire command tree. If this is the case you may prefer to `GenMarkdown()` instead of `GenMarkdownTree`
```go
out := new(bytes.Buffer)
cobra.GenMarkdown(cmd, out)
```
This will write the markdown doc for ONLY "cmd" into the out, buffer.

View File

@ -0,0 +1,64 @@
package cobra
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
)
var _ = fmt.Println
var _ = os.Stderr
func TestGenMdDoc(t *testing.T) {
c := initializeWithRootCmd()
// Need two commands to run the command alphabetical sort
cmdEcho.AddCommand(cmdTimes, cmdEchoSub)
c.AddCommand(cmdPrint, cmdEcho)
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
out := new(bytes.Buffer)
// We generate on s subcommand so we have both subcommands and parents
GenMarkdown(cmdEcho, out)
found := out.String()
// Our description
expected := cmdEcho.Long
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// Better have our example
expected = cmdEcho.Example
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// A local flag
expected = "boolone"
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// persistent flag on parent
expected = "rootflag"
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// We better output info about our parent
expected = cmdRootWithRun.Short
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// And about subcommands
expected = cmdEchoSub.Short
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
fmt.Fprintf(os.Stdout, "%s\n", found)
}