From d6708667da5c79ab4a093ea7473e96b93be5f1f7 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 8 Aug 2020 03:43:34 -0400 Subject: [PATCH] feat(comp): Disable file completion when not valid With Cobra 1.0, it is now possible to control when file completion should or should not be done. For example: helm list should not trigger file completion since 'helm list' does not accept any arguments. This commit disables file completion when appropriate and adds tests to verify that file completion is properly disabled. Signed-off-by: Marc Khouzam --- cmd/helm/completion.go | 16 ++++++--- cmd/helm/completion_test.go | 58 ++++++++++++++++++++++++++++++++ cmd/helm/create.go | 9 +++++ cmd/helm/create_test.go | 5 +++ cmd/helm/dependency.go | 11 +++--- cmd/helm/dependency_test.go | 4 +++ cmd/helm/docs.go | 11 +++--- cmd/helm/docs_test.go | 25 ++++++++++++++ cmd/helm/env.go | 9 ++--- cmd/helm/env_test.go | 25 ++++++++++++++ cmd/helm/get.go | 9 ++--- cmd/helm/get_all_test.go | 5 +++ cmd/helm/get_hooks_test.go | 5 +++ cmd/helm/get_manifest_test.go | 5 +++ cmd/helm/get_notes_test.go | 5 +++ cmd/helm/get_test.go | 25 ++++++++++++++ cmd/helm/get_values_test.go | 5 +++ cmd/helm/history_test.go | 5 +++ cmd/helm/install_test.go | 7 ++++ cmd/helm/lint_test.go | 5 +++ cmd/helm/list.go | 11 +++--- cmd/helm/list_test.go | 4 +++ cmd/helm/package_test.go | 5 +++ cmd/helm/plugin.go | 7 ++-- cmd/helm/plugin_install.go | 8 +++++ cmd/helm/plugin_list.go | 7 ++-- cmd/helm/plugin_test.go | 23 +++++++++++++ cmd/helm/pull_test.go | 5 +++ cmd/helm/release_testing_test.go | 26 ++++++++++++++ cmd/helm/repo.go | 9 ++--- cmd/helm/repo_add.go | 7 ++-- cmd/helm/repo_add_test.go | 6 ++++ cmd/helm/repo_index.go | 8 +++++ cmd/helm/repo_index_test.go | 5 +++ cmd/helm/repo_list.go | 9 ++--- cmd/helm/repo_list_test.go | 4 +++ cmd/helm/repo_remove_test.go | 5 +++ cmd/helm/repo_test.go | 25 ++++++++++++++ cmd/helm/repo_update.go | 11 +++--- cmd/helm/repo_update_test.go | 4 +++ cmd/helm/rollback_test.go | 6 ++++ cmd/helm/root.go | 3 ++ cmd/helm/root_test.go | 8 +++++ cmd/helm/search.go | 7 ++-- cmd/helm/search_hub_test.go | 4 +++ cmd/helm/search_repo.go | 5 +++ cmd/helm/search_repo_test.go | 4 +++ cmd/helm/search_test.go | 23 +++++++++++++ cmd/helm/show.go | 11 +++--- cmd/helm/show_test.go | 20 +++++++++++ cmd/helm/status_test.go | 5 +++ cmd/helm/template_test.go | 7 ++++ cmd/helm/uninstall_test.go | 5 +++ cmd/helm/upgrade_test.go | 6 ++++ cmd/helm/verify.go | 8 +++++ cmd/helm/verify_test.go | 5 +++ cmd/helm/version.go | 9 ++--- cmd/helm/version_test.go | 4 +++ 58 files changed, 517 insertions(+), 61 deletions(-) create mode 100644 cmd/helm/completion_test.go create mode 100644 cmd/helm/docs_test.go create mode 100644 cmd/helm/env_test.go create mode 100644 cmd/helm/get_test.go create mode 100644 cmd/helm/release_testing_test.go create mode 100644 cmd/helm/repo_test.go create mode 100644 cmd/helm/search_test.go diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 696021363..c7e763c3d 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -54,10 +54,11 @@ $ helm completion zsh > "${fpath[1]}/_helm" func newCompletionCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "completion", - Short: "generate autocompletions script for the specified shell", - Long: completionDesc, - Args: require.NoArgs, + Use: "completion", + Short: "generate autocompletions script for the specified shell", + Long: completionDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } bash := &cobra.Command{ @@ -66,6 +67,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command { Long: bashCompDesc, Args: require.NoArgs, DisableFlagsInUseLine: true, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { return runCompletionBash(out, cmd) }, @@ -77,6 +79,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command { Long: zshCompDesc, Args: require.NoArgs, DisableFlagsInUseLine: true, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { return runCompletionZsh(out, cmd) }, @@ -253,3 +256,8 @@ __helm_bash_source <(__helm_convert_bash_to_zsh) out.Write([]byte(zshTail)) return nil } + +// Function to disable file completion +func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/helm/completion_test.go b/cmd/helm/completion_test.go new file mode 100644 index 000000000..44c74f85a --- /dev/null +++ b/cmd/helm/completion_test.go @@ -0,0 +1,58 @@ +/* +Copyright The Helm 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 main + +import ( + "fmt" + "strings" + "testing" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/release" +) + +// Check if file completion should be performed according to parameter 'shouldBePerformed' +func checkFileCompletion(t *testing.T, cmdName string, shouldBePerformed bool) { + storage := storageFixture() + storage.Create(&release.Release{ + Name: "myrelease", + Info: &release.Info{Status: release.StatusDeployed}, + Chart: &chart.Chart{}, + Version: 1, + }) + + testcmd := fmt.Sprintf("__complete %s ''", cmdName) + _, out, err := executeActionCommandC(storage, testcmd) + if err != nil { + t.Errorf("unexpected error, %s", err) + } + if !strings.Contains(out, "ShellCompDirectiveNoFileComp") != shouldBePerformed { + if shouldBePerformed { + t.Error(fmt.Sprintf("Unexpected directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)) + } else { + + t.Error(fmt.Sprintf("Did not receive directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)) + } + t.Log(out) + } +} + +func TestCompletionFileCompletion(t *testing.T) { + checkFileCompletion(t, "completion", false) + checkFileCompletion(t, "completion bash", false) + checkFileCompletion(t, "completion zsh", false) +} diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 5bdda05cb..21a7e026c 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -64,6 +64,15 @@ func newCreateCmd(out io.Writer) *cobra.Command { Short: "create a new chart with the given name", Long: createDesc, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // Allow file completion when completing the argument for the name + // which could be a path + return nil, cobra.ShellCompDirectiveDefault + } + // No more completions, so disable file completion + return nil, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { o.name = args[0] o.starterDir = helmpath.DataPath("starters") diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index bbb8d394a..1db6bed52 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -192,3 +192,8 @@ func TestCreateStarterAbsoluteCmd(t *testing.T) { t.Error("Did not find foo.tpl") } } + +func TestCreateFileCompletion(t *testing.T) { + checkFileCompletion(t, "create", true) + checkFileCompletion(t, "create myname", false) +} diff --git a/cmd/helm/dependency.go b/cmd/helm/dependency.go index 2cc4c5045..39dfd98ce 100644 --- a/cmd/helm/dependency.go +++ b/cmd/helm/dependency.go @@ -84,11 +84,12 @@ This will produce an error if the chart cannot be loaded. func newDependencyCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "dependency update|build|list", - Aliases: []string{"dep", "dependencies"}, - Short: "manage a chart's dependencies", - Long: dependencyDesc, - Args: require.NoArgs, + Use: "dependency update|build|list", + Aliases: []string{"dep", "dependencies"}, + Short: "manage a chart's dependencies", + Long: dependencyDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newDependencyListCmd(out)) diff --git a/cmd/helm/dependency_test.go b/cmd/helm/dependency_test.go index 80b357a77..34c6a25e1 100644 --- a/cmd/helm/dependency_test.go +++ b/cmd/helm/dependency_test.go @@ -51,3 +51,7 @@ func TestDependencyListCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestDependencyFileCompletion(t *testing.T) { + checkFileCompletion(t, "dependency", false) +} diff --git a/cmd/helm/docs.go b/cmd/helm/docs.go index c974d4014..621b17ca1 100644 --- a/cmd/helm/docs.go +++ b/cmd/helm/docs.go @@ -47,11 +47,12 @@ func newDocsCmd(out io.Writer) *cobra.Command { o := &docsOptions{} cmd := &cobra.Command{ - Use: "docs", - Short: "generate documentation as markdown or man pages", - Long: docsDesc, - Hidden: true, - Args: require.NoArgs, + Use: "docs", + Short: "generate documentation as markdown or man pages", + Long: docsDesc, + Hidden: true, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { o.topCmd = cmd.Root() return o.run(out) diff --git a/cmd/helm/docs_test.go b/cmd/helm/docs_test.go new file mode 100644 index 000000000..cda299b0a --- /dev/null +++ b/cmd/helm/docs_test.go @@ -0,0 +1,25 @@ +/* +Copyright The Helm 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 main + +import ( + "testing" +) + +func TestDocsFileCompletion(t *testing.T) { + checkFileCompletion(t, "docs", false) +} diff --git a/cmd/helm/env.go b/cmd/helm/env.go index 0fbfb9da4..4db3d80de 100644 --- a/cmd/helm/env.go +++ b/cmd/helm/env.go @@ -32,10 +32,11 @@ Env prints out all the environment information in use by Helm. func newEnvCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "env", - Short: "helm client environment information", - Long: envHelp, - Args: require.NoArgs, + Use: "env", + Short: "helm client environment information", + Long: envHelp, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, Run: func(cmd *cobra.Command, args []string) { envVars := settings.EnvVars() diff --git a/cmd/helm/env_test.go b/cmd/helm/env_test.go new file mode 100644 index 000000000..a7170a8cc --- /dev/null +++ b/cmd/helm/env_test.go @@ -0,0 +1,25 @@ +/* +Copyright The Helm 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 main + +import ( + "testing" +) + +func TestEnvFileCompletion(t *testing.T) { + checkFileCompletion(t, "env", false) +} diff --git a/cmd/helm/get.go b/cmd/helm/get.go index 7c4854b59..e94871c7b 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -37,10 +37,11 @@ get extended information about the release, including: func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "get", - Short: "download extended information of a named release", - Long: getHelp, - Args: require.NoArgs, + Use: "get", + Short: "download extended information of a named release", + Long: getHelp, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newGetAllCmd(cfg, out)) diff --git a/cmd/helm/get_all_test.go b/cmd/helm/get_all_test.go index a213ac583..0c140faf8 100644 --- a/cmd/helm/get_all_test.go +++ b/cmd/helm/get_all_test.go @@ -45,3 +45,8 @@ func TestGetCmd(t *testing.T) { func TestGetAllRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get all") } + +func TestGetAllFileCompletion(t *testing.T) { + checkFileCompletion(t, "get all", false) + checkFileCompletion(t, "get all myrelease", false) +} diff --git a/cmd/helm/get_hooks_test.go b/cmd/helm/get_hooks_test.go index 7b9142d12..6c010d1bd 100644 --- a/cmd/helm/get_hooks_test.go +++ b/cmd/helm/get_hooks_test.go @@ -40,3 +40,8 @@ func TestGetHooks(t *testing.T) { func TestGetHooksRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get hooks") } + +func TestGetHooksFileCompletion(t *testing.T) { + checkFileCompletion(t, "get hooks", false) + checkFileCompletion(t, "get hooks myrelease", false) +} diff --git a/cmd/helm/get_manifest_test.go b/cmd/helm/get_manifest_test.go index bd0ffc5d6..f3f572e80 100644 --- a/cmd/helm/get_manifest_test.go +++ b/cmd/helm/get_manifest_test.go @@ -40,3 +40,8 @@ func TestGetManifest(t *testing.T) { func TestGetManifestRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get manifest") } + +func TestGetManifestFileCompletion(t *testing.T) { + checkFileCompletion(t, "get manifest", false) + checkFileCompletion(t, "get manifest myrelease", false) +} diff --git a/cmd/helm/get_notes_test.go b/cmd/helm/get_notes_test.go index a59120b77..7d43c87e7 100644 --- a/cmd/helm/get_notes_test.go +++ b/cmd/helm/get_notes_test.go @@ -40,3 +40,8 @@ func TestGetNotesCmd(t *testing.T) { func TestGetNotesRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get notes") } + +func TestGetNotesFileCompletion(t *testing.T) { + checkFileCompletion(t, "get notes", false) + checkFileCompletion(t, "get notes myrelease", false) +} diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go new file mode 100644 index 000000000..79f914bea --- /dev/null +++ b/cmd/helm/get_test.go @@ -0,0 +1,25 @@ +/* +Copyright The Helm 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 main + +import ( + "testing" +) + +func TestGetFileCompletion(t *testing.T) { + checkFileCompletion(t, "get", false) +} diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go index ecd92d354..2a71b1e4d 100644 --- a/cmd/helm/get_values_test.go +++ b/cmd/helm/get_values_test.go @@ -60,3 +60,8 @@ func TestGetValuesRevisionCompletion(t *testing.T) { func TestGetValuesOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "get values") } + +func TestGetValuesFileCompletion(t *testing.T) { + checkFileCompletion(t, "get values", false) + checkFileCompletion(t, "get values myrelease", false) +} diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go index c9bfb648e..fffd983da 100644 --- a/cmd/helm/history_test.go +++ b/cmd/helm/history_test.go @@ -108,3 +108,8 @@ func revisionFlagCompletionTest(t *testing.T, cmdName string) { }} runTestCmd(t, tests) } + +func TestHistoryFileCompletion(t *testing.T) { + checkFileCompletion(t, "history", false) + checkFileCompletion(t, "history myrelease", false) +} diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 36de648f9..6892fcd86 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -239,3 +239,10 @@ func TestInstallVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestInstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "install", false) + checkFileCompletion(t, "install --generate-name", true) + checkFileCompletion(t, "install myname", true) + checkFileCompletion(t, "install myname mychart", false) +} diff --git a/cmd/helm/lint_test.go b/cmd/helm/lint_test.go index 9079935d4..3501ccf87 100644 --- a/cmd/helm/lint_test.go +++ b/cmd/helm/lint_test.go @@ -36,3 +36,8 @@ func TestLintCmdWithSubchartsFlag(t *testing.T) { }} runTestCmd(t, tests) } + +func TestLintFileCompletion(t *testing.T) { + checkFileCompletion(t, "lint", true) + checkFileCompletion(t, "lint mypath", true) // Multiple paths can be given +} diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 2f40707b3..08a26be04 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -63,11 +63,12 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { var outfmt output.Format cmd := &cobra.Command{ - Use: "list", - Short: "list releases", - Long: listHelp, - Aliases: []string{"ls"}, - Args: require.NoArgs, + Use: "list", + Short: "list releases", + Long: listHelp, + Aliases: []string{"ls"}, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { if client.AllNamespaces { if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil { diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index dadb57b94..b3b29356e 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -235,3 +235,7 @@ func TestListCmd(t *testing.T) { func TestListOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "list") } + +func TestListFileCompletion(t *testing.T) { + checkFileCompletion(t, "list", false) +} diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index e0a5fabd6..ecb3ee36c 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -202,3 +202,8 @@ func setFlags(cmd *cobra.Command, flags map[string]string) { dest.Set(f, v) } } + +func TestPackageFileCompletion(t *testing.T) { + checkFileCompletion(t, "package", true) + checkFileCompletion(t, "package mypath", true) // Multiple paths can be given +} diff --git a/cmd/helm/plugin.go b/cmd/helm/plugin.go index 8e1044f54..a118a03eb 100644 --- a/cmd/helm/plugin.go +++ b/cmd/helm/plugin.go @@ -32,9 +32,10 @@ Manage client-side Helm plugins. func newPluginCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "plugin", - Short: "install, list, or uninstall Helm plugins", - Long: pluginHelp, + Use: "plugin", + Short: "install, list, or uninstall Helm plugins", + Long: pluginHelp, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand( newPluginInstallCmd(out), diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go index 8deb1f4f9..183d3dc57 100644 --- a/cmd/helm/plugin_install.go +++ b/cmd/helm/plugin_install.go @@ -43,6 +43,14 @@ func newPluginInstallCmd(out io.Writer) *cobra.Command { Long: pluginInstallDesc, Aliases: []string{"add"}, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // We do file completion, in case the plugin is local + return nil, cobra.ShellCompDirectiveDefault + } + // No more completion once the plugin path has been specified + return nil, cobra.ShellCompDirectiveNoFileComp + }, PreRunE: func(cmd *cobra.Command, args []string) error { return o.complete(args) }, diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go index 49a454963..6503161e8 100644 --- a/cmd/helm/plugin_list.go +++ b/cmd/helm/plugin_list.go @@ -28,9 +28,10 @@ import ( func newPluginListCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "list installed Helm plugins", + Use: "list", + Aliases: []string{"ls"}, + Short: "list installed Helm plugins", + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { debug("pluginDirs: %s", settings.PluginsDirectory) plugins, err := plugin.FindPlugins(settings.PluginsDirectory) diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index e43f277a5..cf21d8460 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -298,3 +298,26 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) { t.Fatalf("Expected 0 plugins, got %d", len(plugins)) } } + +func TestPluginFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin", false) +} + +func TestPluginInstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin install", true) + checkFileCompletion(t, "plugin install mypath", false) +} + +func TestPluginListFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin list", false) +} + +func TestPluginUninstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin uninstall", false) + checkFileCompletion(t, "plugin uninstall myplugin", false) +} + +func TestPluginUpdateFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin update", false) + checkFileCompletion(t, "plugin update myplugin", false) +} diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go index 435df51f1..3f769a1bc 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -221,3 +221,8 @@ func TestPullVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestPullFileCompletion(t *testing.T) { + checkFileCompletion(t, "pull", false) + checkFileCompletion(t, "pull repo/chart", false) +} diff --git a/cmd/helm/release_testing_test.go b/cmd/helm/release_testing_test.go new file mode 100644 index 000000000..257e95721 --- /dev/null +++ b/cmd/helm/release_testing_test.go @@ -0,0 +1,26 @@ +/* +Copyright The Helm 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 main + +import ( + "testing" +) + +func TestReleaseTestingFileCompletion(t *testing.T) { + checkFileCompletion(t, "test", false) + checkFileCompletion(t, "test myrelease", false) +} diff --git a/cmd/helm/repo.go b/cmd/helm/repo.go index ad6ceaa8f..5aac38819 100644 --- a/cmd/helm/repo.go +++ b/cmd/helm/repo.go @@ -34,10 +34,11 @@ It can be used to add, remove, list, and index chart repositories. func newRepoCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "repo add|remove|list|index|update [ARGS]", - Short: "add, list, remove, update, and index chart repositories", - Long: repoHelm, - Args: require.NoArgs, + Use: "repo add|remove|list|index|update [ARGS]", + Short: "add, list, remove, update, and index chart repositories", + Long: repoHelm, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newRepoAddCmd(out)) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 418a549c6..3eeb342f5 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -57,9 +57,10 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { o := &repoAddOptions{} cmd := &cobra.Command{ - Use: "add [NAME] [URL]", - Short: "add a chart repository", - Args: require.ExactArgs(2), + Use: "add [NAME] [URL]", + Short: "add a chart repository", + Args: require.ExactArgs(2), + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { o.name = args[0] o.url = args[1] diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 7cb669a1a..9ef64390b 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -160,3 +160,9 @@ func repoAddConcurrent(t *testing.T, testName, repoFile string) { } } } + +func TestRepoAddFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo add", false) + checkFileCompletion(t, "repo add reponame", false) + checkFileCompletion(t, "repo add reponame https://example.com", false) +} diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go index 63afaf37b..917acd442 100644 --- a/cmd/helm/repo_index.go +++ b/cmd/helm/repo_index.go @@ -53,6 +53,14 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command { Short: "generate an index file given a directory containing packaged charts", Long: repoIndexDesc, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // Allow file completion when completing the argument for the directory + return nil, cobra.ShellCompDirectiveDefault + } + // No more completions, so disable file completion + return nil, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { o.dir = args[0] return o.run(out) diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go index e04ae1b59..ae3390154 100644 --- a/cmd/helm/repo_index_test.go +++ b/cmd/helm/repo_index_test.go @@ -165,3 +165,8 @@ func copyFile(dst, src string) error { return err } + +func TestRepoIndexFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo index", true) + checkFileCompletion(t, "repo index mydir", false) +} diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index ed1c9573c..fc53ba75a 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -32,10 +32,11 @@ import ( func newRepoListCmd(out io.Writer) *cobra.Command { var outfmt output.Format cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "list chart repositories", - Args: require.NoArgs, + Use: "list", + Aliases: []string{"ls"}, + Short: "list chart repositories", + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { f, err := repo.LoadFile(settings.RepositoryConfig) if isNotExist(err) || (len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML)) { diff --git a/cmd/helm/repo_list_test.go b/cmd/helm/repo_list_test.go index f371452f2..90149ebda 100644 --- a/cmd/helm/repo_list_test.go +++ b/cmd/helm/repo_list_test.go @@ -23,3 +23,7 @@ import ( func TestRepoListOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "repo list") } + +func TestRepoListFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo list", false) +} diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index f7d50140e..0ea1d63d2 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -160,3 +160,8 @@ func testCacheFiles(t *testing.T, cacheIndexFile string, cacheChartsFile string, t.Errorf("Error cache chart file was not removed for repository %s", repoName) } } + +func TestRepoRemoveFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo remove", false) + checkFileCompletion(t, "repo remove repo1", false) +} diff --git a/cmd/helm/repo_test.go b/cmd/helm/repo_test.go new file mode 100644 index 000000000..2b0df7c4c --- /dev/null +++ b/cmd/helm/repo_test.go @@ -0,0 +1,25 @@ +/* +Copyright The Helm 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 main + +import ( + "testing" +) + +func TestRepoFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo", false) +} diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index cbfc05b00..e845751c1 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -46,11 +46,12 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command { o := &repoUpdateOptions{update: updateCharts} cmd := &cobra.Command{ - Use: "update", - Aliases: []string{"up"}, - Short: "update information of available charts locally from chart repositories", - Long: updateDesc, - Args: require.NoArgs, + Use: "update", + Aliases: []string{"up"}, + Short: "update information of available charts locally from chart repositories", + Long: updateDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { o.repoFile = settings.RepositoryConfig o.repoCache = settings.RepositoryCache diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index 981f0bba3..e5e4eb337 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -100,3 +100,7 @@ func TestUpdateCharts(t *testing.T) { t.Error("Update was not successful") } } + +func TestRepoUpdateFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo update", false) +} diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go index c11a7fca6..b39378f92 100644 --- a/cmd/helm/rollback_test.go +++ b/cmd/helm/rollback_test.go @@ -104,3 +104,9 @@ func TestRollbackRevisionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestRollbackFileCompletion(t *testing.T) { + checkFileCompletion(t, "rollback", false) + checkFileCompletion(t, "rollback myrelease", false) + checkFileCompletion(t, "rollback myrelease 1", false) +} diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 449009cb5..904f11a21 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -75,6 +75,9 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string Short: "The Helm package manager for Kubernetes.", Long: globalUsage, SilenceUsage: true, + // This breaks completion for 'helm help ' + // The Cobra release following 1.0 will fix this + //ValidArgsFunction: noCompletions, // Disable file completion } flags := cmd.PersistentFlags() diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go index 891bb698e..075544971 100644 --- a/cmd/helm/root_test.go +++ b/cmd/helm/root_test.go @@ -121,3 +121,11 @@ func TestUnknownSubCmd(t *testing.T) { t.Errorf("Expect unknown command error, got %q", err) } } + +// Need the release of Cobra following 1.0 to be able to disable +// file completion on the root command. Until then, we cannot +// because it would break 'helm help ' +// +// func TestRootFileCompletion(t *testing.T) { +// checkFileCompletion(t, "", false) +// } diff --git a/cmd/helm/search.go b/cmd/helm/search.go index 240d5e7c7..44c8d64e3 100644 --- a/cmd/helm/search.go +++ b/cmd/helm/search.go @@ -31,9 +31,10 @@ search subcommands to search different locations for charts. func newSearchCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "search [keyword]", - Short: "search for a keyword in charts", - Long: searchDesc, + Use: "search [keyword]", + Short: "search for a keyword in charts", + Long: searchDesc, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newSearchHubCmd(out)) diff --git a/cmd/helm/search_hub_test.go b/cmd/helm/search_hub_test.go index 7b0f3a389..4f62eed74 100644 --- a/cmd/helm/search_hub_test.go +++ b/cmd/helm/search_hub_test.go @@ -54,3 +54,7 @@ func TestSearchHubCmd(t *testing.T) { func TestSearchHubOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "search hub") } + +func TestSearchHubFileCompletion(t *testing.T) { + checkFileCompletion(t, "search hub", true) // File completion may be useful when inputing a keyword +} diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index dd530379b..a7f27f179 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -365,6 +365,11 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell // We handle it ourselves instead. completions = compEnforceNoSpace(completions) } + if !includeFiles { + // If we should not include files in the completions, + // we should disable file completion + directive = directive | cobra.ShellCompDirectiveNoFileComp + } return completions, directive } diff --git a/cmd/helm/search_repo_test.go b/cmd/helm/search_repo_test.go index 402ef2970..39c9c53f5 100644 --- a/cmd/helm/search_repo_test.go +++ b/cmd/helm/search_repo_test.go @@ -87,3 +87,7 @@ func TestSearchRepositoriesCmd(t *testing.T) { func TestSearchRepoOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "search repo") } + +func TestSearchRepoFileCompletion(t *testing.T) { + checkFileCompletion(t, "search repo", true) // File completion may be useful when inputing a keyword +} diff --git a/cmd/helm/search_test.go b/cmd/helm/search_test.go new file mode 100644 index 000000000..6cf845b06 --- /dev/null +++ b/cmd/helm/search_test.go @@ -0,0 +1,23 @@ +/* +Copyright The Helm 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 main + +import "testing" + +func TestSearchFileCompletion(t *testing.T) { + checkFileCompletion(t, "search", false) +} diff --git a/cmd/helm/show.go b/cmd/helm/show.go index fe5eaee26..888d2d3f3 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -55,11 +55,12 @@ func newShowCmd(out io.Writer) *cobra.Command { client := action.NewShow(action.ShowAll) showCommand := &cobra.Command{ - Use: "show", - Short: "show information of a chart", - Aliases: []string{"inspect"}, - Long: showDesc, - Args: require.NoArgs, + Use: "show", + Short: "show information of a chart", + Aliases: []string{"inspect"}, + Long: showDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } // Function providing dynamic auto-completion diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index 6c550282f..2734faf5e 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -118,3 +118,23 @@ func TestShowVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestShowFileCompletion(t *testing.T) { + checkFileCompletion(t, "show", false) +} + +func TestShowAllFileCompletion(t *testing.T) { + checkFileCompletion(t, "show all", true) +} + +func TestShowChartFileCompletion(t *testing.T) { + checkFileCompletion(t, "show chart", true) +} + +func TestShowReadmeFileCompletion(t *testing.T) { + checkFileCompletion(t, "show readme", true) +} + +func TestShowValuesFileCompletion(t *testing.T) { + checkFileCompletion(t, "show values", true) +} diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go index 0d2500e65..18731e465 100644 --- a/cmd/helm/status_test.go +++ b/cmd/helm/status_test.go @@ -171,3 +171,8 @@ func TestStatusRevisionCompletion(t *testing.T) { func TestStatusOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "status") } + +func TestStatusFileCompletion(t *testing.T) { + checkFileCompletion(t, "status", false) + checkFileCompletion(t, "status myrelease", false) +} diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index f8cd31347..6f7ca939d 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -154,3 +154,10 @@ func TestTemplateVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestTemplateFileCompletion(t *testing.T) { + checkFileCompletion(t, "template", false) + checkFileCompletion(t, "template --generate-name", true) + checkFileCompletion(t, "template myname", true) + checkFileCompletion(t, "template myname mychart", false) +} diff --git a/cmd/helm/uninstall_test.go b/cmd/helm/uninstall_test.go index a34934077..ad78361c1 100644 --- a/cmd/helm/uninstall_test.go +++ b/cmd/helm/uninstall_test.go @@ -66,3 +66,8 @@ func TestUninstall(t *testing.T) { } runTestCmd(t, tests) } + +func TestUninstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "uninstall", false) + checkFileCompletion(t, "uninstall myrelease", false) +} diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 64daf2934..6fe79ebce 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -407,3 +407,9 @@ func TestUpgradeVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestUpgradeFileCompletion(t *testing.T) { + checkFileCompletion(t, "upgrade", false) + checkFileCompletion(t, "upgrade myrelease", true) + checkFileCompletion(t, "upgrade myrelease repo/chart", false) +} diff --git a/cmd/helm/verify.go b/cmd/helm/verify.go index f26fb377f..d126c9ef3 100644 --- a/cmd/helm/verify.go +++ b/cmd/helm/verify.go @@ -44,6 +44,14 @@ func newVerifyCmd(out io.Writer) *cobra.Command { Short: "verify that a chart at the given path has been signed and is valid", Long: verifyDesc, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // Allow file completion when completing the argument for the path + return nil, cobra.ShellCompDirectiveDefault + } + // No more completions, so disable file completion + return nil, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { err := client.Run(args[0]) if err != nil { diff --git a/cmd/helm/verify_test.go b/cmd/helm/verify_test.go index ccbcb3cf2..23b793557 100644 --- a/cmd/helm/verify_test.go +++ b/cmd/helm/verify_test.go @@ -90,3 +90,8 @@ func TestVerifyCmd(t *testing.T) { }) } } + +func TestVerifyFileCompletion(t *testing.T) { + checkFileCompletion(t, "verify", true) + checkFileCompletion(t, "verify mypath", false) +} diff --git a/cmd/helm/version.go b/cmd/helm/version.go index 2f50c876c..72f93e545 100644 --- a/cmd/helm/version.go +++ b/cmd/helm/version.go @@ -59,10 +59,11 @@ func newVersionCmd(out io.Writer) *cobra.Command { o := &versionOptions{} cmd := &cobra.Command{ - Use: "version", - Short: "print the client version information", - Long: versionDesc, - Args: require.NoArgs, + Use: "version", + Short: "print the client version information", + Long: versionDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { return o.run(out) }, diff --git a/cmd/helm/version_test.go b/cmd/helm/version_test.go index 134401948..aa3cbfb7d 100644 --- a/cmd/helm/version_test.go +++ b/cmd/helm/version_test.go @@ -43,3 +43,7 @@ func TestVersion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestVersionFileCompletion(t *testing.T) { + checkFileCompletion(t, "version", false) +}