From 437ddbbbd76df925c25370398c0dda64267801d9 Mon Sep 17 00:00:00 2001 From: Suleiman Dibirov Date: Sat, 7 Dec 2024 17:53:46 +0200 Subject: [PATCH] feat: add latest version and outdated flag to plugin list command Signed-off-by: Suleiman Dibirov --- pkg/cmd/plugin_list.go | 41 +++++++++++++++++++++++-- pkg/plugin/installer/http_installer.go | 5 +++ pkg/plugin/installer/installer.go | 2 ++ pkg/plugin/installer/local_installer.go | 4 +++ pkg/plugin/installer/vcs_installer.go | 20 ++++++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/plugin_list.go b/pkg/cmd/plugin_list.go index 9cca790ae..12c1ec0b1 100644 --- a/pkg/cmd/plugin_list.go +++ b/pkg/cmd/plugin_list.go @@ -18,14 +18,22 @@ package cmd import ( "fmt" "io" + "path/filepath" "github.com/gosuri/uitable" "github.com/spf13/cobra" + "helm.sh/helm/v3/pkg/plugin/installer" "helm.sh/helm/v4/pkg/plugin" ) +type pluginListOptions struct { + showOutdated bool +} + func newPluginListCmd(out io.Writer) *cobra.Command { + o := &pluginListOptions{} + cmd := &cobra.Command{ Use: "list", Aliases: []string{"ls"}, @@ -39,14 +47,24 @@ func newPluginListCmd(out io.Writer) *cobra.Command { } table := uitable.New() - table.AddRow("NAME", "VERSION", "DESCRIPTION") + table.AddRow("NAME", "VERSION", "LATEST", "DESCRIPTION") for _, p := range plugins { - table.AddRow(p.Metadata.Name, p.Metadata.Version, p.Metadata.Description) + latest, err := getLatestVersion(p) + if err != nil { + latest = "unknown" + } + + if !o.showOutdated || (latest != "unknown" && latest != p.Metadata.Version) { + table.AddRow(p.Metadata.Name, p.Metadata.Version, latest, p.Metadata.Description) + } } fmt.Fprintln(out, table) return nil }, } + + cmd.Flags().BoolVar(&o.showOutdated, "outdated", false, "show only outdated plugins") + return cmd } @@ -86,3 +104,22 @@ func compListPlugins(_ string, ignoredPluginNames []string) []string { } return pNames } + +// getLatestVersion returns the latest version of a plugin +func getLatestVersion(p *plugin.Plugin) (string, error) { + exactLocation, err := filepath.EvalSymlinks(p.Dir) + if err != nil { + return "", err + } + absExactLocation, err := filepath.Abs(exactLocation) + if err != nil { + return "", err + } + + i, err := installer.FindSource(absExactLocation) + if err != nil { + return "", err + } + + return i.GetLatestVersion() +} diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index cc45787bf..2178f1c86 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -155,6 +155,11 @@ func (i *HTTPInstaller) Update() error { return errors.Errorf("method Update() not implemented for HttpInstaller") } +// GetLatestVersion fetches the latest version of the plugin. +func (i *HTTPInstaller) GetLatestVersion() (string, error) { + return "", errors.New("not supported") +} + // Path is overridden because we want to join on the plugin name not the file name func (i HTTPInstaller) Path() string { if i.base.Source == "" { diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index 1e90bcaa0..7f3343a8c 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -40,6 +40,8 @@ type Installer interface { Path() string // Update updates a plugin. Update() error + // GetLatestVersion fetches the latest version of the plugin. + GetLatestVersion() (string, error) } // Install installs a plugin. diff --git a/pkg/plugin/installer/local_installer.go b/pkg/plugin/installer/local_installer.go index 52636d019..9301bb357 100644 --- a/pkg/plugin/installer/local_installer.go +++ b/pkg/plugin/installer/local_installer.go @@ -67,3 +67,7 @@ func (i *LocalInstaller) Update() error { slog.Debug("local repository is auto-updated") return nil } + +func (i *LocalInstaller) GetLatestVersion() (string, error) { + return "", errors.New("not supported") +} diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go index d1b704d6e..0cd8c61b9 100644 --- a/pkg/plugin/installer/vcs_installer.go +++ b/pkg/plugin/installer/vcs_installer.go @@ -108,6 +108,26 @@ func (i *VCSInstaller) Update() error { return nil } +func (i *VCSInstaller) GetLatestVersion() (string, error) { + _, err := i.Repo.RunFromDir("git", "fetch", "-q", "--tags") + if err != nil { + return "", fmt.Errorf("failed to fetch tags: %w", err) + } + + refs, err := i.Repo.Tags() + if err != nil { + return "", fmt.Errorf("failed to get tags: %w", err) + } + + semvers := getSemVers(refs) + if len(semvers) == 0 { + return "", fmt.Errorf("no valid semver tags found in repository") + } + + sort.Sort(sort.Reverse(semver.Collection(semvers))) + return semvers[0].Original(), nil +} + func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) { if i.Version == "" { return "", nil