Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor shell completion #229

Merged
merged 3 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 0 additions & 68 deletions pkg/cmd/completion/bash_completion.go

This file was deleted.

75 changes: 71 additions & 4 deletions pkg/cmd/completion/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ import (
"github.com/spf13/cobra"
)

var (
// ShellCompletionMap define a map between a flag to a custom shell
// completion which should be in commonCompletion variable
ShellCompletionMap = map[string]string{
"namespace": "__kubectl_get_namespace",
"kubeconfig": "_filedir",
}
)

const (
desc = `
This command prints shell completion code which must be evaluated to provide
Expand All @@ -36,6 +45,48 @@ Supported Shells:

# generate completion code for zsh
source <(tkn completion zsh)
`

commonCompletion = `
# Custom function for Completions
function __tkn_get_object() {
local type=$1
local util=$2
local template begin tkn_out
template="{{ range .items }}{{ .metadata.name }} {{ end }}"

if [[ ${util} == "kubectl" ]];then
tkn_out=($(kubectl get ${type} -o template --template="${template}" 2>/dev/null))
elif [[ ${util} == "tkn" ]];then
tkn_out=($(tkn ${type} ls -o template --template="${template}" 2>/dev/null))
fi

if [[ -n ${tkn_out} ]]; then
[[ -n ${BASH_VERSION} ]] && COMPREPLY=( $( compgen -W "${tkn_out}" -- "$cur" ) )
[[ -n ${ZSH_VERSION} ]] && compadd ${tkn_out}
fi
}

function __kubectl_get_namespace() { __tkn_get_object namespace kubectl ;}
function __kubectl_get_serviceaccount() { __tkn_get_object serviceaccount kubectl ;}
function __tkn_get_pipeline() { __tkn_get_object pipeline tkn ;}
function __tkn_get_pipelinerun() { __tkn_get_object pipelinerun tkn ;}
function __tkn_get_taskrun() { __tkn_get_object taskrun tkn ;}
function __tkn_get_pipelineresource() { __tkn_get_object pipelineresource tkn ;}
`
bashCompletion = `
function __custom_func() {
case ${last_command} in
*_start|*_describe|*_logs)
obj=${last_command/tkn_/};
obj=${obj/_describe/}; obj=${obj/_logs/};obj=${obj/_start/};
__tkn_get_object ${obj} tkn
return
;;
*)
;;
esac
}
`
)

Expand All @@ -53,7 +104,7 @@ func Command() *cobra.Command {
if len(args) == 1 {
switch args[0] {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
return runCompletionBash(os.Stdout, cmd.Parent())
case "zsh":
return runCompletionZsh(os.Stdout, cmd.Parent())
}
Expand All @@ -64,14 +115,30 @@ func Command() *cobra.Command {
return cmd
}

// runCompletionZsh generate completion manually, we are not using cobra
// completion since it's not flexible enough for us.
// runCompletionBash generate completion with custom functions for bash
func runCompletionBash(out io.Writer, tkn *cobra.Command) error {
if err := tkn.Root().GenBashCompletion(out); err != nil {
return err
}

if _, err := out.Write([]byte(commonCompletion)); err != nil {
return err
}

if _, err := out.Write([]byte(bashCompletion)); err != nil {
return err
}

return nil
}

// runCompletionZsh generate completion with custom functions for bash
func runCompletionZsh(out io.Writer, tkn *cobra.Command) error {
if err := tkn.Root().GenZshCompletion(out); err != nil {
return err
}

if _, err := out.Write([]byte(zshCompletion)); err != nil {
if _, err := out.Write([]byte(commonCompletion)); err != nil {
return err
}
return nil
Expand Down
20 changes: 0 additions & 20 deletions pkg/cmd/completion/zsh_completion.go

This file was deleted.

3 changes: 3 additions & 0 deletions pkg/cmd/pipeline/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ tkn pipeline start foo --param NAME=VALUE --resource source=scaffold-git -s Ser
c.Flags().StringSliceVarP(&res, "resource", "r", []string{}, "pass the resource name and ref")
c.Flags().StringSliceVarP(&params, "param", "p", []string{}, "pass the param")
c.Flags().StringVarP(&svc, "serviceaccount", "s", svc, "pass the serviceaccount name")
flags.AddShellCompletion(c.Flags().Lookup("serviceaccount"), "__kubectl_get_serviceaccount")
c.Flags().StringSliceVar(&svcs, "task-serviceaccount", []string{}, "pass the service account corresponding to the task")
flags.AddShellCompletion(c.Flags().Lookup("task-serviceaccount"), "__kubectl_get_serviceaccount")

c.Flags().BoolVarP(&last, "last", "l", false, "re-run the pipeline using last pipelinerun values")

_ = c.MarkZshCompPositionalArgumentCustom(1, "__tkn_get_pipeline")
Expand Down
7 changes: 3 additions & 4 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,9 @@ func Root(p cli.Params) *cobra.Command {
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)

var cmd = &cobra.Command{
Use: "tkn",
Short: "CLI for tekton pipelines",
Long: ``,
BashCompletionFunction: completion.BashCompletionFunc,
Use: "tkn",
Short: "CLI for tekton pipelines",
Long: ``,
}
cmd.SetUsageTemplate(usageTemplate)

Expand Down
21 changes: 11 additions & 10 deletions pkg/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package flags

import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/tektoncd/cli/pkg/cli"
"github.com/tektoncd/cli/pkg/cmd/completion"
)
Expand All @@ -37,17 +38,9 @@ func AddTektonOptions(cmd *cobra.Command) {

// Add custom completion for that command as specified in
// bashCompletionFlags map
for name, completion := range completion.BashCompletionFlags {
for name, completion := range completion.ShellCompletionMap {
pflag := cmd.PersistentFlags().Lookup(name)
if pflag != nil {
if pflag.Annotations == nil {
pflag.Annotations = map[string][]string{}
}
pflag.Annotations[cobra.BashCompCustom] = append(
pflag.Annotations[cobra.BashCompCustom],
completion,
)
}
AddShellCompletion(pflag, completion)
}
}

Expand Down Expand Up @@ -78,3 +71,11 @@ func InitParams(p cli.Params, cmd *cobra.Command) error {

return nil
}

// AddShellCompletion add a hint to the cobra flag annotation for how to do a completion
func AddShellCompletion(pflag *pflag.Flag, shellfunction string) {
if pflag.Annotations == nil {
pflag.Annotations = map[string][]string{}
}
pflag.Annotations[cobra.BashCompCustom] = append(pflag.Annotations[cobra.BashCompCustom], shellfunction)
}
26 changes: 26 additions & 0 deletions pkg/flags/flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package flags

import (
"testing"

"github.com/spf13/cobra"
)

func TestFlags_add_shell_completion(t *testing.T) {
newflag := "newflag"
shellfunc := "__test_function"
cmd := cobra.Command{}
cmd.PersistentFlags().String(newflag, "", "Completion pinpon pinpon 🎤")

pflag := cmd.PersistentFlags().Lookup(newflag)
AddShellCompletion(pflag, shellfunc)

if pflag.Annotations[cobra.BashCompCustom] == nil {
t.Errorf("annotation should be have been added to the flag")
}

if pflag.Annotations[cobra.BashCompCustom][0] != shellfunc {
t.Errorf("annotation should have been added to the flag")
}

}