mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-23 11:55:13 -07:00
feat(golang-rewrite): introduce Version
struct, get some shim_exec.bats
tests passing
* Get more shim_exec.bats tests passing by adding shebang lines to test scripts * Disable shim_exec test case for scenario that is no longer possible * Add documentation on another breaking change * Create toolversions.Version struct and update code to use new struct
This commit is contained in:
parent
9d9fc698f4
commit
924eecfa6a
94
cmd/cmd.go
94
cmd/cmd.go
@ -299,8 +299,8 @@ func getVersionInfo(conf config.Config, plugin plugins.Plugin, currentDir string
|
||||
installed := false
|
||||
if found {
|
||||
firstVersion := toolversion.Versions[0]
|
||||
versionType, version := toolversions.Parse(firstVersion)
|
||||
installed = installs.IsInstalled(conf, plugin, versionType, version)
|
||||
version := toolversions.Parse(firstVersion)
|
||||
installed = installs.IsInstalled(conf, plugin, version)
|
||||
}
|
||||
return toolversion, found, installed
|
||||
}
|
||||
@ -354,13 +354,41 @@ func execCommand(logger *log.Logger, command string, args []string) error {
|
||||
}
|
||||
|
||||
executable, found, err := shims.FindExecutable(conf, command, currentDir)
|
||||
|
||||
if err != nil {
|
||||
logger.Printf("executable not found due to reason: %s", err.Error())
|
||||
|
||||
shimPath := shims.Path(conf, command)
|
||||
toolVersions, _ := shims.GetToolsAndVersionsFromShimFile(shimPath)
|
||||
|
||||
if len(toolVersions) > 0 {
|
||||
if anyInstalled(conf, toolVersions) {
|
||||
logger.Printf("No version is set for command %s", command)
|
||||
logger.Printf("Consider adding one of the following versions in your config file at %s/.tool-versions\n", currentDir)
|
||||
} else {
|
||||
logger.Printf("No preset version installed for command %s", command)
|
||||
for _, toolVersion := range toolVersions {
|
||||
for _, version := range toolVersion.Versions {
|
||||
fmt.Printf("asdf install %s %s\n", toolVersion.Name, version)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("or add one of the following versions in your config file at %s/.tool-versions\n", currentDir)
|
||||
}
|
||||
|
||||
for _, toolVersion := range toolVersions {
|
||||
for _, version := range toolVersion.Versions {
|
||||
fmt.Printf("%s %s", toolVersion.Name, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(126)
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
logger.Print("executable not found")
|
||||
os.Exit(126)
|
||||
return fmt.Errorf("executable not found")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
@ -372,6 +400,19 @@ func execCommand(logger *log.Logger, command string, args []string) error {
|
||||
return exec.Exec(executable, args, os.Environ())
|
||||
}
|
||||
|
||||
func anyInstalled(conf config.Config, toolVersions []toolversions.ToolVersions) bool {
|
||||
for _, toolVersion := range toolVersions {
|
||||
for _, version := range toolVersion.Versions {
|
||||
version := toolversions.Parse(version)
|
||||
plugin := plugins.New(conf, toolVersion.Name)
|
||||
if installs.IsInstalled(conf, plugin, version) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func pluginAddCommand(_ *cli.Context, conf config.Config, logger *log.Logger, pluginName, pluginRepo string) error {
|
||||
if pluginName == "" {
|
||||
// Invalid arguments
|
||||
@ -577,10 +618,10 @@ func installCommand(logger *log.Logger, toolName, version string) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
parsedVersion, query := toolversions.ParseFromCliArg(version)
|
||||
parsedVersion := toolversions.ParseFromCliArg(version)
|
||||
|
||||
if parsedVersion == "latest" {
|
||||
err = versions.InstallVersion(conf, plugin, version, query, os.Stdout, os.Stderr)
|
||||
if parsedVersion.Type == "latest" {
|
||||
err = versions.InstallVersion(conf, plugin, version, parsedVersion.Value, os.Stdout, os.Stderr)
|
||||
} else {
|
||||
err = versions.InstallOneVersion(conf, plugin, version, os.Stdout, os.Stderr)
|
||||
}
|
||||
@ -890,7 +931,7 @@ func uninstallCommand(logger *log.Logger, tool, version string) error {
|
||||
return shims.GenerateAll(conf, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
func whereCommand(logger *log.Logger, tool, version string) error {
|
||||
func whereCommand(logger *log.Logger, tool, versionStr string) error {
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Printf("error loading config: %s", err)
|
||||
@ -912,20 +953,28 @@ func whereCommand(logger *log.Logger, tool, version string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
versionType, parsedVersion := toolversions.Parse(version)
|
||||
version := toolversions.Parse(versionStr)
|
||||
|
||||
if version == "" {
|
||||
if version.Type == "system" {
|
||||
logger.Printf("System version is selected")
|
||||
return errors.New("System version is selected")
|
||||
}
|
||||
|
||||
if version.Value == "" {
|
||||
// resolve version
|
||||
toolversions, found, err := resolve.Version(conf, plugin, currentDir)
|
||||
versions, found, err := resolve.Version(conf, plugin, currentDir)
|
||||
if err != nil {
|
||||
fmt.Printf("err %#+v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if found && len(toolversions.Versions) > 0 && installs.IsInstalled(conf, plugin, "version", toolversions.Versions[0]) {
|
||||
installPath := installs.InstallPath(conf, plugin, "version", toolversions.Versions[0])
|
||||
logger.Printf("%s", installPath)
|
||||
return nil
|
||||
if found && len(versions.Versions) > 0 {
|
||||
versionStruct := toolversions.Version{Type: "version", Value: versions.Versions[0]}
|
||||
if installs.IsInstalled(conf, plugin, versionStruct) {
|
||||
installPath := installs.InstallPath(conf, plugin, versionStruct)
|
||||
logger.Printf("%s", installPath)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
@ -934,25 +983,20 @@ func whereCommand(logger *log.Logger, tool, version string) error {
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
if version == "system" {
|
||||
logger.Printf("System version is selected")
|
||||
return errors.New("System version is selected")
|
||||
}
|
||||
|
||||
if !installs.IsInstalled(conf, plugin, versionType, parsedVersion) {
|
||||
if !installs.IsInstalled(conf, plugin, version) {
|
||||
logger.Printf("Version not installed")
|
||||
return errors.New("Version not installed")
|
||||
}
|
||||
|
||||
installPath := installs.InstallPath(conf, plugin, versionType, parsedVersion)
|
||||
installPath := installs.InstallPath(conf, plugin, version)
|
||||
logger.Printf("%s", installPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func reshimToolVersion(conf config.Config, tool, version string, out io.Writer, errOut io.Writer) error {
|
||||
versionType, version := toolversions.Parse(version)
|
||||
return shims.GenerateForVersion(conf, plugins.New(conf, tool), versionType, version, out, errOut)
|
||||
func reshimToolVersion(conf config.Config, tool, versionStr string, out io.Writer, errOut io.Writer) error {
|
||||
version := toolversions.Parse(versionStr)
|
||||
return shims.GenerateForVersion(conf, plugins.New(conf, tool), version.Type, version.Value, out, errOut)
|
||||
}
|
||||
|
||||
func latestForPlugin(conf config.Config, toolName, pattern string, showStatus bool) error {
|
||||
@ -971,7 +1015,7 @@ func latestForPlugin(conf config.Config, toolName, pattern string, showStatus bo
|
||||
}
|
||||
|
||||
if showStatus {
|
||||
installed := installs.IsInstalled(conf, plugin, "version", latest)
|
||||
installed := installs.IsInstalled(conf, plugin, toolversions.Version{Type: "version", Value: latest})
|
||||
fmt.Printf("%s\t%s\t%s\n", plugin.Name, latest, installedStatus(installed))
|
||||
} else {
|
||||
fmt.Printf("%s\n", latest)
|
||||
|
@ -43,6 +43,19 @@ not an executable. The new rewrite removes all shell code from asdf, and it is
|
||||
now a binary rather than a shell function, so setting environment variables
|
||||
directly in the shell is no longer possible.
|
||||
|
||||
### Executables Shims Resolve to Must Runnable by `syscall.Exec`
|
||||
|
||||
The most obvious example of this breaking change are scripts that lack a proper
|
||||
shebang line. asdf 0.14.1 and older were implemented in Bash, so as long it was
|
||||
an executable that could be executed with Bash it would run. This mean that
|
||||
scripts lacking a shebang could still be run by `asdf exec`. With asdf 0.15.x
|
||||
implemented in Go we now invoke executables via Go's `syscall.Exec` function,
|
||||
which cannot handle scripts lacking a shebang.
|
||||
|
||||
In practice this isn't much of a problem. Most shell scripts DO contain a
|
||||
shebang line. If a tool managed by asdf provides scripts that don't have a
|
||||
shebang line one will need to be added to them.
|
||||
|
||||
## Installation
|
||||
|
||||
Installation of version 0.15.0 is much simpler than previous versions of asdf. It's just three steps:
|
||||
|
@ -80,9 +80,9 @@ func writePluginHelp(conf config.Config, toolName, toolVersion string, writer io
|
||||
}
|
||||
|
||||
if toolVersion != "" {
|
||||
versionType, version := toolversions.Parse(toolVersion)
|
||||
env["ASDF_INSTALL_VERSION"] = version
|
||||
env["ASDF_INSTALL_TYPE"] = versionType
|
||||
version := toolversions.Parse(toolVersion)
|
||||
env["ASDF_INSTALL_VERSION"] = version.Value
|
||||
env["ASDF_INSTALL_TYPE"] = version.Type
|
||||
}
|
||||
|
||||
if err := plugin.Exists(); err != nil {
|
||||
|
@ -38,26 +38,26 @@ func Installed(conf config.Config, plugin plugins.Plugin) (versions []string, er
|
||||
}
|
||||
|
||||
// InstallPath returns the path to a tool installation
|
||||
func InstallPath(conf config.Config, plugin plugins.Plugin, versionType, version string) string {
|
||||
if versionType == "path" {
|
||||
return version
|
||||
func InstallPath(conf config.Config, plugin plugins.Plugin, version toolversions.Version) string {
|
||||
if version.Type == "path" {
|
||||
return version.Value
|
||||
}
|
||||
|
||||
return filepath.Join(data.InstallDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(versionType, version))
|
||||
return filepath.Join(data.InstallDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(version))
|
||||
}
|
||||
|
||||
// DownloadPath returns the download path for a particular plugin and version
|
||||
func DownloadPath(conf config.Config, plugin plugins.Plugin, versionType, version string) string {
|
||||
if versionType == "path" {
|
||||
func DownloadPath(conf config.Config, plugin plugins.Plugin, version toolversions.Version) string {
|
||||
if version.Type == "path" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(data.DownloadDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(versionType, version))
|
||||
return filepath.Join(data.DownloadDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(version))
|
||||
}
|
||||
|
||||
// IsInstalled checks if a specific version of a tool is installed
|
||||
func IsInstalled(conf config.Config, plugin plugins.Plugin, versionType, version string) bool {
|
||||
installDir := InstallPath(conf, plugin, versionType, version)
|
||||
func IsInstalled(conf config.Config, plugin plugins.Plugin, version toolversions.Version) bool {
|
||||
installDir := InstallPath(conf, plugin, version)
|
||||
|
||||
// Check if version already installed
|
||||
_, err := os.Stat(installDir)
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"asdf/internal/config"
|
||||
"asdf/internal/installtest"
|
||||
"asdf/internal/plugins"
|
||||
"asdf/internal/toolversions"
|
||||
"asdf/repotest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -19,12 +20,14 @@ func TestDownloadPath(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
|
||||
t.Run("returns empty string when given path version", func(t *testing.T) {
|
||||
path := DownloadPath(conf, plugin, "path", "foo/bar")
|
||||
version := toolversions.Version{Type: "path", Value: "foo/bar"}
|
||||
path := DownloadPath(conf, plugin, version)
|
||||
assert.Empty(t, path)
|
||||
})
|
||||
|
||||
t.Run("returns empty string when given path version", func(t *testing.T) {
|
||||
path := DownloadPath(conf, plugin, "version", "1.2.3")
|
||||
version := toolversions.Version{Type: "version", Value: "1.2.3"}
|
||||
path := DownloadPath(conf, plugin, version)
|
||||
assert.Equal(t, path, filepath.Join(conf.DataDir, "downloads", "lua", "1.2.3"))
|
||||
})
|
||||
}
|
||||
@ -33,12 +36,14 @@ func TestInstallPath(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
|
||||
t.Run("returns empty string when given path version", func(t *testing.T) {
|
||||
path := InstallPath(conf, plugin, "path", "foo/bar")
|
||||
version := toolversions.Version{Type: "path", Value: "foo/bar"}
|
||||
path := InstallPath(conf, plugin, version)
|
||||
assert.Equal(t, path, "foo/bar")
|
||||
})
|
||||
|
||||
t.Run("returns install path when given regular version as version", func(t *testing.T) {
|
||||
path := InstallPath(conf, plugin, "version", "1.2.3")
|
||||
version := toolversions.Version{Type: "version", Value: "1.2.3"}
|
||||
path := InstallPath(conf, plugin, version)
|
||||
assert.Equal(t, path, filepath.Join(conf.DataDir, "installs", "lua", "1.2.3"))
|
||||
})
|
||||
}
|
||||
@ -66,10 +71,12 @@ func TestIsInstalled(t *testing.T) {
|
||||
installVersion(t, conf, plugin, "1.0.0")
|
||||
|
||||
t.Run("returns false when not installed", func(t *testing.T) {
|
||||
assert.False(t, IsInstalled(conf, plugin, "version", "4.0.0"))
|
||||
version := toolversions.Version{Type: "version", Value: "4.0.0"}
|
||||
assert.False(t, IsInstalled(conf, plugin, version))
|
||||
})
|
||||
t.Run("returns true when installed", func(t *testing.T) {
|
||||
assert.True(t, IsInstalled(conf, plugin, "version", "1.0.0"))
|
||||
version := toolversions.Version{Type: "version", Value: "1.0.0"}
|
||||
assert.True(t, IsInstalled(conf, plugin, version))
|
||||
})
|
||||
}
|
||||
|
||||
@ -87,9 +94,10 @@ func generateConfig(t *testing.T) (config.Config, plugins.Plugin) {
|
||||
return conf, plugins.New(conf, testPluginName)
|
||||
}
|
||||
|
||||
func mockInstall(t *testing.T, conf config.Config, plugin plugins.Plugin, version string) {
|
||||
func mockInstall(t *testing.T, conf config.Config, plugin plugins.Plugin, versionStr string) {
|
||||
t.Helper()
|
||||
path := InstallPath(conf, plugin, "version", version)
|
||||
version := toolversions.Version{Type: "version", Value: versionStr}
|
||||
path := InstallPath(conf, plugin, version)
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ func getCustomExecutablePath(conf config.Config, plugin plugins.Plugin, shimName
|
||||
var stdOut strings.Builder
|
||||
var stdErr strings.Builder
|
||||
|
||||
installPath := installs.InstallPath(conf, plugin, "version", version)
|
||||
installPath := installs.InstallPath(conf, plugin, toolversions.Version{Type: "version", Value: version})
|
||||
env := map[string]string{"ASDF_INSTALL_TYPE": "version"}
|
||||
|
||||
err := plugin.RunCallback("exec-path", []string{installPath, shimName}, env, &stdOut, &stdErr)
|
||||
@ -303,7 +303,7 @@ func ToolExecutables(conf config.Config, plugin plugins.Plugin, versionType, ver
|
||||
return executables, err
|
||||
}
|
||||
|
||||
installPath := installs.InstallPath(conf, plugin, versionType, version)
|
||||
installPath := installs.InstallPath(conf, plugin, toolversions.Version{Type: versionType, Value: version})
|
||||
paths := dirsToPaths(dirs, installPath)
|
||||
|
||||
for _, path := range paths {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"asdf/internal/installs"
|
||||
"asdf/internal/installtest"
|
||||
"asdf/internal/plugins"
|
||||
"asdf/internal/toolversions"
|
||||
"asdf/repotest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -56,7 +57,8 @@ func TestFindExecutable(t *testing.T) {
|
||||
|
||||
t.Run("returns string containing path to system executable when system version set", func(t *testing.T) {
|
||||
// Create dummy `ls` executable
|
||||
path := filepath.Join(installs.InstallPath(conf, plugin, "version", version), "bin", "ls")
|
||||
versionStruct := toolversions.Version{Type: "version", Value: version}
|
||||
path := filepath.Join(installs.InstallPath(conf, plugin, versionStruct), "bin", "ls")
|
||||
assert.Nil(t, os.WriteFile(path, []byte("echo 'I'm ls'"), 0o777))
|
||||
|
||||
// write system version to version file
|
||||
@ -75,19 +77,19 @@ func TestFindExecutable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetExecutablePath(t *testing.T) {
|
||||
version := "1.1.0"
|
||||
version := toolversions.Version{Type: "version", Value: "1.1.0"}
|
||||
conf, plugin := generateConfig(t)
|
||||
installVersion(t, conf, plugin, version)
|
||||
installVersion(t, conf, plugin, version.Value)
|
||||
|
||||
t.Run("returns path to executable", func(t *testing.T) {
|
||||
path, err := GetExecutablePath(conf, plugin, "dummy", version)
|
||||
path, err := GetExecutablePath(conf, plugin, "dummy", version.Value)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, filepath.Base(path), "dummy")
|
||||
assert.Equal(t, filepath.Base(filepath.Dir(filepath.Dir(path))), version)
|
||||
assert.Equal(t, filepath.Base(filepath.Dir(filepath.Dir(path))), version.Value)
|
||||
})
|
||||
|
||||
t.Run("returns error when executable with name not found", func(t *testing.T) {
|
||||
path, err := GetExecutablePath(conf, plugin, "foo", version)
|
||||
path, err := GetExecutablePath(conf, plugin, "foo", version.Value)
|
||||
assert.ErrorContains(t, err, "executable not found")
|
||||
assert.Equal(t, path, "")
|
||||
})
|
||||
@ -96,7 +98,7 @@ func TestGetExecutablePath(t *testing.T) {
|
||||
// Create exec-path callback
|
||||
installDummyExecPathScript(t, conf, plugin, version, "dummy")
|
||||
|
||||
path, err := GetExecutablePath(conf, plugin, "dummy", version)
|
||||
path, err := GetExecutablePath(conf, plugin, "dummy", version.Value)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, filepath.Base(filepath.Dir(path)), "custom")
|
||||
})
|
||||
@ -285,12 +287,12 @@ func TestWrite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToolExecutables(t *testing.T) {
|
||||
version := "1.1.0"
|
||||
version := toolversions.Version{Type: "version", Value: "1.1.0"}
|
||||
conf, plugin := generateConfig(t)
|
||||
installVersion(t, conf, plugin, version)
|
||||
installVersion(t, conf, plugin, version.Value)
|
||||
|
||||
t.Run("returns list of executables for plugin", func(t *testing.T) {
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version)
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version.Value)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var filenames []string
|
||||
@ -304,7 +306,7 @@ func TestToolExecutables(t *testing.T) {
|
||||
|
||||
t.Run("returns list of executables for version installed in arbitrary directory", func(t *testing.T) {
|
||||
// Reference regular install by path to validate this behavior
|
||||
path := installs.InstallPath(conf, plugin, "version", version)
|
||||
path := installs.InstallPath(conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, "path", path)
|
||||
assert.Nil(t, err)
|
||||
|
||||
@ -357,14 +359,14 @@ func generateConfig(t *testing.T) (config.Config, plugins.Plugin) {
|
||||
return conf, installPlugin(t, conf, "dummy_plugin", testPluginName)
|
||||
}
|
||||
|
||||
func installDummyExecPathScript(t *testing.T, conf config.Config, plugin plugins.Plugin, version, name string) {
|
||||
func installDummyExecPathScript(t *testing.T, conf config.Config, plugin plugins.Plugin, version toolversions.Version, name string) {
|
||||
t.Helper()
|
||||
execPath := filepath.Join(plugin.Dir, "bin", "exec-path")
|
||||
contents := fmt.Sprintf("#!/usr/bin/env bash\necho 'bin/custom/%s'", name)
|
||||
err := os.WriteFile(execPath, []byte(contents), 0o777)
|
||||
assert.Nil(t, err)
|
||||
|
||||
installPath := installs.InstallPath(conf, plugin, "version", version)
|
||||
installPath := installs.InstallPath(conf, plugin, version)
|
||||
err = os.MkdirAll(filepath.Join(installPath, "bin", "custom"), 0o777)
|
||||
assert.Nil(t, err)
|
||||
|
||||
|
@ -10,6 +10,12 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version struct represents a single version in asdf.
|
||||
type Version struct {
|
||||
Type string // Must be one of: version, ref, path, system, latest
|
||||
Value string // Any string
|
||||
}
|
||||
|
||||
// ToolVersions represents a tool along with versions specified for it
|
||||
type ToolVersions struct {
|
||||
Name string
|
||||
@ -86,49 +92,52 @@ func Unique(versions []ToolVersions) (uniques []ToolVersions) {
|
||||
// ParseFromCliArg parses a string that is passed in as an argument to one of
|
||||
// the asdf subcommands. Some subcommands allow the special version `latest` to
|
||||
// be used, with an optional filter string.
|
||||
func ParseFromCliArg(version string) (string, string) {
|
||||
func ParseFromCliArg(version string) Version {
|
||||
segments := strings.Split(version, ":")
|
||||
if len(segments) > 0 && segments[0] == "latest" {
|
||||
if len(segments) > 1 {
|
||||
// Must be latest with filter
|
||||
return "latest", segments[1]
|
||||
return Version{Type: "latest", Value: segments[1]}
|
||||
}
|
||||
return "latest", ""
|
||||
return Version{Type: "latest", Value: ""}
|
||||
}
|
||||
|
||||
return Parse(version)
|
||||
}
|
||||
|
||||
// Parse parses a version string into versionType and version components
|
||||
func Parse(version string) (string, string) {
|
||||
func Parse(version string) Version {
|
||||
segments := strings.Split(version, ":")
|
||||
if len(segments) >= 1 {
|
||||
remainder := strings.Join(segments[1:], ":")
|
||||
switch segments[0] {
|
||||
case "ref":
|
||||
return "ref", remainder
|
||||
return Version{Type: "ref", Value: remainder}
|
||||
case "path":
|
||||
// This is for people who have the local source already compiled
|
||||
// Like those who work on the language, etc
|
||||
// We'll allow specifying path:/foo/bar/project in .tool-versions
|
||||
// And then use the binaries there
|
||||
return "path", remainder
|
||||
return Version{Type: "path", Value: remainder}
|
||||
default:
|
||||
return "version", version
|
||||
}
|
||||
}
|
||||
|
||||
return "version", version
|
||||
if version == "system" {
|
||||
return Version{Type: "system"}
|
||||
}
|
||||
|
||||
return Version{Type: "version", Value: version}
|
||||
}
|
||||
|
||||
// FormatForFS takes a versionType and version strings and generate a version
|
||||
// string suitable for the file system
|
||||
func FormatForFS(versionType, version string) string {
|
||||
switch versionType {
|
||||
func FormatForFS(version Version) string {
|
||||
switch version.Type {
|
||||
case "ref":
|
||||
return fmt.Sprintf("ref-%s", version)
|
||||
return fmt.Sprintf("ref-%s", version.Value)
|
||||
default:
|
||||
return version
|
||||
return version.Value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,63 +159,75 @@ func TestgetAllToolsAndVersionsInContent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
t.Run("returns 'version', and unmodified version when passed semantic version", func(t *testing.T) {
|
||||
versionType, version := Parse("1.2.3")
|
||||
assert.Equal(t, versionType, "version")
|
||||
assert.Equal(t, version, "1.2.3")
|
||||
t.Run("when passed version string returns struct with type of 'version' and version as value", func(t *testing.T) {
|
||||
version := Parse("1.2.3")
|
||||
assert.Equal(t, version.Type, "version")
|
||||
assert.Equal(t, version.Value, "1.2.3")
|
||||
})
|
||||
|
||||
t.Run("returns 'ref' and reference version when passed a ref version", func(t *testing.T) {
|
||||
versionType, version := Parse("ref:abc123")
|
||||
assert.Equal(t, versionType, "ref")
|
||||
assert.Equal(t, version, "abc123")
|
||||
t.Run("when passed ref and version returns struct with type of 'ref' and version as value", func(t *testing.T) {
|
||||
version := Parse("ref:abc123")
|
||||
assert.Equal(t, version.Type, "ref")
|
||||
assert.Equal(t, version.Value, "abc123")
|
||||
})
|
||||
|
||||
t.Run("returns 'ref' and empty string when passed 'ref:'", func(t *testing.T) {
|
||||
versionType, version := Parse("ref:")
|
||||
assert.Equal(t, versionType, "ref")
|
||||
assert.Equal(t, version, "")
|
||||
t.Run("when passed 'ref:' returns struct with type of 'ref' and empty value", func(t *testing.T) {
|
||||
version := Parse("ref:")
|
||||
assert.Equal(t, version.Type, "ref")
|
||||
assert.Equal(t, version.Value, "")
|
||||
})
|
||||
|
||||
t.Run("when passed 'system' returns struct with type of 'system'", func(t *testing.T) {
|
||||
version := Parse("system")
|
||||
assert.Equal(t, version.Type, "system")
|
||||
assert.Equal(t, version.Value, "")
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseFromCliArg(t *testing.T) {
|
||||
t.Run("returns 'latest' as version type when passed string 'latest'", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("latest")
|
||||
assert.Equal(t, versionType, "latest")
|
||||
assert.Equal(t, version, "")
|
||||
t.Run("when passed 'latest' returns struct with type of 'latest'", func(t *testing.T) {
|
||||
version := ParseFromCliArg("latest")
|
||||
assert.Equal(t, version.Type, "latest")
|
||||
assert.Equal(t, version.Value, "")
|
||||
})
|
||||
|
||||
t.Run("returns 'latest' and unmodified filter string when passed a latest version", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("latest:1.2")
|
||||
assert.Equal(t, versionType, "latest")
|
||||
assert.Equal(t, version, "1.2")
|
||||
t.Run("when passed latest with filter returns struct with type of 'latest' and unmodified filter string as value", func(t *testing.T) {
|
||||
version := ParseFromCliArg("latest:1.2")
|
||||
assert.Equal(t, version.Type, "latest")
|
||||
assert.Equal(t, version.Value, "1.2")
|
||||
})
|
||||
|
||||
t.Run("returns 'version', and unmodified version when passed semantic version", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("1.2.3")
|
||||
assert.Equal(t, versionType, "version")
|
||||
assert.Equal(t, version, "1.2.3")
|
||||
t.Run("when passed version string returns struct with type of 'version' and version as value", func(t *testing.T) {
|
||||
version := ParseFromCliArg("1.2.3")
|
||||
assert.Equal(t, version.Type, "version")
|
||||
assert.Equal(t, version.Value, "1.2.3")
|
||||
})
|
||||
|
||||
t.Run("returns 'ref' and reference version when passed a ref version", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("ref:abc123")
|
||||
assert.Equal(t, versionType, "ref")
|
||||
assert.Equal(t, version, "abc123")
|
||||
t.Run("when passed ref and version returns struct with type of 'ref' and version as value", func(t *testing.T) {
|
||||
version := ParseFromCliArg("ref:abc123")
|
||||
assert.Equal(t, version.Type, "ref")
|
||||
assert.Equal(t, version.Value, "abc123")
|
||||
})
|
||||
|
||||
t.Run("returns 'ref' and empty string when passed 'ref:'", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("ref:")
|
||||
assert.Equal(t, versionType, "ref")
|
||||
assert.Equal(t, version, "")
|
||||
t.Run("when passed 'ref:' returns struct with type of 'ref' and empty value", func(t *testing.T) {
|
||||
version := ParseFromCliArg("ref:")
|
||||
assert.Equal(t, version.Type, "ref")
|
||||
assert.Equal(t, version.Value, "")
|
||||
})
|
||||
|
||||
t.Run("when passed 'system' returns struct with type of 'system'", func(t *testing.T) {
|
||||
version := ParseFromCliArg("system")
|
||||
assert.Equal(t, version.Type, "system")
|
||||
assert.Equal(t, version.Value, "")
|
||||
})
|
||||
}
|
||||
|
||||
func TestFormatForFS(t *testing.T) {
|
||||
t.Run("returns version when version type is not ref", func(t *testing.T) {
|
||||
assert.Equal(t, FormatForFS("version", "foobar"), "foobar")
|
||||
assert.Equal(t, FormatForFS(Version{Type: "version", Value: "foobar"}), "foobar")
|
||||
})
|
||||
|
||||
t.Run("returns version prefixed with 'ref-' when version type is ref", func(t *testing.T) {
|
||||
assert.Equal(t, FormatForFS("ref", "foobar"), "ref-foobar")
|
||||
assert.Equal(t, FormatForFS(Version{Type: "ref", Value: "foobar"}), "ref-foobar")
|
||||
})
|
||||
}
|
||||
|
@ -122,31 +122,31 @@ func InstallVersion(conf config.Config, plugin plugins.Plugin, version string, p
|
||||
}
|
||||
|
||||
// InstallOneVersion installs a specific version of a specific tool
|
||||
func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string, stdOut io.Writer, stdErr io.Writer) error {
|
||||
func InstallOneVersion(conf config.Config, plugin plugins.Plugin, versionStr string, stdOut io.Writer, stdErr io.Writer) error {
|
||||
err := plugin.Exists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version == systemVersion {
|
||||
return UninstallableVersionError{versionType: "system"}
|
||||
if versionStr == systemVersion {
|
||||
return UninstallableVersionError{versionType: systemVersion}
|
||||
}
|
||||
|
||||
versionType, version := toolversions.Parse(version)
|
||||
version := toolversions.Parse(versionStr)
|
||||
|
||||
if versionType == "path" {
|
||||
if version.Type == "path" {
|
||||
return UninstallableVersionError{versionType: "path"}
|
||||
}
|
||||
downloadDir := installs.DownloadPath(conf, plugin, versionType, version)
|
||||
installDir := installs.InstallPath(conf, plugin, versionType, version)
|
||||
downloadDir := installs.DownloadPath(conf, plugin, version)
|
||||
installDir := installs.InstallPath(conf, plugin, version)
|
||||
|
||||
if installs.IsInstalled(conf, plugin, versionType, version) {
|
||||
if installs.IsInstalled(conf, plugin, version) {
|
||||
return fmt.Errorf("version %s of %s is already installed", version, plugin.Name)
|
||||
}
|
||||
|
||||
env := map[string]string{
|
||||
"ASDF_INSTALL_TYPE": versionType,
|
||||
"ASDF_INSTALL_VERSION": version,
|
||||
"ASDF_INSTALL_TYPE": version.Type,
|
||||
"ASDF_INSTALL_VERSION": version.Value,
|
||||
"ASDF_INSTALL_PATH": installDir,
|
||||
"ASDF_DOWNLOAD_PATH": downloadDir,
|
||||
"ASDF_CONCURRENCY": asdfConcurrency(conf),
|
||||
@ -157,7 +157,7 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
|
||||
return fmt.Errorf("unable to create download dir: %w", err)
|
||||
}
|
||||
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_download_%s", plugin.Name), []string{version}, stdOut, stdErr)
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_download_%s", plugin.Name), []string{version.Value}, stdOut, stdErr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run pre-download hook: %w", err)
|
||||
}
|
||||
@ -167,7 +167,7 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
|
||||
return fmt.Errorf("failed to run download callback: %w", err)
|
||||
}
|
||||
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_install_%s", plugin.Name), []string{version}, stdOut, stdErr)
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_install_%s", plugin.Name), []string{version.Value}, stdOut, stdErr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run pre-install hook: %w", err)
|
||||
}
|
||||
@ -188,7 +188,7 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
|
||||
return fmt.Errorf("unable to generate shims post-install: %w", err)
|
||||
}
|
||||
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("post_asdf_install_%s", plugin.Name), []string{version}, stdOut, stdErr)
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("post_asdf_install_%s", plugin.Name), []string{version.Value}, stdOut, stdErr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run post-install hook: %w", err)
|
||||
}
|
||||
@ -278,26 +278,26 @@ func AllVersionsFiltered(plugin plugins.Plugin, query string) (versions []string
|
||||
// post-uninstall hooks if set, and runs the plugin's uninstall callback if
|
||||
// defined.
|
||||
func Uninstall(conf config.Config, plugin plugins.Plugin, rawVersion string, stdout, stderr io.Writer) error {
|
||||
versionType, version := toolversions.ParseFromCliArg(rawVersion)
|
||||
version := toolversions.ParseFromCliArg(rawVersion)
|
||||
|
||||
if versionType == "latest" {
|
||||
if version.Type == "latest" {
|
||||
return errors.New("'latest' is a special version value that cannot be used for uninstall command")
|
||||
}
|
||||
|
||||
if !installs.IsInstalled(conf, plugin, versionType, version) {
|
||||
if !installs.IsInstalled(conf, plugin, version) {
|
||||
return errors.New("No such version")
|
||||
}
|
||||
|
||||
err := hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_uninstall_%s", plugin.Name), []string{version}, stdout, stderr)
|
||||
err := hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_uninstall_%s", plugin.Name), []string{version.Value}, stdout, stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// invoke uninstall callback if available
|
||||
installDir := installs.InstallPath(conf, plugin, versionType, version)
|
||||
installDir := installs.InstallPath(conf, plugin, version)
|
||||
env := map[string]string{
|
||||
"ASDF_INSTALL_TYPE": versionType,
|
||||
"ASDF_INSTALL_VERSION": version,
|
||||
"ASDF_INSTALL_TYPE": version.Type,
|
||||
"ASDF_INSTALL_VERSION": version.Value,
|
||||
"ASDF_INSTALL_PATH": installDir,
|
||||
}
|
||||
err = plugin.RunCallback("uninstall", []string{}, env, stdout, stderr)
|
||||
@ -310,7 +310,7 @@ func Uninstall(conf config.Config, plugin plugins.Plugin, rawVersion string, std
|
||||
return err
|
||||
}
|
||||
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("post_asdf_uninstall_%s", plugin.Name), []string{version}, stdout, stderr)
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("post_asdf_uninstall_%s", plugin.Name), []string{version.Value}, stdout, stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
2
test/fixtures/dummy_plugin/bin/install
vendored
2
test/fixtures/dummy_plugin/bin/install
vendored
@ -17,11 +17,13 @@ echo "$ASDF_INSTALL_VERSION" >"$ASDF_INSTALL_PATH/version"
|
||||
# create the dummy executable
|
||||
mkdir -p "$ASDF_INSTALL_PATH/bin"
|
||||
cat <<EOF >"$ASDF_INSTALL_PATH/bin/dummy"
|
||||
#!/usr/bin/env bash
|
||||
echo This is Dummy ${ASDF_INSTALL_VERSION}! \$2 \$1
|
||||
EOF
|
||||
chmod +x "$ASDF_INSTALL_PATH/bin/dummy"
|
||||
mkdir -p "$ASDF_INSTALL_PATH/bin/subdir"
|
||||
cat <<EOF >"$ASDF_INSTALL_PATH/bin/subdir/other_bin"
|
||||
#!/usr/bin/env bash
|
||||
echo This is Other Bin ${ASDF_INSTALL_VERSION}! \$2 \$1
|
||||
EOF
|
||||
chmod +x "$ASDF_INSTALL_PATH/bin/subdir/other_bin"
|
||||
|
@ -61,7 +61,8 @@ teardown() {
|
||||
echo "dummy 1.0" >"$PROJECT_DIR/.tool-versions"
|
||||
run asdf install
|
||||
|
||||
echo "tr [:lower:] [:upper:]" >"$ASDF_DIR/installs/dummy/1.0/bin/upper"
|
||||
echo "#!/usr/bin/env bash
|
||||
tr [:lower:] [:upper:]" >"$ASDF_DIR/installs/dummy/1.0/bin/upper"
|
||||
chmod +x "$ASDF_DIR/installs/dummy/1.0/bin/upper"
|
||||
|
||||
run asdf reshim dummy 1.0
|
||||
@ -114,20 +115,22 @@ teardown() {
|
||||
echo "$output" | grep -q "mummy 3.0" 2>/dev/null
|
||||
}
|
||||
|
||||
@test "shim exec should suggest to install missing version" {
|
||||
run asdf install dummy 1.0
|
||||
# No longer possible for shim to specify version that isn't installed because
|
||||
# shims are re-generated after every install and uninstall.
|
||||
#@test "shim exec should suggest to install missing version" {
|
||||
# run asdf install dummy 1.0
|
||||
|
||||
echo "dummy 2.0.0 1.3" >"$PROJECT_DIR/.tool-versions"
|
||||
# echo "dummy 2.0.0 1.3" >"$PROJECT_DIR/.tool-versions"
|
||||
|
||||
run "$ASDF_DIR/shims/dummy" world hello
|
||||
[ "$status" -eq 126 ]
|
||||
echo "$output" | grep -q "No preset version installed for command dummy" 2>/dev/null
|
||||
echo "$output" | grep -q "Please install a version by running one of the following:" 2>/dev/null
|
||||
echo "$output" | grep -q "asdf install dummy 2.0.0" 2>/dev/null
|
||||
echo "$output" | grep -q "asdf install dummy 1.3" 2>/dev/null
|
||||
echo "$output" | grep -q "or add one of the following versions in your config file at $PROJECT_DIR/.tool-versions" 2>/dev/null
|
||||
echo "$output" | grep -q "dummy 1.0" 2>/dev/null
|
||||
}
|
||||
# run "$ASDF_DIR/shims/dummy" world hello
|
||||
# [ "$status" -eq 126 ]
|
||||
# echo "$output" | grep -q "No preset version installed for command dummy" 2>/dev/null
|
||||
# echo "$output" | grep -q "Please install a version by running one of the following:" 2>/dev/null
|
||||
# echo "$output" | grep -q "asdf install dummy 2.0.0" 2>/dev/null
|
||||
# echo "$output" | grep -q "asdf install dummy 1.3" 2>/dev/null
|
||||
# echo "$output" | grep -q "or add one of the following versions in your config file at $PROJECT_DIR/.tool-versions" 2>/dev/null
|
||||
# echo "$output" | grep -q "dummy 1.0" 2>/dev/null
|
||||
#}
|
||||
|
||||
@test "shim exec should execute first plugin that is installed and set" {
|
||||
run asdf install dummy 2.0.0
|
||||
@ -199,7 +202,8 @@ teardown() {
|
||||
echo "dummy system" >"$PROJECT_DIR/.tool-versions"
|
||||
|
||||
mkdir "$PROJECT_DIR/foo/"
|
||||
echo "echo System" >"$PROJECT_DIR/foo/dummy"
|
||||
echo "#!/usr/bin/env bash
|
||||
echo System" >"$PROJECT_DIR/foo/dummy"
|
||||
chmod +x "$PROJECT_DIR/foo/dummy"
|
||||
|
||||
run env "PATH=$PATH:$PROJECT_DIR/foo" "$ASDF_DIR/shims/dummy" hello
|
||||
@ -214,7 +218,8 @@ teardown() {
|
||||
CUSTOM_DUMMY_PATH="$PROJECT_DIR/foo"
|
||||
CUSTOM_DUMMY_BIN_PATH="$CUSTOM_DUMMY_PATH/bin"
|
||||
mkdir -p "$CUSTOM_DUMMY_BIN_PATH"
|
||||
echo "echo System" >"$CUSTOM_DUMMY_BIN_PATH/dummy"
|
||||
echo "#!/usr/bin/env bash
|
||||
echo System" >"$CUSTOM_DUMMY_BIN_PATH/dummy"
|
||||
chmod +x "$CUSTOM_DUMMY_BIN_PATH/dummy"
|
||||
|
||||
echo "dummy path:$CUSTOM_DUMMY_PATH" >"$PROJECT_DIR/.tool-versions"
|
||||
@ -230,7 +235,8 @@ teardown() {
|
||||
echo "dummy 2.0.0" >>"$PROJECT_DIR/.tool-versions"
|
||||
|
||||
mkdir "$PROJECT_DIR/foo/"
|
||||
echo "echo System" >"$PROJECT_DIR/foo/dummy"
|
||||
echo "#!/usr/bin/env bash
|
||||
echo System" >"$PROJECT_DIR/foo/dummy"
|
||||
chmod +x "$PROJECT_DIR/foo/dummy"
|
||||
|
||||
run env "PATH=$PATH:$PROJECT_DIR/foo" "$ASDF_DIR/shims/dummy" hello
|
||||
|
Loading…
Reference in New Issue
Block a user