Repository: helm/helm (go)

Issue:
Unable to install helm plugin 3.18.0, if plugin has both platformCommand and command
Ref:
* https://github.com/jkroepke/helm-secrets/issues/530

Hi,

end users reports that helm-secrets plugin is not installable under 3.18:

```
$ helm plugin install https://github.com/jkroepke/helm-secrets
Error: plugin is installed but unusable: both platformCommand and command are set in "/home/runner/.local/share/helm/plugins/helm-secrets/plugin.yaml"
Error: Process completed with exit code 1.
```

Related PR: 

* https://github.com/helm/helm/pull/12962 
* https://github.com/helm/helm/pull/13592

Before helm 3.18, its was fine to have a `platformCommand` and `command` at the same time. Now it's forbidden which is an backwards incompatible change.

The behavior to have both properties was documented before 

* https://github.com/helm/helm-www/pull/1627

Pull request description:
Log warning when platformCommand|Hook and Command|Hook are set
same for `PlatformHooks` and `hooks`.



<!--  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**:

When [backporting a feature for Helm v4](https://github.com/helm/helm/pull/13592) we forgot to mark as deprecation validations what will be enforced in v4. It was instand error even if [documentation say something different](https://helm.sh/docs/topics/plugins/#the-pluginyaml-file).

See the PR (search for "Remove in Helm 4."):
- https://github.com/helm/helm/pull/13592/files#diff-fc137d62147eb6a9c89c0566479ff219320ca691f2505631ebacbe513bcc8108R268-R270

**I am wondering how much enforcing like this in the PR was a good idea. Maybe we wanted only to inform, that soon helm will no longer support both attributes or `command` or `hooks`**.

My patch, **is probably not what we want** but it fixes #30876. Do we want to enforce this change in v3.18.0? If yes we can close this PR.

Before:

```sh
make build && bin/helm version && bin/helm plugin install https://github.com/jkroepke/helm-secrets --version v4.6.4 --debug                                                     1 ↵
make: Nothing to be done for `build'.
failed to load plugins: both platformCommand and command are set in "/Users/benoit.tigeot/Library/helm/plugins/helm-secrets/plugin.yaml"
version.BuildInfo{Version:"v3.18+unreleased", GitCommit:"e6122aba27fde30306dac79865df3189c5ff3be7", GitTreeState:"dirty", GoVersion:"go1.24.0"}
failed to load plugins: both platformCommand and command are set in "/Users/benoit.tigeot/Library/helm/plugins/helm-secrets/plugin.yaml"
Error: plugin already exists
helm.go:92: 2025-05-20 22:13:45.12691 +0200 CEST m=+0.138222667 [debug] plugin already exists
helm.sh/helm/v3/pkg/plugin/installer.Install
        helm.sh/helm/v3/pkg/plugin/installer/installer.go:53
main.(*pluginInstallOptions).run
        helm.sh/helm/v3/cmd/helm/plugin_install.go:78
main.newPluginInstallCmd.func3
        helm.sh/helm/v3/cmd/helm/plugin_install.go:59
github.com/spf13/cobra.(*Command).execute
        github.com/spf13/cobra@v1.9.1/command.go:1015
github.com/spf13/cobra.(*Command).ExecuteC
        github.com/spf13/cobra@v1.9.1/command.go:1148
github.com/spf13/cobra.(*Command).Execute
        github.com/spf13/cobra@v1.9.1/command.go:1071
main.main
        helm.sh/helm/v3/cmd/helm/helm.go:91
runtime.main
        runtime/proc.go:283
runtime.goexit
        runtime/asm_arm64.s:1223
```

after
```sh
make build && bin/helm version && bin/helm plugin install https://github.com/jkroepke/helm-secrets --version v4.6.4 --debug
make: Nothing to be done for `build'.
version.BuildInfo{Version:"v3.18+unreleased", GitCommit:"e6122aba27fde30306dac79865df3189c5ff3be7", GitTreeState:"dirty", GoVersion:"go1.24.0"}
[debug] vcs_installer.go:162: updating https://github.com/jkroepke/helm-secrets
[debug] vcs_installer.go:152: setting version to "v4.6.4"
[debug] vcs_installer.go:91: copying /Users/benoit.tigeot/Library/Caches/helm/plugins/https-github.com-jkroepke-helm-secrets to /Users/benoit.tigeot/Library/helm/plugins/helm-secrets
plugin_install.go:82: 2025-05-20 22:14:19.927423 +0200 CEST m=+1.938548418 [debug] loading plugin from /Users/benoit.tigeot/Library/helm/plugins/helm-secrets
Installed plugin: secrets
```

Close: https://github.com/helm/helm/issues/30876

**Special notes for your reviewer**:

**If applicable**:
- [ ] this PR contains user facing changes (the `docs needed` label should be applied if so)
- [ ] this PR contains unit tests
- [ ] this PR has been tested for backwards compatibility


Patch:
diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go
index 8eac1f74d..e2fb78672 100644
--- a/pkg/plugin/plugin.go
+++ b/pkg/plugin/plugin.go
@@ -84,7 +84,7 @@ type Metadata struct {
 	PlatformCommand []PlatformCommand `json:"platformCommand"`
 
 	// Command is the plugin command, as a single string.
-	// Providing a command will result in an error if PlatformCommand is also set.
+	// Providing a command will result in an deprecation warning if PlatformCommand is also set.
 	//
 	// The command will be passed through environment expansion, so env vars can
 	// be present in this command. Unless IgnoreFlags is set, this will
@@ -266,11 +266,11 @@ func validatePluginData(plug *Plugin, filepath string) error {
 	plug.Metadata.Usage = sanitizeString(plug.Metadata.Usage)
 
 	if len(plug.Metadata.PlatformCommand) > 0 && len(plug.Metadata.Command) > 0 {
-		return fmt.Errorf("both platformCommand and command are set in %q", filepath)
+		fmt.Printf("WARNING: both 'platformCommand' and 'command' are set in %q (this will become an error in a future Helm version)\n", filepath)
 	}
 
 	if len(plug.Metadata.PlatformHooks) > 0 && len(plug.Metadata.Hooks) > 0 {
-		return fmt.Errorf("both platformHooks and hooks are set in %q", filepath)
+		fmt.Printf("WARNING: both 'platformHooks' and 'hooks' are set in %q (this will become an error in a future Helm version)\n", filepath)
 	}
 
 	// We could also validate SemVer, executable, and other fields should we so choose.


Test patch:
diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go
index 770d701fa..7447bdb50 100644
--- a/pkg/plugin/plugin_test.go
+++ b/pkg/plugin/plugin_test.go
@@ -496,8 +496,8 @@ func TestValidatePluginData(t *testing.T) {
 		{false, mockMissingMeta},         // Test if the metadata section missing
 		{true, mockNoCommand},            // Test no command metadata works
 		{true, mockLegacyCommand},        // Test legacy command metadata works
-		{false, mockWithCommand},         // Test platformCommand and command both set fails
-		{false, mockWithHooks},           // Test platformHooks and hooks both set fails
+		{true, mockWithCommand},          // Test platformCommand and command both set pass until v4
+		{true, mockWithHooks},            // Test platformHooks and hooks both set pass until v4
 	} {
 		err := validatePluginData(item.plug, fmt.Sprintf("test-%d", i))
 		if item.pass && err != nil {