Repository: helm/helm (go)

Issue:
Add shell completion for OCI feature
The new OCI commands and charts should have shell completion when possible.

Pull request:
feat(comp): Shell completion for OCI
<!--  Thanks for sending a pull request!  Here are some tips for you:
1. Make sure to read the Contributing Guide before submitting your PR: https://github.com/helm/helm/blob/main/CONTRIBUTING.md
2. If this PR closes another issue, add 'closes #<issue number>' somewhere in the PR summary. GitHub will automatically close that issue when this PR gets merged. Alternatively, adding 'refs #<issue number>' will not close the issue, but help provide the reviewer more context.-->

**What this PR does / why we need it**:

Adds shell completion for the OCI feature

Fixes #10553 

**Special notes for your reviewer**:

There are a couple of things I would like to clarify in my understanding of the current OCI support.  I will post review comments to make it easier to discuss.


Patch:
diff --git a/cmd/helm/push.go b/cmd/helm/push.go
index ed5d1699a..d2cf2693e 100644
--- a/cmd/helm/push.go
+++ b/cmd/helm/push.go
@@ -24,6 +24,7 @@ import (
 
 	"helm.sh/helm/v3/cmd/helm/require"
 	"helm.sh/helm/v3/pkg/action"
+	"helm.sh/helm/v3/pkg/pusher"
 )
 
 const pushDesc = `
@@ -41,6 +42,23 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 		Short: "push a chart to remote",
 		Long:  pushDesc,
 		Args:  require.MinimumNArgs(2),
+		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+			if len(args) == 0 {
+				// Do file completion for the chart file to push
+				return nil, cobra.ShellCompDirectiveDefault
+			}
+			if len(args) == 1 {
+				providers := []pusher.Provider(pusher.All(settings))
+				var comps []string
+				for _, p := range providers {
+					for _, scheme := range p.Schemes {
+						comps = append(comps, fmt.Sprintf("%s://", scheme))
+					}
+				}
+				return comps, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
+			}
+			return nil, cobra.ShellCompDirectiveNoFileComp
+		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			chartRef := args[0]
 			remote := args[1]
diff --git a/cmd/helm/registry_login.go b/cmd/helm/registry_login.go
index 84f9f27b5..8abf91e77 100644
--- a/cmd/helm/registry_login.go
+++ b/cmd/helm/registry_login.go
@@ -41,10 +41,11 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
 	var passwordFromStdinOpt, insecureOpt bool
 
 	cmd := &cobra.Command{
-		Use:   "login [host]",
-		Short: "login to a registry",
-		Long:  registryLoginDesc,
-		Args:  require.MinimumNArgs(1),
+		Use:               "login [host]",
+		Short:             "login to a registry",
+		Long:              registryLoginDesc,
+		Args:              require.MinimumNArgs(1),
+		ValidArgsFunction: noCompletions,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			hostname := args[0]
 
diff --git a/cmd/helm/registry_logout.go b/cmd/helm/registry_logout.go
index 8d6a54d97..0084f8c09 100644
--- a/cmd/helm/registry_logout.go
+++ b/cmd/helm/registry_logout.go
@@ -31,10 +31,11 @@ Remove credentials stored for a remote registry.
 
 func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	return &cobra.Command{
-		Use:   "logout [host]",
-		Short: "logout from a registry",
-		Long:  registryLogoutDesc,
-		Args:  require.MinimumNArgs(1),
+		Use:               "logout [host]",
+		Short:             "logout from a registry",
+		Long:              registryLogoutDesc,
+		Args:              require.MinimumNArgs(1),
+		ValidArgsFunction: noCompletions,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			hostname := args[0]
 			return action.NewRegistryLogout(cfg).Run(out, hostname)
diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go
index 34232fcfa..f794f6bca 100644
--- a/cmd/helm/search_repo.go
+++ b/cmd/helm/search_repo.go
@@ -326,7 +326,7 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell
 	cobra.CompDebugln(fmt.Sprintf("Completions after repos: %v", completions), settings.Debug)
 
 	// Now handle completions for url prefixes
-	for _, url := range []string{"https://\tChart URL prefix", "http://\tChart URL prefix", "file://\tChart local URL prefix"} {
+	for _, url := range []string{"oci://\tChart OCI prefix", "https://\tChart URL prefix", "http://\tChart URL prefix", "file://\tChart local URL prefix"} {
 		if strings.HasPrefix(toComplete, url) {
 			// The user already put in the full url prefix; we don't have
 			// anything to add, but make sure the shell does not default


Test patch:
diff --git a/cmd/helm/push_test.go b/cmd/helm/push_test.go
new file mode 100644
index 000000000..8e56d99dc
--- /dev/null
+++ b/cmd/helm/push_test.go
@@ -0,0 +1,27 @@
+/*
+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 TestPushFileCompletion(t *testing.T) {
+	checkFileCompletion(t, "push", true)
+	checkFileCompletion(t, "push package.tgz", false)
+	checkFileCompletion(t, "push package.tgz oci://localhost:5000", false)
+}
diff --git a/cmd/helm/registry_login_test.go b/cmd/helm/registry_login_test.go
new file mode 100644
index 000000000..517fe08e1
--- /dev/null
+++ b/cmd/helm/registry_login_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 TestRegistryLoginFileCompletion(t *testing.T) {
+	checkFileCompletion(t, "registry login", false)
+}
diff --git a/cmd/helm/registry_logout_test.go b/cmd/helm/registry_logout_test.go
new file mode 100644
index 000000000..31f716725
--- /dev/null
+++ b/cmd/helm/registry_logout_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 TestRegistryLogoutFileCompletion(t *testing.T) {
+	checkFileCompletion(t, "registry logout", false)
+}