From 327dfeab38834b28b165836c6b11c73934fb83d5 Mon Sep 17 00:00:00 2001 From: Suleiman Dibirov Date: Sat, 18 Oct 2025 12:33:32 +0300 Subject: [PATCH] feat: add plugin existence check before installation Signed-off-by: Suleiman Dibirov --- internal/plugin/installer/http_installer.go | 7 ++++++- internal/plugin/installer/local_installer.go | 21 +++++++++++++++++++- internal/plugin/installer/oci_installer.go | 8 ++++++-- internal/plugin/installer/vcs_installer.go | 14 +++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/internal/plugin/installer/http_installer.go b/internal/plugin/installer/http_installer.go index bb96314f4..a8f8f9b3a 100644 --- a/internal/plugin/installer/http_installer.go +++ b/internal/plugin/installer/http_installer.go @@ -91,7 +91,12 @@ func (i *HTTPInstaller) Install() error { return fmt.Errorf("failed to extract plugin metadata from tarball: %w", err) } filename := fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version) - tarballPath := helmpath.DataPath("plugins", filename) + pluginsPath := helmpath.DataPath("plugins") + foundPlugins, _ := plugin.FindPlugins([]string{pluginsPath}, plugin.Descriptor{Name: metadata.Name}) + if len(foundPlugins) > 0 { + return fmt.Errorf("plugin %q already exists at %q", metadata.Name, foundPlugins[0].Dir()) + } + tarballPath := filepath.Join(pluginsPath, filename) if err := os.MkdirAll(filepath.Dir(tarballPath), 0755); err != nil { return fmt.Errorf("failed to create plugins directory: %w", err) } diff --git a/internal/plugin/installer/local_installer.go b/internal/plugin/installer/local_installer.go index 1c8314282..475f1d920 100644 --- a/internal/plugin/installer/local_installer.go +++ b/internal/plugin/installer/local_installer.go @@ -97,6 +97,20 @@ func (i *LocalInstaller) installFromDirectory() error { if !isPlugin(i.Source) { return ErrMissingMetadata } + + // Load plugin to get metadata and check for existing plugin with same name + p, err := plugin.LoadDir(i.Source) + if err != nil { + return fmt.Errorf("failed to load plugin: %w", err) + } + metadata := p.Metadata() + + pluginsPath := helmpath.DataPath("plugins") + foundPlugins, _ := plugin.FindPlugins([]string{pluginsPath}, plugin.Descriptor{Name: metadata.Name}) + if len(foundPlugins) > 0 { + return fmt.Errorf("plugin %q already exists at %q", metadata.Name, foundPlugins[0].Dir()) + } + slog.Debug("symlinking", "source", i.Source, "path", i.Path()) return os.Symlink(i.Source, i.Path()) } @@ -116,7 +130,12 @@ func (i *LocalInstaller) installFromArchive() error { return fmt.Errorf("failed to extract plugin metadata from tarball: %w", err) } filename := fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version) - tarballPath := helmpath.DataPath("plugins", filename) + pluginsPath := helmpath.DataPath("plugins") + foundPlugins, _ := plugin.FindPlugins([]string{pluginsPath}, plugin.Descriptor{Name: metadata.Name}) + if len(foundPlugins) > 0 { + return fmt.Errorf("plugin %q already exists at %q", metadata.Name, foundPlugins[0].Dir()) + } + tarballPath := filepath.Join(pluginsPath, filename) if err := os.MkdirAll(filepath.Dir(tarballPath), 0755); err != nil { return fmt.Errorf("failed to create plugins directory: %w", err) } diff --git a/internal/plugin/installer/oci_installer.go b/internal/plugin/installer/oci_installer.go index afbb42ca5..346646d3e 100644 --- a/internal/plugin/installer/oci_installer.go +++ b/internal/plugin/installer/oci_installer.go @@ -101,8 +101,12 @@ func (i *OCIInstaller) Install() error { return fmt.Errorf("failed to extract plugin metadata from tarball: %w", err) } filename := fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version) - - tarballPath := helmpath.DataPath("plugins", filename) + pluginsPath := helmpath.DataPath("plugins") + foundPlugins, _ := plugin.FindPlugins([]string{pluginsPath}, plugin.Descriptor{Name: metadata.Name}) + if len(foundPlugins) > 0 { + return fmt.Errorf("plugin %q already exists at %q", metadata.Name, foundPlugins[0].Dir()) + } + tarballPath := filepath.Join(pluginsPath, filename) if err := os.MkdirAll(filepath.Dir(tarballPath), 0755); err != nil { return fmt.Errorf("failed to create plugins directory: %w", err) } diff --git a/internal/plugin/installer/vcs_installer.go b/internal/plugin/installer/vcs_installer.go index 3601ec7a8..79b1faa6a 100644 --- a/internal/plugin/installer/vcs_installer.go +++ b/internal/plugin/installer/vcs_installer.go @@ -26,6 +26,7 @@ import ( "github.com/Masterminds/semver/v3" "github.com/Masterminds/vcs" + "helm.sh/helm/v4/internal/plugin" "helm.sh/helm/v4/internal/plugin/cache" "helm.sh/helm/v4/internal/third_party/dep/fs" "helm.sh/helm/v4/pkg/helmpath" @@ -91,6 +92,19 @@ func (i *VCSInstaller) Install() error { return ErrMissingMetadata } + // Load plugin to get metadata and check for existing plugin with same name + p, err := plugin.LoadDir(i.Repo.LocalPath()) + if err != nil { + return fmt.Errorf("failed to load plugin: %w", err) + } + metadata := p.Metadata() + + // Check if a plugin with the same name already exists + foundPlugins, _ := plugin.FindPlugins([]string{i.PluginsDirectory}, plugin.Descriptor{Name: metadata.Name}) + if len(foundPlugins) > 0 { + return fmt.Errorf("plugin %q already exists at %q", metadata.Name, foundPlugins[0].Dir()) + } + slog.Debug("copying files", "source", i.Repo.LocalPath(), "destination", i.Path()) return fs.CopyDir(i.Repo.LocalPath(), i.Path()) }