Skip to content

Commit

Permalink
Merge pull request #77 from asdf-vm/tb/shim-exec-bats
Browse files Browse the repository at this point in the history
feat(golang-rewrite): introduce `Version` struct, get some `shim_exec.bats` tests passing
  • Loading branch information
Stratus3D authored Nov 23, 2024
2 parents 2c4d5e6 + ba86ee0 commit 357ae08
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 143 deletions.
94 changes: 69 additions & 25 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions docs/guide/upgrading-from-v0-14-to-v0-15.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions internal/help/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
18 changes: 9 additions & 9 deletions internal/installs/installs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
24 changes: 16 additions & 8 deletions internal/installs/installs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"asdf/internal/config"
"asdf/internal/installtest"
"asdf/internal/plugins"
"asdf/internal/toolversions"
"asdf/repotest"

"github.com/stretchr/testify/assert"
Expand All @@ -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"))
})
}
Expand All @@ -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"))
})
}
Expand Down Expand Up @@ -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))
})
}

Expand All @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/shims/shims.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 357ae08

Please sign in to comment.