mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-23 20:05:09 -07:00
Merge pull request #53 from asdf-vm/tb/full-version-resolution
feat(golang-rewrite): full version resolution
This commit is contained in:
commit
ef91474538
@ -4,6 +4,7 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -29,6 +30,19 @@ type PluginRepoCheckDuration struct {
|
||||
|
||||
var pluginRepoCheckDurationDefault = PluginRepoCheckDuration{Every: 60}
|
||||
|
||||
// Config is the primary value this package builds and returns
|
||||
type Config struct {
|
||||
Home string
|
||||
ConfigFile string `env:"ASDF_CONFIG_FILE, overwrite"`
|
||||
DefaultToolVersionsFilename string `env:"ASDF_DEFAULT_TOOL_VERSIONS_FILENAME, overwrite"`
|
||||
// Unclear if this value will be needed with the golang implementation.
|
||||
// AsdfDir string
|
||||
DataDir string `env:"ASDF_DATA_DIR, overwrite"`
|
||||
ForcePrepend bool `env:"ASDF_FORCE_PREPEND, overwrite"`
|
||||
// Field that stores the settings struct if it is loaded
|
||||
Settings Settings
|
||||
}
|
||||
|
||||
// Settings is a struct that stores config values from the asdfrc file
|
||||
type Settings struct {
|
||||
Loaded bool
|
||||
@ -41,17 +55,13 @@ type Settings struct {
|
||||
DisablePluginShortNameRepository bool
|
||||
}
|
||||
|
||||
// Config is the primary value this package builds and returns
|
||||
type Config struct {
|
||||
Home string
|
||||
ConfigFile string `env:"ASDF_CONFIG_FILE, overwrite"`
|
||||
DefaultToolVersionsFilename string `env:"ASDF_DEFAULT_TOOL_VERSIONS_FILENAME, overwrite"`
|
||||
// Unclear if this value will be needed with the golang implementation.
|
||||
// AsdfDir string
|
||||
DataDir string `env:"ASDF_DATA_DIR, overwrite"`
|
||||
ForcePrepend bool `env:"ASDF_FORCE_PREPEND, overwrite"`
|
||||
// Field that stores the settings struct if it is loaded
|
||||
Settings Settings
|
||||
func defaultConfig(dataDir, configFile string) *Config {
|
||||
return &Config{
|
||||
ForcePrepend: forcePrependDefault,
|
||||
DataDir: dataDir,
|
||||
ConfigFile: configFile,
|
||||
DefaultToolVersionsFilename: defaultToolVersionsFilenameDefault,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultSettings() *Settings {
|
||||
@ -151,7 +161,11 @@ func (c *Config) GetHook(hook string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return c.Settings.Raw.Key(hook).String(), nil
|
||||
if c.Settings.Raw != nil {
|
||||
return c.Settings.Raw.Key(hook).String(), nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (c *Config) loadSettings() error {
|
||||
@ -160,12 +174,18 @@ func (c *Config) loadSettings() error {
|
||||
}
|
||||
|
||||
settings, err := loadSettings(c.ConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Settings = settings
|
||||
|
||||
if err != nil {
|
||||
_, ok := err.(*fs.PathError)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -180,29 +200,25 @@ func loadConfigEnv() (Config, error) {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
config := Config{
|
||||
ForcePrepend: forcePrependDefault,
|
||||
DataDir: dataDir,
|
||||
ConfigFile: configFile,
|
||||
DefaultToolVersionsFilename: defaultToolVersionsFilenameDefault,
|
||||
}
|
||||
config := defaultConfig(dataDir, configFile)
|
||||
|
||||
context := context.Background()
|
||||
err = envconfig.Process(context, &config)
|
||||
err = envconfig.Process(context, config)
|
||||
|
||||
return config, err
|
||||
return *config, err
|
||||
}
|
||||
|
||||
func loadSettings(asdfrcPath string) (Settings, error) {
|
||||
settings := defaultSettings()
|
||||
|
||||
// asdfrc is effectively formatted as ini
|
||||
config, err := ini.Load(asdfrcPath)
|
||||
if err != nil {
|
||||
return Settings{}, err
|
||||
return *settings, err
|
||||
}
|
||||
|
||||
mainConf := config.Section("")
|
||||
|
||||
settings := defaultSettings()
|
||||
settings.Raw = mainConf
|
||||
|
||||
settings.Loaded = true
|
||||
|
@ -92,6 +92,30 @@ func TestConfigMethods(t *testing.T) {
|
||||
assert.Nil(t, err, "Returned error when loading settings")
|
||||
assert.True(t, DisablePluginShortNameRepository, "Expected DisablePluginShortNameRepository to be set")
|
||||
})
|
||||
|
||||
t.Run("When file does not exist returns settings struct with defaults", func(t *testing.T) {
|
||||
config := Config{ConfigFile: "non-existant"}
|
||||
|
||||
legacy, err := config.LegacyVersionFile()
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, legacy)
|
||||
|
||||
keepDownload, err := config.AlwaysKeepDownload()
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, keepDownload)
|
||||
|
||||
lastCheck, err := config.PluginRepositoryLastCheckDuration()
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, lastCheck.Never)
|
||||
|
||||
checkDuration, err := config.PluginRepositoryLastCheckDuration()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, checkDuration.Every, 60)
|
||||
|
||||
shortName, err := config.DisablePluginShortNameRepository()
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, shortName)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConfigGetHook(t *testing.T) {
|
||||
@ -124,4 +148,12 @@ func TestConfigGetHook(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, hookCmd, "echo 'Executing' \"with args: $@\"")
|
||||
})
|
||||
|
||||
t.Run("works if no config file", func(t *testing.T) {
|
||||
config := Config{}
|
||||
|
||||
hookCmd, err := config.GetHook("some_hook")
|
||||
assert.Nil(t, err)
|
||||
assert.Empty(t, hookCmd)
|
||||
})
|
||||
}
|
||||
|
@ -15,19 +15,55 @@ import (
|
||||
|
||||
// ToolVersions represents a tool along with versions specified for it
|
||||
type ToolVersions struct {
|
||||
Name string
|
||||
Versions []string
|
||||
Source string
|
||||
Versions []string
|
||||
Directory string
|
||||
Source string
|
||||
}
|
||||
|
||||
func findVersionsInDir(conf config.Config, plugin plugins.Plugin, directory string) (versions []string, found bool, err error) {
|
||||
filename := conf.DefaultToolVersionsFilename
|
||||
filepath := path.Join(directory, filename)
|
||||
// Version takes a plugin and a directory and resolves the tool to one or more
|
||||
// versions.
|
||||
func Version(conf config.Config, plugin plugins.Plugin, directory string) (versions ToolVersions, found bool, err error) {
|
||||
version, envVariableName, found := findVersionsInEnv(plugin.Name)
|
||||
if found {
|
||||
return ToolVersions{Versions: version, Source: envVariableName}, true, nil
|
||||
}
|
||||
|
||||
for !found {
|
||||
versions, found, err = findVersionsInDir(conf, plugin, directory)
|
||||
if err != nil {
|
||||
return versions, false, err
|
||||
}
|
||||
|
||||
nextDir := path.Dir(directory)
|
||||
if nextDir == directory {
|
||||
break
|
||||
}
|
||||
directory = nextDir
|
||||
}
|
||||
|
||||
return versions, found, err
|
||||
}
|
||||
|
||||
func findVersionsInDir(conf config.Config, plugin plugins.Plugin, directory string) (versions ToolVersions, found bool, err error) {
|
||||
legacyFiles, err := conf.LegacyVersionFile()
|
||||
if err != nil {
|
||||
return versions, found, err
|
||||
}
|
||||
|
||||
if legacyFiles {
|
||||
versions, found, err := findVersionsInLegacyFile(plugin, directory)
|
||||
|
||||
if found || err != nil {
|
||||
return versions, found, err
|
||||
}
|
||||
}
|
||||
|
||||
filepath := path.Join(directory, conf.DefaultToolVersionsFilename)
|
||||
|
||||
if _, err = os.Stat(filepath); err == nil {
|
||||
versions, found, err := toolversions.FindToolVersions(filepath, plugin.Name)
|
||||
if found || err != nil {
|
||||
return versions, found, err
|
||||
return ToolVersions{Versions: versions, Source: conf.DefaultToolVersionsFilename, Directory: directory}, found, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,36 +71,36 @@ func findVersionsInDir(conf config.Config, plugin plugins.Plugin, directory stri
|
||||
}
|
||||
|
||||
// findVersionsInEnv returns the version from the environment if present
|
||||
func findVersionsInEnv(pluginName string) ([]string, bool) {
|
||||
func findVersionsInEnv(pluginName string) ([]string, string, bool) {
|
||||
envVariableName := "ASDF_" + strings.ToUpper(pluginName) + "_VERSION"
|
||||
versionString := os.Getenv(envVariableName)
|
||||
if versionString == "" {
|
||||
return []string{}, false
|
||||
return []string{}, envVariableName, false
|
||||
}
|
||||
return parseVersion(versionString), true
|
||||
return parseVersion(versionString), envVariableName, true
|
||||
}
|
||||
|
||||
// findVersionsInLegacyFile looks up a legacy version in the given directory if
|
||||
// the specified plugin has a list-legacy-filenames callback script. If the
|
||||
// callback script exists asdf will look for files with the given name in the
|
||||
// current and extract the version from them.
|
||||
func findVersionsInLegacyFile(plugin plugins.Plugin, directory string) (versions []string, found bool, err error) {
|
||||
func findVersionsInLegacyFile(plugin plugins.Plugin, directory string) (versions ToolVersions, found bool, err error) {
|
||||
var legacyFileNames []string
|
||||
|
||||
legacyFileNames, err = plugin.LegacyFilenames()
|
||||
if err != nil {
|
||||
return []string{}, false, err
|
||||
return versions, false, err
|
||||
}
|
||||
|
||||
for _, filename := range legacyFileNames {
|
||||
filepath := path.Join(directory, filename)
|
||||
if _, err := os.Stat(filepath); err == nil {
|
||||
versions, err := plugin.ParseLegacyVersionFile(filepath)
|
||||
versionsSlice, err := plugin.ParseLegacyVersionFile(filepath)
|
||||
|
||||
if len(versions) == 0 || (len(versions) == 1 && versions[0] == "") {
|
||||
return nil, false, nil
|
||||
if len(versionsSlice) == 0 || (len(versionsSlice) == 1 && versionsSlice[0] == "") {
|
||||
return versions, false, nil
|
||||
}
|
||||
return versions, err == nil, err
|
||||
return ToolVersions{Versions: versionsSlice, Source: filename, Directory: directory}, err == nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,66 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
testDataDir := t.TempDir()
|
||||
currentDir := t.TempDir()
|
||||
conf := config.Config{DataDir: testDataDir, DefaultToolVersionsFilename: ".tool-versions", ConfigFile: "testdata/asdfrc"}
|
||||
_, err := repotest.InstallPlugin("dummy_plugin", conf.DataDir, "lua")
|
||||
assert.Nil(t, err)
|
||||
plugin := plugins.New(conf, "lua")
|
||||
|
||||
t.Run("returns empty slice when non-existent version passed", func(t *testing.T) {
|
||||
toolVersion, found, err := Version(conf, plugin, t.TempDir())
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, found)
|
||||
assert.Empty(t, toolVersion.Versions)
|
||||
})
|
||||
|
||||
t.Run("returns single version from .tool-versions file", func(t *testing.T) {
|
||||
// write a version file
|
||||
data := []byte("lua 1.2.3")
|
||||
err = os.WriteFile(filepath.Join(currentDir, ".tool-versions"), data, 0o666)
|
||||
|
||||
toolVersion, found, err := Version(conf, plugin, currentDir)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, toolVersion.Versions, []string{"1.2.3"})
|
||||
})
|
||||
|
||||
t.Run("returns version from env when env variable set", func(t *testing.T) {
|
||||
// Set env
|
||||
t.Setenv("ASDF_LUA_VERSION", "2.3.4")
|
||||
|
||||
// write a version file
|
||||
data := []byte("lua 1.2.3")
|
||||
err = os.WriteFile(filepath.Join(currentDir, ".tool-versions"), data, 0o666)
|
||||
|
||||
// assert env variable takes precedence
|
||||
toolVersion, found, err := Version(conf, plugin, currentDir)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, toolVersion.Versions, []string{"2.3.4"})
|
||||
})
|
||||
|
||||
t.Run("returns single version from .tool-versions file in parent directory", func(t *testing.T) {
|
||||
// write a version file
|
||||
data := []byte("lua 1.2.3")
|
||||
err = os.WriteFile(filepath.Join(currentDir, ".tool-versions"), data, 0o666)
|
||||
|
||||
subDir := filepath.Join(currentDir, "subdir")
|
||||
err = os.MkdirAll(subDir, 0o777)
|
||||
assert.Nil(t, err)
|
||||
|
||||
toolVersion, found, err := Version(conf, plugin, subDir)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, toolVersion.Versions, []string{"1.2.3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestFindVersionsInDir(t *testing.T) {
|
||||
testDataDir := t.TempDir()
|
||||
conf := config.Config{DataDir: testDataDir, DefaultToolVersionsFilename: ".tool-versions"}
|
||||
conf := config.Config{DataDir: testDataDir, DefaultToolVersionsFilename: ".tool-versions", ConfigFile: "testdata/asdfrc"}
|
||||
_, err := repotest.InstallPlugin("dummy_plugin", conf.DataDir, "lua")
|
||||
assert.Nil(t, err)
|
||||
plugin := plugins.New(conf, "lua")
|
||||
@ -35,9 +92,9 @@ func TestFindVersionsInDir(t *testing.T) {
|
||||
data := []byte("lua 1.2.3")
|
||||
err = os.WriteFile(filepath.Join(currentDir, ".tool-versions"), data, 0o666)
|
||||
|
||||
versions, found, err := findVersionsInDir(conf, plugin, currentDir)
|
||||
toolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)
|
||||
|
||||
assert.Equal(t, versions, []string{"1.2.3"})
|
||||
assert.Equal(t, toolVersion.Versions, []string{"1.2.3"})
|
||||
assert.True(t, found)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
@ -48,9 +105,9 @@ func TestFindVersionsInDir(t *testing.T) {
|
||||
data := []byte("lua 1.2.3 2.3.4")
|
||||
err = os.WriteFile(filepath.Join(currentDir, ".tool-versions"), data, 0o666)
|
||||
|
||||
versions, found, err := findVersionsInDir(conf, plugin, currentDir)
|
||||
toolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)
|
||||
|
||||
assert.Equal(t, versions, []string{"1.2.3", "2.3.4"})
|
||||
assert.Equal(t, toolVersion.Versions, []string{"1.2.3", "2.3.4"})
|
||||
assert.True(t, found)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
@ -62,9 +119,22 @@ func TestFindVersionsInDir(t *testing.T) {
|
||||
data := []byte("lua 1.2.3 2.3.4")
|
||||
err = os.WriteFile(filepath.Join(currentDir, "custom-file"), data, 0o666)
|
||||
|
||||
versions, found, err := findVersionsInDir(conf, plugin, currentDir)
|
||||
toolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)
|
||||
|
||||
assert.Equal(t, versions, []string{"1.2.3", "2.3.4"})
|
||||
assert.Equal(t, toolVersion.Versions, []string{"1.2.3", "2.3.4"})
|
||||
assert.True(t, found)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("when legacy file support is on looks up version in legacy file", func(t *testing.T) {
|
||||
currentDir := t.TempDir()
|
||||
|
||||
data := []byte("1.2.3 2.3.4")
|
||||
err = os.WriteFile(filepath.Join(currentDir, ".dummy-version"), data, 0o666)
|
||||
|
||||
toolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)
|
||||
|
||||
assert.Equal(t, toolVersion.Versions, []string{"1.2.3", "2.3.4"})
|
||||
assert.True(t, found)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
@ -82,15 +152,15 @@ func TestFindVersionsLegacyFiles(t *testing.T) {
|
||||
_, err := repotest.InstallPlugin("dummy_plugin_no_download", conf.DataDir, pluginName)
|
||||
assert.Nil(t, err)
|
||||
plugin := plugins.New(conf, pluginName)
|
||||
versions, found, err := findVersionsInLegacyFile(plugin, t.TempDir())
|
||||
assert.Empty(t, versions)
|
||||
toolVersion, found, err := findVersionsInLegacyFile(plugin, t.TempDir())
|
||||
assert.Empty(t, toolVersion.Versions)
|
||||
assert.False(t, found)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("when given tool that has a list-legacy-filenames callback but file not found returns empty versions list", func(t *testing.T) {
|
||||
versions, found, err := findVersionsInLegacyFile(plugin, t.TempDir())
|
||||
assert.Empty(t, versions)
|
||||
toolVersion, found, err := findVersionsInLegacyFile(plugin, t.TempDir())
|
||||
assert.Empty(t, toolVersion.Versions)
|
||||
assert.False(t, found)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
@ -102,8 +172,8 @@ func TestFindVersionsLegacyFiles(t *testing.T) {
|
||||
err = os.WriteFile(filepath.Join(currentDir, ".dummy-version"), data, 0o666)
|
||||
assert.Nil(t, err)
|
||||
|
||||
versions, found, err := findVersionsInLegacyFile(plugin, currentDir)
|
||||
assert.Equal(t, versions, []string{"1.2.3"})
|
||||
toolVersion, found, err := findVersionsInLegacyFile(plugin, currentDir)
|
||||
assert.Equal(t, toolVersion.Versions, []string{"1.2.3"})
|
||||
assert.True(t, found)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
@ -111,24 +181,28 @@ func TestFindVersionsLegacyFiles(t *testing.T) {
|
||||
|
||||
func TestFindVersionsInEnv(t *testing.T) {
|
||||
t.Run("when env variable isn't set returns empty list of versions", func(t *testing.T) {
|
||||
versions, found := findVersionsInEnv("non-existent")
|
||||
versions, envVariableName, found := findVersionsInEnv("non-existent")
|
||||
assert.False(t, found)
|
||||
assert.Empty(t, versions)
|
||||
assert.Equal(t, envVariableName, "ASDF_NON-EXISTENT_VERSION")
|
||||
})
|
||||
|
||||
t.Run("when env variable is set returns version", func(t *testing.T) {
|
||||
os.Setenv("ASDF_LUA_VERSION", "5.4.5")
|
||||
versions, found := findVersionsInEnv("lua")
|
||||
versions, envVariableName, found := findVersionsInEnv("lua")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, versions, []string{"5.4.5"})
|
||||
assert.Equal(t, envVariableName, "ASDF_LUA_VERSION")
|
||||
|
||||
os.Unsetenv("ASDF_LUA_VERSION")
|
||||
})
|
||||
|
||||
t.Run("when env variable is set to multiple versions", func(t *testing.T) {
|
||||
os.Setenv("ASDF_LUA_VERSION", "5.4.5 5.4.6")
|
||||
versions, found := findVersionsInEnv("lua")
|
||||
versions, envVariableName, found := findVersionsInEnv("lua")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, versions, []string{"5.4.5", "5.4.6"})
|
||||
assert.Equal(t, envVariableName, "ASDF_LUA_VERSION")
|
||||
os.Unsetenv("ASDF_LUA_VERSION")
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user