Skip to content

Commit 924eecf

Browse files
committed
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
1 parent 9d9fc69 commit 924eecf

File tree

12 files changed

+239
-143
lines changed

12 files changed

+239
-143
lines changed

cmd/cmd.go

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ func getVersionInfo(conf config.Config, plugin plugins.Plugin, currentDir string
299299
installed := false
300300
if found {
301301
firstVersion := toolversion.Versions[0]
302-
versionType, version := toolversions.Parse(firstVersion)
303-
installed = installs.IsInstalled(conf, plugin, versionType, version)
302+
version := toolversions.Parse(firstVersion)
303+
installed = installs.IsInstalled(conf, plugin, version)
304304
}
305305
return toolversion, found, installed
306306
}
@@ -354,13 +354,41 @@ func execCommand(logger *log.Logger, command string, args []string) error {
354354
}
355355

356356
executable, found, err := shims.FindExecutable(conf, command, currentDir)
357+
357358
if err != nil {
358-
logger.Printf("executable not found due to reason: %s", err.Error())
359+
360+
shimPath := shims.Path(conf, command)
361+
toolVersions, _ := shims.GetToolsAndVersionsFromShimFile(shimPath)
362+
363+
if len(toolVersions) > 0 {
364+
if anyInstalled(conf, toolVersions) {
365+
logger.Printf("No version is set for command %s", command)
366+
logger.Printf("Consider adding one of the following versions in your config file at %s/.tool-versions\n", currentDir)
367+
} else {
368+
logger.Printf("No preset version installed for command %s", command)
369+
for _, toolVersion := range toolVersions {
370+
for _, version := range toolVersion.Versions {
371+
fmt.Printf("asdf install %s %s\n", toolVersion.Name, version)
372+
}
373+
}
374+
375+
fmt.Printf("or add one of the following versions in your config file at %s/.tool-versions\n", currentDir)
376+
}
377+
378+
for _, toolVersion := range toolVersions {
379+
for _, version := range toolVersion.Versions {
380+
fmt.Printf("%s %s", toolVersion.Name, version)
381+
}
382+
}
383+
}
384+
385+
os.Exit(126)
359386
return err
360387
}
361388

362389
if !found {
363390
logger.Print("executable not found")
391+
os.Exit(126)
364392
return fmt.Errorf("executable not found")
365393
}
366394
if len(args) > 1 {
@@ -372,6 +400,19 @@ func execCommand(logger *log.Logger, command string, args []string) error {
372400
return exec.Exec(executable, args, os.Environ())
373401
}
374402

403+
func anyInstalled(conf config.Config, toolVersions []toolversions.ToolVersions) bool {
404+
for _, toolVersion := range toolVersions {
405+
for _, version := range toolVersion.Versions {
406+
version := toolversions.Parse(version)
407+
plugin := plugins.New(conf, toolVersion.Name)
408+
if installs.IsInstalled(conf, plugin, version) {
409+
return true
410+
}
411+
}
412+
}
413+
return false
414+
}
415+
375416
func pluginAddCommand(_ *cli.Context, conf config.Config, logger *log.Logger, pluginName, pluginRepo string) error {
376417
if pluginName == "" {
377418
// Invalid arguments
@@ -577,10 +618,10 @@ func installCommand(logger *log.Logger, toolName, version string) error {
577618
return err
578619
}
579620
} else {
580-
parsedVersion, query := toolversions.ParseFromCliArg(version)
621+
parsedVersion := toolversions.ParseFromCliArg(version)
581622

582-
if parsedVersion == "latest" {
583-
err = versions.InstallVersion(conf, plugin, version, query, os.Stdout, os.Stderr)
623+
if parsedVersion.Type == "latest" {
624+
err = versions.InstallVersion(conf, plugin, version, parsedVersion.Value, os.Stdout, os.Stderr)
584625
} else {
585626
err = versions.InstallOneVersion(conf, plugin, version, os.Stdout, os.Stderr)
586627
}
@@ -890,7 +931,7 @@ func uninstallCommand(logger *log.Logger, tool, version string) error {
890931
return shims.GenerateAll(conf, os.Stdout, os.Stderr)
891932
}
892933

893-
func whereCommand(logger *log.Logger, tool, version string) error {
934+
func whereCommand(logger *log.Logger, tool, versionStr string) error {
894935
conf, err := config.LoadConfig()
895936
if err != nil {
896937
logger.Printf("error loading config: %s", err)
@@ -912,20 +953,28 @@ func whereCommand(logger *log.Logger, tool, version string) error {
912953
return err
913954
}
914955

915-
versionType, parsedVersion := toolversions.Parse(version)
956+
version := toolversions.Parse(versionStr)
916957

917-
if version == "" {
958+
if version.Type == "system" {
959+
logger.Printf("System version is selected")
960+
return errors.New("System version is selected")
961+
}
962+
963+
if version.Value == "" {
918964
// resolve version
919-
toolversions, found, err := resolve.Version(conf, plugin, currentDir)
965+
versions, found, err := resolve.Version(conf, plugin, currentDir)
920966
if err != nil {
921967
fmt.Printf("err %#+v\n", err)
922968
return err
923969
}
924970

925-
if found && len(toolversions.Versions) > 0 && installs.IsInstalled(conf, plugin, "version", toolversions.Versions[0]) {
926-
installPath := installs.InstallPath(conf, plugin, "version", toolversions.Versions[0])
927-
logger.Printf("%s", installPath)
928-
return nil
971+
if found && len(versions.Versions) > 0 {
972+
versionStruct := toolversions.Version{Type: "version", Value: versions.Versions[0]}
973+
if installs.IsInstalled(conf, plugin, versionStruct) {
974+
installPath := installs.InstallPath(conf, plugin, versionStruct)
975+
logger.Printf("%s", installPath)
976+
return nil
977+
}
929978
}
930979

931980
// not found
@@ -934,25 +983,20 @@ func whereCommand(logger *log.Logger, tool, version string) error {
934983
return errors.New(msg)
935984
}
936985

937-
if version == "system" {
938-
logger.Printf("System version is selected")
939-
return errors.New("System version is selected")
940-
}
941-
942-
if !installs.IsInstalled(conf, plugin, versionType, parsedVersion) {
986+
if !installs.IsInstalled(conf, plugin, version) {
943987
logger.Printf("Version not installed")
944988
return errors.New("Version not installed")
945989
}
946990

947-
installPath := installs.InstallPath(conf, plugin, versionType, parsedVersion)
991+
installPath := installs.InstallPath(conf, plugin, version)
948992
logger.Printf("%s", installPath)
949993

950994
return nil
951995
}
952996

953-
func reshimToolVersion(conf config.Config, tool, version string, out io.Writer, errOut io.Writer) error {
954-
versionType, version := toolversions.Parse(version)
955-
return shims.GenerateForVersion(conf, plugins.New(conf, tool), versionType, version, out, errOut)
997+
func reshimToolVersion(conf config.Config, tool, versionStr string, out io.Writer, errOut io.Writer) error {
998+
version := toolversions.Parse(versionStr)
999+
return shims.GenerateForVersion(conf, plugins.New(conf, tool), version.Type, version.Value, out, errOut)
9561000
}
9571001

9581002
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
9711015
}
9721016

9731017
if showStatus {
974-
installed := installs.IsInstalled(conf, plugin, "version", latest)
1018+
installed := installs.IsInstalled(conf, plugin, toolversions.Version{Type: "version", Value: latest})
9751019
fmt.Printf("%s\t%s\t%s\n", plugin.Name, latest, installedStatus(installed))
9761020
} else {
9771021
fmt.Printf("%s\n", latest)

docs/guide/upgrading-from-v0-14-to-v0-15.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ not an executable. The new rewrite removes all shell code from asdf, and it is
4343
now a binary rather than a shell function, so setting environment variables
4444
directly in the shell is no longer possible.
4545

46+
### Executables Shims Resolve to Must Runnable by `syscall.Exec`
47+
48+
The most obvious example of this breaking change are scripts that lack a proper
49+
shebang line. asdf 0.14.1 and older were implemented in Bash, so as long it was
50+
an executable that could be executed with Bash it would run. This mean that
51+
scripts lacking a shebang could still be run by `asdf exec`. With asdf 0.15.x
52+
implemented in Go we now invoke executables via Go's `syscall.Exec` function,
53+
which cannot handle scripts lacking a shebang.
54+
55+
In practice this isn't much of a problem. Most shell scripts DO contain a
56+
shebang line. If a tool managed by asdf provides scripts that don't have a
57+
shebang line one will need to be added to them.
58+
4659
## Installation
4760

4861
Installation of version 0.15.0 is much simpler than previous versions of asdf. It's just three steps:

internal/help/help.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ func writePluginHelp(conf config.Config, toolName, toolVersion string, writer io
8080
}
8181

8282
if toolVersion != "" {
83-
versionType, version := toolversions.Parse(toolVersion)
84-
env["ASDF_INSTALL_VERSION"] = version
85-
env["ASDF_INSTALL_TYPE"] = versionType
83+
version := toolversions.Parse(toolVersion)
84+
env["ASDF_INSTALL_VERSION"] = version.Value
85+
env["ASDF_INSTALL_TYPE"] = version.Type
8686
}
8787

8888
if err := plugin.Exists(); err != nil {

internal/installs/installs.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,26 @@ func Installed(conf config.Config, plugin plugins.Plugin) (versions []string, er
3838
}
3939

4040
// InstallPath returns the path to a tool installation
41-
func InstallPath(conf config.Config, plugin plugins.Plugin, versionType, version string) string {
42-
if versionType == "path" {
43-
return version
41+
func InstallPath(conf config.Config, plugin plugins.Plugin, version toolversions.Version) string {
42+
if version.Type == "path" {
43+
return version.Value
4444
}
4545

46-
return filepath.Join(data.InstallDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(versionType, version))
46+
return filepath.Join(data.InstallDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(version))
4747
}
4848

4949
// DownloadPath returns the download path for a particular plugin and version
50-
func DownloadPath(conf config.Config, plugin plugins.Plugin, versionType, version string) string {
51-
if versionType == "path" {
50+
func DownloadPath(conf config.Config, plugin plugins.Plugin, version toolversions.Version) string {
51+
if version.Type == "path" {
5252
return ""
5353
}
5454

55-
return filepath.Join(data.DownloadDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(versionType, version))
55+
return filepath.Join(data.DownloadDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(version))
5656
}
5757

5858
// IsInstalled checks if a specific version of a tool is installed
59-
func IsInstalled(conf config.Config, plugin plugins.Plugin, versionType, version string) bool {
60-
installDir := InstallPath(conf, plugin, versionType, version)
59+
func IsInstalled(conf config.Config, plugin plugins.Plugin, version toolversions.Version) bool {
60+
installDir := InstallPath(conf, plugin, version)
6161

6262
// Check if version already installed
6363
_, err := os.Stat(installDir)

internal/installs/installs_test.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"asdf/internal/config"
99
"asdf/internal/installtest"
1010
"asdf/internal/plugins"
11+
"asdf/internal/toolversions"
1112
"asdf/repotest"
1213

1314
"github.com/stretchr/testify/assert"
@@ -19,12 +20,14 @@ func TestDownloadPath(t *testing.T) {
1920
conf, plugin := generateConfig(t)
2021

2122
t.Run("returns empty string when given path version", func(t *testing.T) {
22-
path := DownloadPath(conf, plugin, "path", "foo/bar")
23+
version := toolversions.Version{Type: "path", Value: "foo/bar"}
24+
path := DownloadPath(conf, plugin, version)
2325
assert.Empty(t, path)
2426
})
2527

2628
t.Run("returns empty string when given path version", func(t *testing.T) {
27-
path := DownloadPath(conf, plugin, "version", "1.2.3")
29+
version := toolversions.Version{Type: "version", Value: "1.2.3"}
30+
path := DownloadPath(conf, plugin, version)
2831
assert.Equal(t, path, filepath.Join(conf.DataDir, "downloads", "lua", "1.2.3"))
2932
})
3033
}
@@ -33,12 +36,14 @@ func TestInstallPath(t *testing.T) {
3336
conf, plugin := generateConfig(t)
3437

3538
t.Run("returns empty string when given path version", func(t *testing.T) {
36-
path := InstallPath(conf, plugin, "path", "foo/bar")
39+
version := toolversions.Version{Type: "path", Value: "foo/bar"}
40+
path := InstallPath(conf, plugin, version)
3741
assert.Equal(t, path, "foo/bar")
3842
})
3943

4044
t.Run("returns install path when given regular version as version", func(t *testing.T) {
41-
path := InstallPath(conf, plugin, "version", "1.2.3")
45+
version := toolversions.Version{Type: "version", Value: "1.2.3"}
46+
path := InstallPath(conf, plugin, version)
4247
assert.Equal(t, path, filepath.Join(conf.DataDir, "installs", "lua", "1.2.3"))
4348
})
4449
}
@@ -66,10 +71,12 @@ func TestIsInstalled(t *testing.T) {
6671
installVersion(t, conf, plugin, "1.0.0")
6772

6873
t.Run("returns false when not installed", func(t *testing.T) {
69-
assert.False(t, IsInstalled(conf, plugin, "version", "4.0.0"))
74+
version := toolversions.Version{Type: "version", Value: "4.0.0"}
75+
assert.False(t, IsInstalled(conf, plugin, version))
7076
})
7177
t.Run("returns true when installed", func(t *testing.T) {
72-
assert.True(t, IsInstalled(conf, plugin, "version", "1.0.0"))
78+
version := toolversions.Version{Type: "version", Value: "1.0.0"}
79+
assert.True(t, IsInstalled(conf, plugin, version))
7380
})
7481
}
7582

@@ -87,9 +94,10 @@ func generateConfig(t *testing.T) (config.Config, plugins.Plugin) {
8794
return conf, plugins.New(conf, testPluginName)
8895
}
8996

90-
func mockInstall(t *testing.T, conf config.Config, plugin plugins.Plugin, version string) {
97+
func mockInstall(t *testing.T, conf config.Config, plugin plugins.Plugin, versionStr string) {
9198
t.Helper()
92-
path := InstallPath(conf, plugin, "version", version)
99+
version := toolversions.Version{Type: "version", Value: versionStr}
100+
path := InstallPath(conf, plugin, version)
93101
err := os.MkdirAll(path, os.ModePerm)
94102
assert.Nil(t, err)
95103
}

internal/shims/shims.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func getCustomExecutablePath(conf config.Config, plugin plugins.Plugin, shimName
173173
var stdOut strings.Builder
174174
var stdErr strings.Builder
175175

176-
installPath := installs.InstallPath(conf, plugin, "version", version)
176+
installPath := installs.InstallPath(conf, plugin, toolversions.Version{Type: "version", Value: version})
177177
env := map[string]string{"ASDF_INSTALL_TYPE": "version"}
178178

179179
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
303303
return executables, err
304304
}
305305

306-
installPath := installs.InstallPath(conf, plugin, versionType, version)
306+
installPath := installs.InstallPath(conf, plugin, toolversions.Version{Type: versionType, Value: version})
307307
paths := dirsToPaths(dirs, installPath)
308308

309309
for _, path := range paths {

0 commit comments

Comments
 (0)