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) +}