From 163db8124e96df66fddbd66fe51af8576b52c2dd Mon Sep 17 00:00:00 2001 From: Sergiy Kulanov Date: Fri, 13 Oct 2023 18:48:10 +0300 Subject: [PATCH] feat: Add completion subcommand Signed-off-by: Sergiy Kulanov --- pkg/cmd/completion/completion.go | 103 ++++++++++++++++++++++++++ pkg/cmd/completion/completion_test.go | 25 +++++++ pkg/cmd/root.go | 2 + pkg/test/cobra.go | 41 ++++++++++ 4 files changed, 171 insertions(+) create mode 100644 pkg/cmd/completion/completion.go create mode 100644 pkg/cmd/completion/completion_test.go create mode 100644 pkg/test/cobra.go diff --git a/pkg/cmd/completion/completion.go b/pkg/cmd/completion/completion.go new file mode 100644 index 0000000..ff469a8 --- /dev/null +++ b/pkg/cmd/completion/completion.go @@ -0,0 +1,103 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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 completion + +import ( + "bytes" + "fmt" + + "github.com/spf13/cobra" +) + +const ( + desc = `This command prints shell completion code which must be evaluated to provide +interactive completion + +Supported Shells: + - bash + - zsh + - fish + - powershell +` + eg = `To load completions: + +Bash: + +$ source <(tkn-graph completion bash) + +# To load completions for each session, execute once: +Linux: + $ tkn-graph completion bash > /etc/bash_completion.d/tkn + +MacOS: + $ tkn-graph completion bash > /usr/local/etc/bash_completion.d/tkn + +Zsh: + +$ source <(tkn-graph completion zsh) + +# To load completions for every sessions, you can execute the following once: + +$ echo "autoload -U compinit; compinit" >> ~/.zshrc + +# and add the completion to your fpath (may differ from the first one in the fpath array) +$ tkn-graph completion zsh > "${fpath[1]}/_tkn" + +# You will need to start a new shell for this setup to take effect. + +Fish: + +$ tkn-graph completion fish | source + +# To load completions for each session, execute once: +$ tkn-graph completion fish > ~/.config/fish/completions/tkn.fish +` +) + +func genZshCompletion(cmd *cobra.Command) string { + var output bytes.Buffer + _ = cmd.Root().GenZshCompletion(&output) + return fmt.Sprintf("#compdef %s\ncompdef _%s %s\n%s", cmd.Root().Use, + cmd.Root().Use, cmd.Root().Use, output.String()) +} + +func Command() *cobra.Command { + cmd := &cobra.Command{ + Use: "completion [SHELL]", + Short: "Prints shell completion scripts", + Long: desc, + ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, + Example: eg, + Annotations: map[string]string{ + "commandType": "utility", + }, + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), + RunE: func(cmd *cobra.Command, args []string) error { + switch args[0] { + case "bash": + _ = cmd.Root().GenBashCompletion(cmd.OutOrStdout()) + case "zsh": + fmt.Fprint(cmd.OutOrStdout(), genZshCompletion(cmd)) + case "fish": + _ = cmd.Root().GenFishCompletion(cmd.OutOrStdout(), true) + case "powershell": + _ = cmd.Root().GenPowerShellCompletion(cmd.OutOrStdout()) + } + + return nil + }, + } + return cmd +} diff --git a/pkg/cmd/completion/completion_test.go b/pkg/cmd/completion/completion_test.go new file mode 100644 index 0000000..e5dd160 --- /dev/null +++ b/pkg/cmd/completion/completion_test.go @@ -0,0 +1,25 @@ +package completion + +import ( + "strings" + "testing" + + "github.com/sergk/tkn-graph/pkg/test" + "github.com/stretchr/testify/assert" +) + +func TestCompletion_Empty(t *testing.T) { + completion := Command() + out, err := test.ExecuteCommand(completion) + if err == nil { + t.Errorf("No errors was defined. Output: %s", out) + } + expected := "accepts 1 arg(s), received 0" + assert.Contains(t, out, expected) +} + +func TestCompletionZSH(t *testing.T) { + cmd := Command() + output := genZshCompletion(cmd) + assert.True(t, strings.HasPrefix(output, "#compdef")) +} diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 416821f..1e48c5c 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "github.com/sergk/tkn-graph/pkg/cmd/completion" "github.com/sergk/tkn-graph/pkg/cmd/pipeline" "github.com/sergk/tkn-graph/pkg/cmd/pipelinerun" "github.com/sergk/tkn-graph/pkg/cmd/version" @@ -49,6 +50,7 @@ func Root(p cli.Params) *cobra.Command { pipeline.Command(p), pipelinerun.Command(p), version.Command(), + completion.Command(), ) return cmd diff --git a/pkg/test/cobra.go b/pkg/test/cobra.go new file mode 100644 index 0000000..cdcb593 --- /dev/null +++ b/pkg/test/cobra.go @@ -0,0 +1,41 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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 test + +import ( + "bytes" + + "github.com/spf13/cobra" +) + +// ExecuteCommand executes the root command passing the args and returns +// the output as a string and error +func ExecuteCommand(root *cobra.Command, args ...string) (string, error) { + _, output, err := ExecuteCommandC(root, args...) + return output, err +} + +// ExecuteCommandC executes the root command passing the args and returns +// the root command, output as a string and error if any +func ExecuteCommandC(c *cobra.Command, args ...string) (*cobra.Command, string, error) { + buf := new(bytes.Buffer) + c.SetOutput(buf) + c.SetArgs(args) + c.SilenceUsage = true + + root, err := c.ExecuteC() + + return root, buf.String(), err +}