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 <TAB>
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 <marc.khouzam@montreal.ca>
pull/8611/head
Marc Khouzam 4 years ago committed by Marc Khouzam
parent c2c8c570e9
commit d6708667da

@ -54,10 +54,11 @@ $ helm completion zsh > "${fpath[1]}/_helm"
func newCompletionCmd(out io.Writer) *cobra.Command { func newCompletionCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "completion", Use: "completion",
Short: "generate autocompletions script for the specified shell", Short: "generate autocompletions script for the specified shell",
Long: completionDesc, Long: completionDesc,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions, // Disable file completion
} }
bash := &cobra.Command{ bash := &cobra.Command{
@ -66,6 +67,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
Long: bashCompDesc, Long: bashCompDesc,
Args: require.NoArgs, Args: require.NoArgs,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCompletionBash(out, cmd) return runCompletionBash(out, cmd)
}, },
@ -77,6 +79,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
Long: zshCompDesc, Long: zshCompDesc,
Args: require.NoArgs, Args: require.NoArgs,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCompletionZsh(out, cmd) return runCompletionZsh(out, cmd)
}, },
@ -253,3 +256,8 @@ __helm_bash_source <(__helm_convert_bash_to_zsh)
out.Write([]byte(zshTail)) out.Write([]byte(zshTail))
return nil return nil
} }
// Function to disable file completion
func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp
}

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

@ -64,6 +64,15 @@ func newCreateCmd(out io.Writer) *cobra.Command {
Short: "create a new chart with the given name", Short: "create a new chart with the given name",
Long: createDesc, Long: createDesc,
Args: require.ExactArgs(1), 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 { RunE: func(cmd *cobra.Command, args []string) error {
o.name = args[0] o.name = args[0]
o.starterDir = helmpath.DataPath("starters") o.starterDir = helmpath.DataPath("starters")

@ -192,3 +192,8 @@ func TestCreateStarterAbsoluteCmd(t *testing.T) {
t.Error("Did not find foo.tpl") t.Error("Did not find foo.tpl")
} }
} }
func TestCreateFileCompletion(t *testing.T) {
checkFileCompletion(t, "create", true)
checkFileCompletion(t, "create myname", false)
}

@ -84,11 +84,12 @@ This will produce an error if the chart cannot be loaded.
func newDependencyCmd(out io.Writer) *cobra.Command { func newDependencyCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "dependency update|build|list", Use: "dependency update|build|list",
Aliases: []string{"dep", "dependencies"}, Aliases: []string{"dep", "dependencies"},
Short: "manage a chart's dependencies", Short: "manage a chart's dependencies",
Long: dependencyDesc, Long: dependencyDesc,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions, // Disable file completion
} }
cmd.AddCommand(newDependencyListCmd(out)) cmd.AddCommand(newDependencyListCmd(out))

@ -51,3 +51,7 @@ func TestDependencyListCmd(t *testing.T) {
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestDependencyFileCompletion(t *testing.T) {
checkFileCompletion(t, "dependency", false)
}

@ -47,11 +47,12 @@ func newDocsCmd(out io.Writer) *cobra.Command {
o := &docsOptions{} o := &docsOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "docs", Use: "docs",
Short: "generate documentation as markdown or man pages", Short: "generate documentation as markdown or man pages",
Long: docsDesc, Long: docsDesc,
Hidden: true, Hidden: true,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
o.topCmd = cmd.Root() o.topCmd = cmd.Root()
return o.run(out) return o.run(out)

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

@ -32,10 +32,11 @@ Env prints out all the environment information in use by Helm.
func newEnvCmd(out io.Writer) *cobra.Command { func newEnvCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "env", Use: "env",
Short: "helm client environment information", Short: "helm client environment information",
Long: envHelp, Long: envHelp,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
envVars := settings.EnvVars() envVars := settings.EnvVars()

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

@ -37,10 +37,11 @@ get extended information about the release, including:
func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "get", Use: "get",
Short: "download extended information of a named release", Short: "download extended information of a named release",
Long: getHelp, Long: getHelp,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions, // Disable file completion
} }
cmd.AddCommand(newGetAllCmd(cfg, out)) cmd.AddCommand(newGetAllCmd(cfg, out))

@ -45,3 +45,8 @@ func TestGetCmd(t *testing.T) {
func TestGetAllRevisionCompletion(t *testing.T) { func TestGetAllRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get all") revisionFlagCompletionTest(t, "get all")
} }
func TestGetAllFileCompletion(t *testing.T) {
checkFileCompletion(t, "get all", false)
checkFileCompletion(t, "get all myrelease", false)
}

@ -40,3 +40,8 @@ func TestGetHooks(t *testing.T) {
func TestGetHooksRevisionCompletion(t *testing.T) { func TestGetHooksRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get hooks") revisionFlagCompletionTest(t, "get hooks")
} }
func TestGetHooksFileCompletion(t *testing.T) {
checkFileCompletion(t, "get hooks", false)
checkFileCompletion(t, "get hooks myrelease", false)
}

@ -40,3 +40,8 @@ func TestGetManifest(t *testing.T) {
func TestGetManifestRevisionCompletion(t *testing.T) { func TestGetManifestRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get manifest") revisionFlagCompletionTest(t, "get manifest")
} }
func TestGetManifestFileCompletion(t *testing.T) {
checkFileCompletion(t, "get manifest", false)
checkFileCompletion(t, "get manifest myrelease", false)
}

@ -40,3 +40,8 @@ func TestGetNotesCmd(t *testing.T) {
func TestGetNotesRevisionCompletion(t *testing.T) { func TestGetNotesRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get notes") revisionFlagCompletionTest(t, "get notes")
} }
func TestGetNotesFileCompletion(t *testing.T) {
checkFileCompletion(t, "get notes", false)
checkFileCompletion(t, "get notes myrelease", false)
}

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

@ -60,3 +60,8 @@ func TestGetValuesRevisionCompletion(t *testing.T) {
func TestGetValuesOutputCompletion(t *testing.T) { func TestGetValuesOutputCompletion(t *testing.T) {
outputFlagCompletionTest(t, "get values") outputFlagCompletionTest(t, "get values")
} }
func TestGetValuesFileCompletion(t *testing.T) {
checkFileCompletion(t, "get values", false)
checkFileCompletion(t, "get values myrelease", false)
}

@ -108,3 +108,8 @@ func revisionFlagCompletionTest(t *testing.T, cmdName string) {
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestHistoryFileCompletion(t *testing.T) {
checkFileCompletion(t, "history", false)
checkFileCompletion(t, "history myrelease", false)
}

@ -239,3 +239,10 @@ func TestInstallVersionCompletion(t *testing.T) {
}} }}
runTestCmd(t, tests) 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)
}

@ -36,3 +36,8 @@ func TestLintCmdWithSubchartsFlag(t *testing.T) {
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestLintFileCompletion(t *testing.T) {
checkFileCompletion(t, "lint", true)
checkFileCompletion(t, "lint mypath", true) // Multiple paths can be given
}

@ -63,11 +63,12 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
var outfmt output.Format var outfmt output.Format
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list", Use: "list",
Short: "list releases", Short: "list releases",
Long: listHelp, Long: listHelp,
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if client.AllNamespaces { if client.AllNamespaces {
if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil { if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil {

@ -235,3 +235,7 @@ func TestListCmd(t *testing.T) {
func TestListOutputCompletion(t *testing.T) { func TestListOutputCompletion(t *testing.T) {
outputFlagCompletionTest(t, "list") outputFlagCompletionTest(t, "list")
} }
func TestListFileCompletion(t *testing.T) {
checkFileCompletion(t, "list", false)
}

@ -202,3 +202,8 @@ func setFlags(cmd *cobra.Command, flags map[string]string) {
dest.Set(f, v) dest.Set(f, v)
} }
} }
func TestPackageFileCompletion(t *testing.T) {
checkFileCompletion(t, "package", true)
checkFileCompletion(t, "package mypath", true) // Multiple paths can be given
}

@ -32,9 +32,10 @@ Manage client-side Helm plugins.
func newPluginCmd(out io.Writer) *cobra.Command { func newPluginCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "plugin", Use: "plugin",
Short: "install, list, or uninstall Helm plugins", Short: "install, list, or uninstall Helm plugins",
Long: pluginHelp, Long: pluginHelp,
ValidArgsFunction: noCompletions, // Disable file completion
} }
cmd.AddCommand( cmd.AddCommand(
newPluginInstallCmd(out), newPluginInstallCmd(out),

@ -43,6 +43,14 @@ func newPluginInstallCmd(out io.Writer) *cobra.Command {
Long: pluginInstallDesc, Long: pluginInstallDesc,
Aliases: []string{"add"}, Aliases: []string{"add"},
Args: require.ExactArgs(1), 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 { PreRunE: func(cmd *cobra.Command, args []string) error {
return o.complete(args) return o.complete(args)
}, },

@ -28,9 +28,10 @@ import (
func newPluginListCmd(out io.Writer) *cobra.Command { func newPluginListCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list", Use: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Short: "list installed Helm plugins", Short: "list installed Helm plugins",
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
debug("pluginDirs: %s", settings.PluginsDirectory) debug("pluginDirs: %s", settings.PluginsDirectory)
plugins, err := plugin.FindPlugins(settings.PluginsDirectory) plugins, err := plugin.FindPlugins(settings.PluginsDirectory)

@ -298,3 +298,26 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
t.Fatalf("Expected 0 plugins, got %d", len(plugins)) 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)
}

@ -221,3 +221,8 @@ func TestPullVersionCompletion(t *testing.T) {
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestPullFileCompletion(t *testing.T) {
checkFileCompletion(t, "pull", false)
checkFileCompletion(t, "pull repo/chart", false)
}

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

@ -34,10 +34,11 @@ It can be used to add, remove, list, and index chart repositories.
func newRepoCmd(out io.Writer) *cobra.Command { func newRepoCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "repo add|remove|list|index|update [ARGS]", Use: "repo add|remove|list|index|update [ARGS]",
Short: "add, list, remove, update, and index chart repositories", Short: "add, list, remove, update, and index chart repositories",
Long: repoHelm, Long: repoHelm,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions, // Disable file completion
} }
cmd.AddCommand(newRepoAddCmd(out)) cmd.AddCommand(newRepoAddCmd(out))

@ -57,9 +57,10 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
o := &repoAddOptions{} o := &repoAddOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "add [NAME] [URL]", Use: "add [NAME] [URL]",
Short: "add a chart repository", Short: "add a chart repository",
Args: require.ExactArgs(2), Args: require.ExactArgs(2),
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
o.name = args[0] o.name = args[0]
o.url = args[1] o.url = args[1]

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

@ -53,6 +53,14 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
Short: "generate an index file given a directory containing packaged charts", Short: "generate an index file given a directory containing packaged charts",
Long: repoIndexDesc, Long: repoIndexDesc,
Args: require.ExactArgs(1), 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 { RunE: func(cmd *cobra.Command, args []string) error {
o.dir = args[0] o.dir = args[0]
return o.run(out) return o.run(out)

@ -165,3 +165,8 @@ func copyFile(dst, src string) error {
return err return err
} }
func TestRepoIndexFileCompletion(t *testing.T) {
checkFileCompletion(t, "repo index", true)
checkFileCompletion(t, "repo index mydir", false)
}

@ -32,10 +32,11 @@ import (
func newRepoListCmd(out io.Writer) *cobra.Command { func newRepoListCmd(out io.Writer) *cobra.Command {
var outfmt output.Format var outfmt output.Format
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list", Use: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Short: "list chart repositories", Short: "list chart repositories",
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
f, err := repo.LoadFile(settings.RepositoryConfig) f, err := repo.LoadFile(settings.RepositoryConfig)
if isNotExist(err) || (len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML)) { if isNotExist(err) || (len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML)) {

@ -23,3 +23,7 @@ import (
func TestRepoListOutputCompletion(t *testing.T) { func TestRepoListOutputCompletion(t *testing.T) {
outputFlagCompletionTest(t, "repo list") outputFlagCompletionTest(t, "repo list")
} }
func TestRepoListFileCompletion(t *testing.T) {
checkFileCompletion(t, "repo list", false)
}

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

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

@ -46,11 +46,12 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
o := &repoUpdateOptions{update: updateCharts} o := &repoUpdateOptions{update: updateCharts}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update", Use: "update",
Aliases: []string{"up"}, Aliases: []string{"up"},
Short: "update information of available charts locally from chart repositories", Short: "update information of available charts locally from chart repositories",
Long: updateDesc, Long: updateDesc,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig o.repoFile = settings.RepositoryConfig
o.repoCache = settings.RepositoryCache o.repoCache = settings.RepositoryCache

@ -100,3 +100,7 @@ func TestUpdateCharts(t *testing.T) {
t.Error("Update was not successful") t.Error("Update was not successful")
} }
} }
func TestRepoUpdateFileCompletion(t *testing.T) {
checkFileCompletion(t, "repo update", false)
}

@ -104,3 +104,9 @@ func TestRollbackRevisionCompletion(t *testing.T) {
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestRollbackFileCompletion(t *testing.T) {
checkFileCompletion(t, "rollback", false)
checkFileCompletion(t, "rollback myrelease", false)
checkFileCompletion(t, "rollback myrelease 1", false)
}

@ -75,6 +75,9 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
Short: "The Helm package manager for Kubernetes.", Short: "The Helm package manager for Kubernetes.",
Long: globalUsage, Long: globalUsage,
SilenceUsage: true, SilenceUsage: true,
// This breaks completion for 'helm help <TAB>'
// The Cobra release following 1.0 will fix this
//ValidArgsFunction: noCompletions, // Disable file completion
} }
flags := cmd.PersistentFlags() flags := cmd.PersistentFlags()

@ -121,3 +121,11 @@ func TestUnknownSubCmd(t *testing.T) {
t.Errorf("Expect unknown command error, got %q", err) 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 <TAB>'
//
// func TestRootFileCompletion(t *testing.T) {
// checkFileCompletion(t, "", false)
// }

@ -31,9 +31,10 @@ search subcommands to search different locations for charts.
func newSearchCmd(out io.Writer) *cobra.Command { func newSearchCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "search [keyword]", Use: "search [keyword]",
Short: "search for a keyword in charts", Short: "search for a keyword in charts",
Long: searchDesc, Long: searchDesc,
ValidArgsFunction: noCompletions, // Disable file completion
} }
cmd.AddCommand(newSearchHubCmd(out)) cmd.AddCommand(newSearchHubCmd(out))

@ -54,3 +54,7 @@ func TestSearchHubCmd(t *testing.T) {
func TestSearchHubOutputCompletion(t *testing.T) { func TestSearchHubOutputCompletion(t *testing.T) {
outputFlagCompletionTest(t, "search hub") outputFlagCompletionTest(t, "search hub")
} }
func TestSearchHubFileCompletion(t *testing.T) {
checkFileCompletion(t, "search hub", true) // File completion may be useful when inputing a keyword
}

@ -365,6 +365,11 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell
// We handle it ourselves instead. // We handle it ourselves instead.
completions = compEnforceNoSpace(completions) 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 return completions, directive
} }

@ -87,3 +87,7 @@ func TestSearchRepositoriesCmd(t *testing.T) {
func TestSearchRepoOutputCompletion(t *testing.T) { func TestSearchRepoOutputCompletion(t *testing.T) {
outputFlagCompletionTest(t, "search repo") outputFlagCompletionTest(t, "search repo")
} }
func TestSearchRepoFileCompletion(t *testing.T) {
checkFileCompletion(t, "search repo", true) // File completion may be useful when inputing a keyword
}

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

@ -55,11 +55,12 @@ func newShowCmd(out io.Writer) *cobra.Command {
client := action.NewShow(action.ShowAll) client := action.NewShow(action.ShowAll)
showCommand := &cobra.Command{ showCommand := &cobra.Command{
Use: "show", Use: "show",
Short: "show information of a chart", Short: "show information of a chart",
Aliases: []string{"inspect"}, Aliases: []string{"inspect"},
Long: showDesc, Long: showDesc,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions, // Disable file completion
} }
// Function providing dynamic auto-completion // Function providing dynamic auto-completion

@ -118,3 +118,23 @@ func TestShowVersionCompletion(t *testing.T) {
}} }}
runTestCmd(t, tests) 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)
}

@ -171,3 +171,8 @@ func TestStatusRevisionCompletion(t *testing.T) {
func TestStatusOutputCompletion(t *testing.T) { func TestStatusOutputCompletion(t *testing.T) {
outputFlagCompletionTest(t, "status") outputFlagCompletionTest(t, "status")
} }
func TestStatusFileCompletion(t *testing.T) {
checkFileCompletion(t, "status", false)
checkFileCompletion(t, "status myrelease", false)
}

@ -154,3 +154,10 @@ func TestTemplateVersionCompletion(t *testing.T) {
}} }}
runTestCmd(t, tests) 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)
}

@ -66,3 +66,8 @@ func TestUninstall(t *testing.T) {
} }
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestUninstallFileCompletion(t *testing.T) {
checkFileCompletion(t, "uninstall", false)
checkFileCompletion(t, "uninstall myrelease", false)
}

@ -407,3 +407,9 @@ func TestUpgradeVersionCompletion(t *testing.T) {
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestUpgradeFileCompletion(t *testing.T) {
checkFileCompletion(t, "upgrade", false)
checkFileCompletion(t, "upgrade myrelease", true)
checkFileCompletion(t, "upgrade myrelease repo/chart", false)
}

@ -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", Short: "verify that a chart at the given path has been signed and is valid",
Long: verifyDesc, Long: verifyDesc,
Args: require.ExactArgs(1), 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 { RunE: func(cmd *cobra.Command, args []string) error {
err := client.Run(args[0]) err := client.Run(args[0])
if err != nil { if err != nil {

@ -90,3 +90,8 @@ func TestVerifyCmd(t *testing.T) {
}) })
} }
} }
func TestVerifyFileCompletion(t *testing.T) {
checkFileCompletion(t, "verify", true)
checkFileCompletion(t, "verify mypath", false)
}

@ -59,10 +59,11 @@ func newVersionCmd(out io.Writer) *cobra.Command {
o := &versionOptions{} o := &versionOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "version", Use: "version",
Short: "print the client version information", Short: "print the client version information",
Long: versionDesc, Long: versionDesc,
Args: require.NoArgs, Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return o.run(out) return o.run(out)
}, },

@ -43,3 +43,7 @@ func TestVersion(t *testing.T) {
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestVersionFileCompletion(t *testing.T) {
checkFileCompletion(t, "version", false)
}

Loading…
Cancel
Save