mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-19 09:55:01 -07:00
Merge pull request #56 from asdf-vm/tb/latest-version
feat(golang-rewrite): create versions.Latest function
This commit is contained in:
commit
123162946c
@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"asdf/config"
|
"asdf/config"
|
||||||
@ -21,6 +22,9 @@ const (
|
|||||||
uninstallableVersionMsg = "uninstallable version: system"
|
uninstallableVersionMsg = "uninstallable version: system"
|
||||||
dataDirDownloads = "downloads"
|
dataDirDownloads = "downloads"
|
||||||
dataDirInstalls = "installs"
|
dataDirInstalls = "installs"
|
||||||
|
defaultQuery = "[0-9]"
|
||||||
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UninstallableVersion is an error returned if someone tries to install the
|
// UninstallableVersion is an error returned if someone tries to install the
|
||||||
@ -50,8 +54,16 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
|
|||||||
}
|
}
|
||||||
|
|
||||||
if version == latestVersion {
|
if version == latestVersion {
|
||||||
// TODO: Implement this
|
versions, err := Latest(plugin, "")
|
||||||
return errors.New("not implemented")
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(versions) < 1 {
|
||||||
|
return errors.New(noLatestVersionErrMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
version = versions[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadDir := downloadPath(conf, plugin, version)
|
downloadDir := downloadPath(conf, plugin, version)
|
||||||
@ -107,12 +119,92 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadPath(conf config.Config, plugin plugins.Plugin, version string) string {
|
// Latest invokes the plugin's latest-stable callback if it exists and returns
|
||||||
return filepath.Join(conf.DataDir, dataDirDownloads, plugin.Name, version)
|
// 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) (versions []string, err error) {
|
||||||
|
if query == "" {
|
||||||
|
query = defaultQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdOut strings.Builder
|
||||||
|
var stdErr strings.Builder
|
||||||
|
|
||||||
|
err = plugin.RunCallback("latest-stable", []string{query}, map[string]string{}, &stdOut, &stdErr)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(plugins.NoCallbackError); !ok {
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allVersions, err := AllVersionsFiltered(plugin, query)
|
||||||
|
if err != nil {
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
versions = filterByRegex(allVersions, latestFilterRegex, false)
|
||||||
|
|
||||||
|
if len(versions) < 1 {
|
||||||
|
return versions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{versions[len(versions)-1]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse stdOut and return version
|
||||||
|
versions = parseVersions(stdOut.String())
|
||||||
|
return versions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func installPath(conf config.Config, plugin plugins.Plugin, version string) string {
|
// AllVersions returns a slice of all available versions for the tool managed by
|
||||||
return filepath.Join(conf.DataDir, dataDirInstalls, plugin.Name, version)
|
// the given plugin by invoking the plugin's list-all callback
|
||||||
|
func AllVersions(plugin plugins.Plugin) (versions []string, err error) {
|
||||||
|
var stdout strings.Builder
|
||||||
|
var stderr strings.Builder
|
||||||
|
|
||||||
|
err = plugin.RunCallback("list-all", []string{}, map[string]string{}, &stdout, &stderr)
|
||||||
|
if err != nil {
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
versions = parseVersions(stdout.String())
|
||||||
|
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllVersionsFiltered returns a list of existing versions that match a regex
|
||||||
|
// query provided by the user.
|
||||||
|
func AllVersionsFiltered(plugin plugins.Plugin, query string) (versions []string, err error) {
|
||||||
|
all, err := AllVersions(plugin)
|
||||||
|
if err != nil {
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterByRegex(all, query, true), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterByRegex(allVersions []string, pattern string, include bool) (versions []string) {
|
||||||
|
for _, version := range allVersions {
|
||||||
|
match, _ := regexp.MatchString(pattern, version)
|
||||||
|
if match && include || !match && !include {
|
||||||
|
versions = append(versions, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions
|
||||||
|
}
|
||||||
|
|
||||||
|
// future refactoring opportunity: this function is an exact copy of
|
||||||
|
// resolve.parseVersion
|
||||||
|
func parseVersions(rawVersions string) []string {
|
||||||
|
var versions []string
|
||||||
|
for _, version := range strings.Split(rawVersions, " ") {
|
||||||
|
version = strings.TrimSpace(version)
|
||||||
|
if len(version) > 0 {
|
||||||
|
versions = append(versions, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return versions
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseString parses a version string into versionType and version components
|
// ParseString parses a version string into versionType and version components
|
||||||
@ -124,3 +216,11 @@ func ParseString(version string) (string, string) {
|
|||||||
|
|
||||||
return "version", version
|
return "version", version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downloadPath(conf config.Config, plugin plugins.Plugin, version string) string {
|
||||||
|
return filepath.Join(conf.DataDir, dataDirDownloads, plugin.Name, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func installPath(conf config.Config, plugin plugins.Plugin, version string) string {
|
||||||
|
return filepath.Join(conf.DataDir, dataDirInstalls, plugin.Name, version)
|
||||||
|
}
|
||||||
|
@ -32,9 +32,12 @@ func TestInstallOneVersion(t *testing.T) {
|
|||||||
assert.IsType(t, UninstallableVersion{}, err)
|
assert.IsType(t, UninstallableVersion{}, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
//t.Run("installs latest version of tool when version is 'latest'", func(t *testing.T) {
|
t.Run("installs latest version of tool when version is 'latest'", func(t *testing.T) {
|
||||||
// t.Fatal("not implemented")
|
conf, plugin := generateConfig(t)
|
||||||
//})
|
stdout, stderr := buildOutputs()
|
||||||
|
err := InstallOneVersion(conf, plugin, "latest", false, &stdout, &stderr)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("returns error when version doesn't exist", func(t *testing.T) {
|
t.Run("returns error when version doesn't exist", func(t *testing.T) {
|
||||||
version := "other-dummy"
|
version := "other-dummy"
|
||||||
@ -128,6 +131,43 @@ func TestInstallOneVersion(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLatest(t *testing.T) {
|
||||||
|
pluginName := "latest_test"
|
||||||
|
conf, _ := generateConfig(t)
|
||||||
|
_, err := repotest.InstallPlugin("dummy_legacy_plugin", conf.DataDir, pluginName)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
plugin := plugins.New(conf, pluginName)
|
||||||
|
|
||||||
|
t.Run("when plugin has a latest-stable callback invokes it and returns version it printed", func(t *testing.T) {
|
||||||
|
pluginName := "latest-with-callback"
|
||||||
|
_, err := repotest.InstallPlugin("dummy_plugin", conf.DataDir, pluginName)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
plugin := plugins.New(conf, pluginName)
|
||||||
|
|
||||||
|
versions, err := Latest(plugin, "")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, []string{"2.0.0"}, versions)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when given query matching no versions return empty slice of versions", func(t *testing.T) {
|
||||||
|
versions, err := Latest(plugin, "impossible-to-satisfy-query")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Empty(t, versions)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when given no query returns latest version of plugin", func(t *testing.T) {
|
||||||
|
versions, err := Latest(plugin, "")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, []string{"5.1.0"}, versions)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when given no query returns latest version of plugin", func(t *testing.T) {
|
||||||
|
versions, err := Latest(plugin, "^4")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, []string{"4.0.0"}, versions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseString(t *testing.T) {
|
func TestParseString(t *testing.T) {
|
||||||
t.Run("returns 'version', and unmodified version when passed semantic version", func(t *testing.T) {
|
t.Run("returns 'version', and unmodified version when passed semantic version", func(t *testing.T) {
|
||||||
versionType, version := ParseString("1.2.3")
|
versionType, version := ParseString("1.2.3")
|
||||||
@ -148,6 +188,31 @@ func TestParseString(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAllVersions(t *testing.T) {
|
||||||
|
pluginName := "list-all-test"
|
||||||
|
conf, _ := generateConfig(t)
|
||||||
|
_, err := repotest.InstallPlugin("dummy_plugin", conf.DataDir, pluginName)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
plugin := plugins.New(conf, pluginName)
|
||||||
|
|
||||||
|
t.Run("returns slice of available versions from plugin", func(t *testing.T) {
|
||||||
|
versions, err := AllVersions(plugin)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, versions, []string{"1.0.0", "1.1.0", "2.0.0"})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("returns error when callback missing", func(t *testing.T) {
|
||||||
|
pluginName = "list-all-fail"
|
||||||
|
_, err := repotest.InstallPlugin("dummy_plugin_no_download", conf.DataDir, pluginName)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
plugin := plugins.New(conf, pluginName)
|
||||||
|
|
||||||
|
versions, err := AllVersions(plugin)
|
||||||
|
assert.Equal(t, err.(plugins.NoCallbackError).Error(), "Plugin named list-all-fail does not have a callback named list-all")
|
||||||
|
assert.Empty(t, versions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
func buildOutputs() (strings.Builder, strings.Builder) {
|
func buildOutputs() (strings.Builder, strings.Builder) {
|
||||||
var stdout strings.Builder
|
var stdout strings.Builder
|
||||||
|
Loading…
Reference in New Issue
Block a user