mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-23 20:05:09 -07:00
Merge pull request #64 from asdf-vm/tb/shim-generation-4
feat(golang-rewrite): use version type when generating shims
This commit is contained in:
commit
631b0d8793
26
cmd/cmd.go
26
cmd/cmd.go
@ -4,6 +4,7 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
@ -139,8 +140,9 @@ func Execute(version string) {
|
||||
},
|
||||
{
|
||||
Name: "reshim",
|
||||
Action: func(_ *cli.Context) error {
|
||||
return reshimCommand(logger)
|
||||
Action: func(cCtx *cli.Context) error {
|
||||
args := cCtx.Args()
|
||||
return reshimCommand(logger, args.Get(0), args.Get(1))
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -390,24 +392,32 @@ func latestCommand(logger *log.Logger, all bool, toolName, pattern string) (err
|
||||
return nil
|
||||
}
|
||||
|
||||
func reshimCommand(logger *log.Logger) (err error) {
|
||||
func reshimCommand(logger *log.Logger, tool, version string) (err error) {
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Printf("error loading config: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// if either tool or version are missing just regenerate all shims. This is
|
||||
// fast enough now.
|
||||
if tool == "" || version == "" {
|
||||
err = shims.RemoveAll(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = shims.GenerateAll(conf, os.Stdout, os.Stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
return shims.GenerateAll(conf, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
return err
|
||||
// If provided a specific version it could be something special like a path
|
||||
// version so we need to generate it manually
|
||||
return reshimToolVersion(conf, tool, version, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
func reshimToolVersion(conf config.Config, tool, version string, out io.Writer, errOut io.Writer) error {
|
||||
versionType, version := versions.ParseString(version)
|
||||
return shims.GenerateForVersion(conf, plugins.New(conf, tool), versionType, version, out, errOut)
|
||||
}
|
||||
|
||||
func latestForPlugin(conf config.Config, toolName, pattern string, showStatus bool) error {
|
||||
@ -426,7 +436,7 @@ func latestForPlugin(conf config.Config, toolName, pattern string, showStatus bo
|
||||
}
|
||||
|
||||
if showStatus {
|
||||
installed := installs.IsInstalled(conf, plugin, latest)
|
||||
installed := installs.IsInstalled(conf, plugin, "version", latest)
|
||||
fmt.Printf("%s\t%s\t%s\n", plugin.Name, latest, installedStatus(installed))
|
||||
} else {
|
||||
fmt.Printf("%s\n", latest)
|
||||
|
@ -41,18 +41,24 @@ 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, version string) string {
|
||||
func InstallPath(conf config.Config, plugin plugins.Plugin, versionType, version string) string {
|
||||
if versionType == "path" {
|
||||
return version
|
||||
}
|
||||
return filepath.Join(pluginInstallPath(conf, plugin), version)
|
||||
}
|
||||
|
||||
// DownloadPath returns the download path for a particular plugin and version
|
||||
func DownloadPath(conf config.Config, plugin plugins.Plugin, version string) string {
|
||||
func DownloadPath(conf config.Config, plugin plugins.Plugin, versionType, version string) string {
|
||||
if versionType == "path" {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(conf.DataDir, dataDirDownloads, plugin.Name, version)
|
||||
}
|
||||
|
||||
// IsInstalled checks if a specific version of a tool is installed
|
||||
func IsInstalled(conf config.Config, plugin plugins.Plugin, version string) bool {
|
||||
installDir := InstallPath(conf, plugin, version)
|
||||
func IsInstalled(conf config.Config, plugin plugins.Plugin, versionType, version string) bool {
|
||||
installDir := InstallPath(conf, plugin, versionType, version)
|
||||
|
||||
// Check if version already installed
|
||||
_, err := os.Stat(installDir)
|
||||
|
@ -2,6 +2,7 @@ package installs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"asdf/internal/config"
|
||||
@ -14,6 +15,34 @@ import (
|
||||
|
||||
const testPluginName = "lua"
|
||||
|
||||
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")
|
||||
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")
|
||||
assert.Equal(t, path, filepath.Join(conf.DataDir, "downloads", "lua", "1.2.3"))
|
||||
})
|
||||
}
|
||||
|
||||
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")
|
||||
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")
|
||||
assert.Equal(t, path, filepath.Join(conf.DataDir, "installs", "lua", "1.2.3"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestInstalled(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
|
||||
@ -37,10 +66,10 @@ 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, "4.0.0"))
|
||||
assert.False(t, IsInstalled(conf, plugin, "version", "4.0.0"))
|
||||
})
|
||||
t.Run("returns true when installed", func(t *testing.T) {
|
||||
assert.True(t, IsInstalled(conf, plugin, "1.0.0"))
|
||||
assert.True(t, IsInstalled(conf, plugin, "version", "1.0.0"))
|
||||
})
|
||||
}
|
||||
|
||||
@ -60,7 +89,7 @@ func generateConfig(t *testing.T) (config.Config, plugins.Plugin) {
|
||||
|
||||
func mockInstall(t *testing.T, conf config.Config, plugin plugins.Plugin, version string) {
|
||||
t.Helper()
|
||||
path := InstallPath(conf, plugin, version)
|
||||
path := InstallPath(conf, plugin, "version", version)
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
@ -62,28 +62,26 @@ func GenerateForPluginVersions(conf config.Config, plugin plugins.Plugin, stdOut
|
||||
}
|
||||
|
||||
for _, version := range installedVersions {
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_reshim_%s", plugin.Name), []string{version}, stdOut, stdErr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
GenerateForVersion(conf, plugin, version)
|
||||
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("post_asdf_reshim_%s", plugin.Name), []string{version}, stdOut, stdErr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
GenerateForVersion(conf, plugin, "version", version, stdOut, stdErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateForVersion loops over all the executable files found for a tool and
|
||||
// generates a shim for each one
|
||||
func GenerateForVersion(conf config.Config, plugin plugins.Plugin, version string) error {
|
||||
executables, err := ToolExecutables(conf, plugin, version)
|
||||
func GenerateForVersion(conf config.Config, plugin plugins.Plugin, versionType, version string, stdOut io.Writer, stdErr io.Writer) error {
|
||||
err := hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_reshim_%s", plugin.Name), []string{version}, stdOut, stdErr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
executables, err := ToolExecutables(conf, plugin, versionType, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if versionType == "path" {
|
||||
version = fmt.Sprintf("path:%s", version)
|
||||
}
|
||||
|
||||
for _, executablePath := range executables {
|
||||
err := Write(conf, plugin, version, executablePath)
|
||||
@ -92,6 +90,10 @@ func GenerateForVersion(conf config.Config, plugin plugins.Plugin, version strin
|
||||
}
|
||||
}
|
||||
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("post_asdf_reshim_%s", plugin.Name), []string{version}, stdOut, stdErr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -129,13 +131,13 @@ func ensureShimDirExists(conf config.Config) error {
|
||||
}
|
||||
|
||||
// ToolExecutables returns a slice of executables for a given tool version
|
||||
func ToolExecutables(conf config.Config, plugin plugins.Plugin, version string) (executables []string, err error) {
|
||||
func ToolExecutables(conf config.Config, plugin plugins.Plugin, versionType, version string) (executables []string, err error) {
|
||||
dirs, err := ExecutableDirs(plugin)
|
||||
if err != nil {
|
||||
return executables, err
|
||||
}
|
||||
|
||||
installPath := installs.InstallPath(conf, plugin, version)
|
||||
installPath := installs.InstallPath(conf, plugin, versionType, version)
|
||||
paths := dirsToPaths(dirs, installPath)
|
||||
|
||||
for _, path := range paths {
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"asdf/internal/config"
|
||||
"asdf/internal/installs"
|
||||
"asdf/internal/installtest"
|
||||
"asdf/internal/plugins"
|
||||
"asdf/repotest"
|
||||
@ -23,7 +24,7 @@ func TestRemoveAll(t *testing.T) {
|
||||
version := "1.1.0"
|
||||
conf, plugin := generateConfig(t)
|
||||
installVersion(t, conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version)
|
||||
assert.Nil(t, err)
|
||||
stdout, stderr := buildOutputs()
|
||||
|
||||
@ -46,7 +47,7 @@ func TestGenerateAll(t *testing.T) {
|
||||
installVersion(t, conf, plugin, version)
|
||||
installPlugin(t, conf, "dummy_plugin", "ruby")
|
||||
installVersion(t, conf, plugin, version2)
|
||||
executables, err := ToolExecutables(conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version)
|
||||
assert.Nil(t, err)
|
||||
stdout, stderr := buildOutputs()
|
||||
|
||||
@ -75,7 +76,7 @@ func TestGenerateForPluginVersions(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
installVersion(t, conf, plugin, version)
|
||||
installVersion(t, conf, plugin, version2)
|
||||
executables, err := ToolExecutables(conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version)
|
||||
assert.Nil(t, err)
|
||||
stdout, stderr := buildOutputs()
|
||||
|
||||
@ -112,11 +113,12 @@ func TestGenerateForVersion(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
installVersion(t, conf, plugin, version)
|
||||
installVersion(t, conf, plugin, version2)
|
||||
executables, err := ToolExecutables(conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version)
|
||||
assert.Nil(t, err)
|
||||
|
||||
t.Run("generates shim script for every executable in version", func(t *testing.T) {
|
||||
assert.Nil(t, GenerateForVersion(conf, plugin, version))
|
||||
stdout, stderr := buildOutputs()
|
||||
assert.Nil(t, GenerateForVersion(conf, plugin, "version", version, &stdout, &stderr))
|
||||
|
||||
// check for generated shims
|
||||
for _, executable := range executables {
|
||||
@ -127,8 +129,9 @@ func TestGenerateForVersion(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("updates existing shims for every executable in version", func(t *testing.T) {
|
||||
assert.Nil(t, GenerateForVersion(conf, plugin, version))
|
||||
assert.Nil(t, GenerateForVersion(conf, plugin, version2))
|
||||
stdout, stderr := buildOutputs()
|
||||
assert.Nil(t, GenerateForVersion(conf, plugin, "version", version, &stdout, &stderr))
|
||||
assert.Nil(t, GenerateForVersion(conf, plugin, "version", version2, &stdout, &stderr))
|
||||
|
||||
// check for generated shims
|
||||
for _, executable := range executables {
|
||||
@ -145,7 +148,7 @@ func TestWrite(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
installVersion(t, conf, plugin, version)
|
||||
installVersion(t, conf, plugin, version2)
|
||||
executables, err := ToolExecutables(conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version)
|
||||
executable := executables[0]
|
||||
assert.Nil(t, err)
|
||||
|
||||
@ -205,7 +208,22 @@ func TestToolExecutables(t *testing.T) {
|
||||
installVersion(t, conf, plugin, version)
|
||||
|
||||
t.Run("returns list of executables for plugin", func(t *testing.T) {
|
||||
executables, err := ToolExecutables(conf, plugin, version)
|
||||
executables, err := ToolExecutables(conf, plugin, "version", version)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var filenames []string
|
||||
for _, executablePath := range executables {
|
||||
assert.True(t, strings.HasPrefix(executablePath, conf.DataDir))
|
||||
filenames = append(filenames, filepath.Base(executablePath))
|
||||
}
|
||||
|
||||
assert.Equal(t, filenames, []string{"dummy"})
|
||||
})
|
||||
|
||||
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)
|
||||
executables, err := ToolExecutables(conf, plugin, "path", path)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var filenames []string
|
||||
|
@ -15,22 +15,25 @@ import (
|
||||
"asdf/internal/installs"
|
||||
"asdf/internal/plugins"
|
||||
"asdf/internal/resolve"
|
||||
"asdf/internal/shims"
|
||||
)
|
||||
|
||||
const (
|
||||
systemVersion = "system"
|
||||
latestVersion = "latest"
|
||||
uninstallableVersionMsg = "uninstallable version: system"
|
||||
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"
|
||||
)
|
||||
|
||||
// UninstallableVersionError is an error returned if someone tries to install the
|
||||
// system version.
|
||||
type UninstallableVersionError struct{}
|
||||
type UninstallableVersionError struct {
|
||||
versionType string
|
||||
}
|
||||
|
||||
func (e UninstallableVersionError) Error() string {
|
||||
return fmt.Sprint(uninstallableVersionMsg)
|
||||
return fmt.Sprintf(uninstallableVersionMsg, e.versionType)
|
||||
}
|
||||
|
||||
// NoVersionSetError is returned whenever an operation that requires a version
|
||||
@ -125,14 +128,18 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
|
||||
}
|
||||
|
||||
if version == systemVersion {
|
||||
return UninstallableVersionError{}
|
||||
return UninstallableVersionError{versionType: "system"}
|
||||
}
|
||||
|
||||
downloadDir := installs.DownloadPath(conf, plugin, version)
|
||||
installDir := installs.InstallPath(conf, plugin, version)
|
||||
versionType, version := ParseString(version)
|
||||
|
||||
if installs.IsInstalled(conf, plugin, version) {
|
||||
if versionType == "path" {
|
||||
return UninstallableVersionError{versionType: "path"}
|
||||
}
|
||||
downloadDir := installs.DownloadPath(conf, plugin, versionType, version)
|
||||
installDir := installs.InstallPath(conf, plugin, versionType, version)
|
||||
|
||||
if installs.IsInstalled(conf, plugin, versionType, version) {
|
||||
return fmt.Errorf("version %s of %s is already installed", version, plugin.Name)
|
||||
}
|
||||
|
||||
@ -174,6 +181,12 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
|
||||
return fmt.Errorf("failed to run install callback: %w", err)
|
||||
}
|
||||
|
||||
// Reshim
|
||||
err = shims.GenerateAll(conf, stdOut, stdErr)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run post-install hook: %w", err)
|
||||
@ -297,8 +310,20 @@ func parseVersions(rawVersions string) []string {
|
||||
// ParseString parses a version string into versionType and version components
|
||||
func ParseString(version string) (string, string) {
|
||||
segments := strings.Split(version, ":")
|
||||
if len(segments) >= 1 && segments[0] == "ref" {
|
||||
return "ref", strings.Join(segments[1:], ":")
|
||||
if len(segments) >= 1 {
|
||||
remainder := strings.Join(segments[1:], ":")
|
||||
switch segments[0] {
|
||||
case "ref":
|
||||
return "ref", 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
|
||||
default:
|
||||
return "version", version
|
||||
}
|
||||
}
|
||||
|
||||
return "version", version
|
||||
|
@ -164,6 +164,14 @@ func TestInstallOneVersion(t *testing.T) {
|
||||
assert.IsType(t, plugins.PluginMissing{}, err)
|
||||
})
|
||||
|
||||
t.Run("returns error when passed a path version", func(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
stdout, stderr := buildOutputs()
|
||||
err := InstallOneVersion(conf, plugin, "path:/foo/bar", &stdout, &stderr)
|
||||
|
||||
assert.ErrorContains(t, err, "uninstallable version: path")
|
||||
})
|
||||
|
||||
t.Run("returns error when plugin version is 'system'", func(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
stdout, stderr := buildOutputs()
|
||||
|
10
main_test.go
10
main_test.go
@ -71,9 +71,9 @@ func TestBatsTests(t *testing.T) {
|
||||
// runBatsFile(t, dir, "remove_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("reshim_command", func(t *testing.T) {
|
||||
// runBatsFile(t, dir, "reshim_command.bats")
|
||||
//})
|
||||
t.Run("reshim_command", func(t *testing.T) {
|
||||
runBatsFile(t, dir, "reshim_command.bats")
|
||||
})
|
||||
|
||||
//t.Run("shim_env_command", func(t *testing.T) {
|
||||
// runBatsFile(t, dir, "shim_env_command.bats")
|
||||
@ -91,10 +91,6 @@ func TestBatsTests(t *testing.T) {
|
||||
// runBatsFile(t, dir, "uninstall_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("update_command", func(t *testing.T) {
|
||||
// runBatsFile(t, dir, "update_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("version_commands", func(t *testing.T) {
|
||||
// runBatsFile(t, dir, "version_commands.bats")
|
||||
//})
|
||||
|
Loading…
Reference in New Issue
Block a user