From 1c561ee2fc4b57933e1391dde814fda4f7da9b87 Mon Sep 17 00:00:00 2001 From: Andre Cloutier Date: Sun, 16 Feb 2025 18:51:59 -0500 Subject: [PATCH 1/2] fix: install should report missing plugins --- internal/versions/versions.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/internal/versions/versions.go b/internal/versions/versions.go index 04544ed52..8da1132c5 100644 --- a/internal/versions/versions.go +++ b/internal/versions/versions.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "os" + "path" "regexp" "strings" @@ -26,6 +27,7 @@ const ( uninstallableVersionMsg = "uninstallable version: %s" latestFilterRegex = "(?i)(^Available versions:|-src|-dev|-latest|-stm|[-\\.]rc|-milestone|-alpha|-beta|[-\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master)" noLatestVersionErrMsg = "no latest version found" + missingPluginErrMsg = "missing plugin for %s" ) // UninstallableVersionError is an error returned if someone tries to install the @@ -44,6 +46,16 @@ type NoVersionSetError struct { toolName string } +// MissingPluginError is returned whenever an operation expects a plugin, +// but it is not installed. +type MissingPluginError struct { + toolName string +} + +func (e MissingPluginError) Error() string { + return fmt.Sprintf(missingPluginErrMsg, e.toolName) +} + func (e NoVersionSetError) Error() string { // Eventually switch this to a more friendly error message, BATS tests fail // with this improvement @@ -61,16 +73,31 @@ func InstallAll(conf config.Config, dir string, stdOut io.Writer, stdErr io.Writ return []error{fmt.Errorf("unable to list plugins: %w", err)} } + toolNames := map[string]bool{} + filepath := path.Join(dir, conf.DefaultToolVersionsFilename) + toolVersions, err := toolversions.GetAllToolsAndVersions(filepath) + if err == nil { + for _, version := range toolVersions { + toolNames[version.Name] = true + } + } + // Ideally we should install these in the order they are specified in the // closest .tool-versions file, but for now that is too complicated to // implement. for _, plugin := range plugins { + delete(toolNames, plugin.Name) err := Install(conf, plugin, dir, stdOut, stdErr) if err != nil { failures = append(failures, err) } } + for toolName := range toolNames { + err = MissingPluginError{toolName: toolName} + failures = append(failures, err) + } + return failures } From 2d9edc89ee6e5f3d967ea47ef0901759f17a8f7d Mon Sep 17 00:00:00 2001 From: Andre Cloutier Date: Sun, 16 Feb 2025 19:20:50 -0500 Subject: [PATCH 2/2] Add a test --- internal/versions/versions_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/internal/versions/versions_test.go b/internal/versions/versions_test.go index f179565ac..402073955 100644 --- a/internal/versions/versions_test.go +++ b/internal/versions/versions_test.go @@ -47,6 +47,7 @@ func TestInstallAll(t *testing.T) { writeVersionFile(t, currentDir, content) err := InstallAll(conf, currentDir, &stdout, &stderr) + assert.Len(t, err, 1, "expected 1 install error") assert.ErrorContains(t, err[0], "no version set") assertVersionInstalled(t, conf.DataDir, plugin.Name, version) @@ -70,6 +71,23 @@ func TestInstallAll(t *testing.T) { assertNotInstalled(t, conf.DataDir, secondPlugin.Name, version) assertVersionInstalled(t, conf.DataDir, plugin.Name, version) }) + + t.Run("reports skipped tools due to missing plugin", func(t *testing.T) { + conf, plugin := generateConfig(t) + stdout, stderr := buildOutputs() + currentDir := t.TempDir() + version := "1.0.0" + + // write a version file + content := fmt.Sprintf("%s %s\n%s %s", plugin.Name, version, "non-existant-tool", version) + writeVersionFile(t, currentDir, content) + + err := InstallAll(conf, currentDir, &stdout, &stderr) + assert.Len(t, err, 1, "expected 1 install error") + assert.ErrorContains(t, err[0], "missing plugin for") + + assertVersionInstalled(t, conf.DataDir, plugin.Name, version) + }) } func TestInstall(t *testing.T) {