diff --git a/internal/cli/cli.go b/internal/cli/cli.go index e583661f7..aa9ccb858 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -1165,7 +1165,7 @@ func latestCommand(logger *log.Logger, all bool, toolName, pattern string) (err } if !all { - err = latestForPlugin(conf, toolName, pattern, false) + err = latestForPlugin(conf, toolName, pattern, false, os.Stderr) if err != nil { cli.OsExiter(1) } @@ -1182,7 +1182,7 @@ func latestCommand(logger *log.Logger, all bool, toolName, pattern string) (err var maybeErr error // loop over all plugins and show latest for each one. for _, plugin := range plugins { - maybeErr = latestForPlugin(conf, plugin.Name, "", true) + maybeErr = latestForPlugin(conf, plugin.Name, "", true, os.Stderr) if maybeErr != nil { err = maybeErr } @@ -1550,10 +1550,10 @@ func reshimToolVersion(conf config.Config, plugin plugins.Plugin, versionStr str return shims.GenerateForVersion(conf, plugin, version, out, errOut) } -func latestForPlugin(conf config.Config, toolName, pattern string, showStatus bool) error { +func latestForPlugin(conf config.Config, toolName, pattern string, showStatus bool, errOut io.Writer) error { // show single plugin plugin := plugins.New(conf, toolName) - latest, err := versions.Latest(plugin, pattern) + latest, err := versions.Latest(plugin, pattern, errOut) if err != nil && err.Error() != "no latest version found" { fmt.Printf("unable to load latest version: %s\n", err) return err diff --git a/internal/cli/set/set.go b/internal/cli/set/set.go index 407872e81..de22b5796 100644 --- a/internal/cli/set/set.go +++ b/internal/cli/set/set.go @@ -40,7 +40,7 @@ func Main(_ io.Writer, stderr io.Writer, args []string, home bool, parent bool, parsedVersion := toolversions.ParseFromCliArg(version) if parsedVersion.Type == "latest" { plugin := plugins.New(conf, args[0]) - resolvedVersion, err := versions.Latest(plugin, parsedVersion.Value) + resolvedVersion, err := versions.Latest(plugin, parsedVersion.Value, os.Stderr) if err != nil { return fmt.Errorf("unable to resolve latest version for %s", plugin.Name) } diff --git a/internal/versions/versions.go b/internal/versions/versions.go index 2cf669928..5e2f640bc 100644 --- a/internal/versions/versions.go +++ b/internal/versions/versions.go @@ -128,7 +128,7 @@ func InstallVersion(conf config.Config, plugin plugins.Plugin, version toolversi resolvedVersion := "" if version.Type == latestVersion { - resolvedVersion, err = Latest(plugin, version.Value) + resolvedVersion, err = Latest(plugin, version.Value, stdErr) if err != nil { return err } @@ -235,11 +235,10 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, versionStr str // the version it returns. If the callback is missing it invokes the list-all // callback and returns the last version matching the query, if a query is // provided. -func Latest(plugin plugins.Plugin, query string) (version string, err error) { +func Latest(plugin plugins.Plugin, query string, stdErr io.Writer) (version string, err error) { var stdOut strings.Builder - var stdErr strings.Builder - err = plugin.RunCallback("latest-stable", []string{query}, map[string]string{}, &stdOut, &stdErr) + err = plugin.RunCallback("latest-stable", []string{query}, map[string]string{}, &stdOut, stdErr) if err == nil { versions := parseVersions(stdOut.String()) if len(versions) < 1 { @@ -253,7 +252,7 @@ func Latest(plugin plugins.Plugin, query string) (version string, err error) { return version, err } - allVersions, err := AllVersions(plugin) + allVersions, err := AllVersions(plugin, stdErr) if err != nil { return version, err } @@ -276,11 +275,10 @@ func Latest(plugin plugins.Plugin, query string) (version string, err error) { // AllVersions returns a slice of all available versions for the tool managed by // the given plugin by invoking the plugin's list-all callback -func AllVersions(plugin plugins.Plugin) (versions []string, err error) { +func AllVersions(plugin plugins.Plugin, stdErr io.Writer) (versions []string, err error) { var stdout strings.Builder - var stderr strings.Builder - err = plugin.RunCallback("list-all", []string{}, map[string]string{}, &stdout, &stderr) + err = plugin.RunCallback("list-all", []string{}, map[string]string{}, &stdout, stdErr) if err != nil { return versions, err } diff --git a/internal/versions/versions_test.go b/internal/versions/versions_test.go index bb5a93874..1affad3fb 100644 --- a/internal/versions/versions_test.go +++ b/internal/versions/versions_test.go @@ -2,6 +2,7 @@ package versions import ( "fmt" + "io" "os" "path/filepath" "strings" @@ -334,7 +335,7 @@ func TestLatest(t *testing.T) { assert.Nil(t, err) plugin := plugins.New(conf, pluginName) - version, err := Latest(plugin, "") + version, err := Latest(plugin, "", io.Discard) assert.Nil(t, err) assert.Equal(t, "2.0.0", version) }) @@ -350,25 +351,25 @@ func TestLatest(t *testing.T) { err = os.WriteFile(latestScript, []byte("#!/usr/bin/env bash\necho 1.2.3-dev"), 0o777) assert.Nil(t, err) - version, err := Latest(plugin, "") + version, err := Latest(plugin, "", io.Discard) assert.Nil(t, err) assert.Equal(t, "1.2.3-dev", version) }) t.Run("when given query matching no versions return empty slice of versions", func(t *testing.T) { - version, err := Latest(plugin, "impossible-to-satisfy-query") + version, err := Latest(plugin, "impossible-to-satisfy-query", io.Discard) assert.Error(t, err, "no latest version found") assert.Equal(t, version, "") }) t.Run("when given no query returns latest version of plugin", func(t *testing.T) { - version, err := Latest(plugin, "") + version, err := Latest(plugin, "", io.Discard) assert.Nil(t, err) assert.Equal(t, "5.1.0", version) }) t.Run("when given no query returns latest version of plugin", func(t *testing.T) { - version, err := Latest(plugin, "4") + version, err := Latest(plugin, "4", io.Discard) assert.Nil(t, err) assert.Equal(t, "4.0.0", version) }) @@ -387,7 +388,7 @@ func TestLatest(t *testing.T) { err = os.Remove(latestScript) assert.Nil(t, err) - version, err := Latest(plugin, "") + version, err := Latest(plugin, "", io.Discard) assert.Nil(t, err) assert.Equal(t, "3.4.5", version) }) @@ -424,7 +425,7 @@ func TestLatestWithSamples(t *testing.T) { assert.Nil(t, err) plugin := plugins.New(conf, pluginName) - version, err := Latest(plugin, "") + version, err := Latest(plugin, "", io.Discard) assert.Nil(t, err) assert.Equal(t, tt.expectedOutput, version) } @@ -438,7 +439,7 @@ func TestAllVersions(t *testing.T) { plugin := plugins.New(conf, pluginName) t.Run("returns slice of available versions from plugin", func(t *testing.T) { - versions, err := AllVersions(plugin) + versions, err := AllVersions(plugin, io.Discard) assert.Nil(t, err) assert.Equal(t, versions, []string{"1.0.0", "1.1.0", "2.0.0"}) }) @@ -449,10 +450,24 @@ func TestAllVersions(t *testing.T) { assert.Nil(t, err) plugin := plugins.New(conf, pluginName) - versions, err := AllVersions(plugin) + versions, err := AllVersions(plugin, io.Discard) assert.Equal(t, err.(plugins.NoCallbackError).Error(), "Plugin named list-all-fail does not have a callback named list-all") assert.Empty(t, versions) }) + + t.Run("returns error when callback fails", func(t *testing.T) { + pluginName = "list-all-stderr" + _, err := repotest.InstallPlugin("dummy_broken_plugin", conf.DataDir, pluginName) + assert.Nil(t, err) + plugin := plugins.New(conf, pluginName) + + var stdErr strings.Builder + + versions, err := AllVersions(plugin, &stdErr) + assert.Equal(t, "exit status 1", err.Error()) + assert.Empty(t, versions) + assert.Equal(t, "List-all failed!\n", stdErr.String()) + }) } func TestUninstall(t *testing.T) {