bash_completions.md

  1# Generating Bash Completions For Your Own cobra.Command
  2
  3If you are using the generator you can create a completion command by running
  4
  5```bash
  6cobra add completion
  7```
  8
  9Update the help text show how to install the bash_completion Linux show here [Kubectl docs show mac options](https://kubernetes.io/docs/tasks/tools/install-kubectl/#enabling-shell-autocompletion)
 10
 11Writing the shell script to stdout allows the most flexible use.
 12
 13```go
 14// completionCmd represents the completion command
 15var completionCmd = &cobra.Command{
 16	Use:   "completion",
 17	Short: "Generates bash completion scripts",
 18	Long: `To load completion run
 19
 20. <(bitbucket completion)
 21
 22To configure your bash shell to load completions for each session add to your bashrc
 23
 24# ~/.bashrc or ~/.profile
 25. <(bitbucket completion)
 26`,
 27	Run: func(cmd *cobra.Command, args []string) {
 28		rootCmd.GenBashCompletion(os.Stdout);
 29	},
 30}
 31```
 32
 33**Note:** The cobra generator may include messages printed to stdout for example if the config file is loaded, this will break the auto complete script
 34
 35
 36## Example from kubectl
 37
 38Generating bash completions from a cobra command is incredibly easy. An actual program which does so for the kubernetes kubectl binary is as follows:
 39
 40```go
 41package main
 42
 43import (
 44	"io/ioutil"
 45	"os"
 46
 47	"k8s.io/kubernetes/pkg/kubectl/cmd"
 48	"k8s.io/kubernetes/pkg/kubectl/cmd/util"
 49)
 50
 51func main() {
 52	kubectl := cmd.NewKubectlCommand(util.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
 53	kubectl.GenBashCompletionFile("out.sh")
 54}
 55```
 56
 57`out.sh` will get you completions of subcommands and flags. Copy it to `/etc/bash_completion.d/` as described [here](https://debian-administration.org/article/316/An_introduction_to_bash_completion_part_1) and reset your terminal to use autocompletion. If you make additional annotations to your code, you can get even more intelligent and flexible behavior.
 58
 59## Creating your own custom functions
 60
 61Some more actual code that works in kubernetes:
 62
 63```bash
 64const (
 65        bash_completion_func = `__kubectl_parse_get()
 66{
 67    local kubectl_output out
 68    if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then
 69        out=($(echo "${kubectl_output}" | awk '{print $1}'))
 70        COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
 71    fi
 72}
 73
 74__kubectl_get_resource()
 75{
 76    if [[ ${#nouns[@]} -eq 0 ]]; then
 77        return 1
 78    fi
 79    __kubectl_parse_get ${nouns[${#nouns[@]} -1]}
 80    if [[ $? -eq 0 ]]; then
 81        return 0
 82    fi
 83}
 84
 85__kubectl_custom_func() {
 86    case ${last_command} in
 87        kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop)
 88            __kubectl_get_resource
 89            return
 90            ;;
 91        *)
 92            ;;
 93    esac
 94}
 95`)
 96```
 97
 98And then I set that in my command definition:
 99
100```go
101cmds := &cobra.Command{
102	Use:   "kubectl",
103	Short: "kubectl controls the Kubernetes cluster manager",
104	Long: `kubectl controls the Kubernetes cluster manager.
105
106Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
107	Run: runHelp,
108	BashCompletionFunction: bash_completion_func,
109}
110```
111
112The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__kubectl_custom_func()` (`__<command-use>_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 `__kubectl_customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__kubectl_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!
113
114## Have the completions code complete your 'nouns'
115
116In 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:
117
118```go
119validArgs []string = { "pod", "node", "service", "replicationcontroller" }
120
121cmd := &cobra.Command{
122	Use:     "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
123	Short:   "Display one or many resources",
124	Long:    get_long,
125	Example: get_example,
126	Run: func(cmd *cobra.Command, args []string) {
127		err := RunGet(f, out, cmd, args)
128		util.CheckErr(err)
129	},
130	ValidArgs: validArgs,
131}
132```
133
134Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like
135
136```bash
137# kubectl get [tab][tab]
138node                 pod                    replicationcontroller  service
139```
140
141## Plural form and shortcuts for nouns
142
143If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`:
144
145```go
146argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
147
148cmd := &cobra.Command{
149    ...
150	ValidArgs:  validArgs,
151	ArgAliases: argAliases
152}
153```
154
155The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
156the completion algorithm if entered manually, e.g. in:
157
158```bash
159# kubectl get rc [tab][tab]
160backend        frontend       database 
161```
162
163Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns
164in this example again instead of the replication controllers.
165
166## Mark flags as required
167
168Most 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.
169
170```go
171cmd.MarkFlagRequired("pod")
172cmd.MarkFlagRequired("container")
173```
174
175and you'll get something like
176
177```bash
178# kubectl exec [tab][tab][tab]
179-c            --container=  -p            --pod=  
180```
181
182# Specify valid filename extensions for flags that take a filename
183
184In 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.
185
186```go
187	annotations := []string{"json", "yaml", "yml"}
188	annotation := make(map[string][]string)
189	annotation[cobra.BashCompFilenameExt] = annotations
190
191	flag := &pflag.Flag{
192		Name:        "filename",
193		Shorthand:   "f",
194		Usage:       usage,
195		Value:       value,
196		DefValue:    value.String(),
197		Annotations: annotation,
198	}
199	cmd.Flags().AddFlag(flag)
200```
201
202Now when you run a command with this filename flag you'll get something like
203
204```bash
205# kubectl create -f 
206test/                         example/                      rpmbuild/
207hello.yml                     test.json
208```
209
210So while there are many other files in the CWD it only shows me subdirs and those with valid extensions.
211
212# Specify custom flag completion
213
214Similar to the filename completion and filtering using cobra.BashCompFilenameExt, you can specify
215a custom flag completion function with cobra.BashCompCustom:
216
217```go
218	annotation := make(map[string][]string)
219	annotation[cobra.BashCompCustom] = []string{"__kubectl_get_namespaces"}
220
221	flag := &pflag.Flag{
222		Name:        "namespace",
223		Usage:       usage,
224		Annotations: annotation,
225	}
226	cmd.Flags().AddFlag(flag)
227```
228
229In addition add the `__handle_namespace_flag` implementation in the `BashCompletionFunction`
230value, e.g.:
231
232```bash
233__kubectl_get_namespaces()
234{
235    local template
236    template="{{ range .items  }}{{ .metadata.name }} {{ end }}"
237    local kubectl_out
238    if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then
239        COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) )
240    fi
241}
242```
243# Using bash aliases for commands
244
245You can also configure the `bash aliases` for the commands and they will also support completions.
246
247```bash
248alias aliasname=origcommand
249complete -o default -F __start_origcommand aliasname
250
251# and now when you run `aliasname` completion will make
252# suggestions as it did for `origcommand`.
253
254$) aliasname <tab><tab>
255completion     firstcommand   secondcommand
256```