Skip to content

Commit

Permalink
Merge pull request #71 from asdf-vm/tb/asdf-where-command
Browse files Browse the repository at this point in the history
feat(golang-rewrite): create `asdf where` command
  • Loading branch information
Stratus3D authored Oct 7, 2024
2 parents 44651dd + 034e5bb commit 94eef94
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 53 deletions.
74 changes: 72 additions & 2 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"asdf/internal/plugins"
"asdf/internal/resolve"
"asdf/internal/shims"
"asdf/internal/toolversions"
"asdf/internal/versions"

"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -175,6 +176,15 @@ func Execute(version string) {
return reshimCommand(logger, args.Get(0), args.Get(1))
},
},
{
Name: "where",
Action: func(cCtx *cli.Context) error {
tool := cCtx.Args().Get(0)
version := cCtx.Args().Get(1)

return whereCommand(logger, tool, version)
},
},
{
Name: "which",
Action: func(cCtx *cli.Context) error {
Expand Down Expand Up @@ -265,7 +275,7 @@ func getVersionInfo(conf config.Config, plugin plugins.Plugin, currentDir string
installed := false
if found {
firstVersion := toolversion.Versions[0]
versionType, version := versions.ParseString(firstVersion)
versionType, version := toolversions.Parse(firstVersion)
installed = installs.IsInstalled(conf, plugin, versionType, version)
}
return toolversion, found, installed
Expand Down Expand Up @@ -663,8 +673,68 @@ func whichCommand(logger *log.Logger, command string) error {
return nil
}

func whereCommand(logger *log.Logger, tool, version string) error {
conf, err := config.LoadConfig()
if err != nil {
logger.Printf("error loading config: %s", err)
return err
}

currentDir, err := os.Getwd()
if err != nil {
logger.Printf("unable to get current directory: %s", err)
return err
}

plugin := plugins.New(conf, tool)
err = plugin.Exists()
if err != nil {
if _, ok := err.(plugins.PluginMissing); ok {
logger.Printf("No such plugin: %s", tool)
}
return err
}

versionType, parsedVersion := toolversions.Parse(version)

if version == "" {
// resolve version
toolversions, 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
}

// not found
msg := fmt.Sprintf("No version is set for %s; please run `asdf <global | shell | local> %s <version>`", tool, tool)
logger.Print(msg)
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) {
logger.Printf("Version not installed")
return errors.New("Version not installed")
}

installPath := installs.InstallPath(conf, plugin, versionType, parsedVersion)
logger.Printf("%s", installPath)

return nil
}

func reshimToolVersion(conf config.Config, tool, version string, out io.Writer, errOut io.Writer) error {
versionType, version := versions.ParseString(version)
versionType, version := toolversions.Parse(version)
return shims.GenerateForVersion(conf, plugins.New(conf, tool), versionType, version, out, errOut)
}

Expand Down
4 changes: 2 additions & 2 deletions internal/help/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

"asdf/internal/config"
"asdf/internal/plugins"
"asdf/internal/versions"
"asdf/internal/toolversions"
)

//go:embed help.txt
Expand Down Expand Up @@ -80,7 +80,7 @@ func writePluginHelp(conf config.Config, toolName, toolVersion string, writer io
}

if toolVersion != "" {
versionType, version := versions.ParseString(toolVersion)
versionType, version := toolversions.Parse(toolVersion)
env["ASDF_INSTALL_VERSION"] = version
env["ASDF_INSTALL_TYPE"] = versionType
}
Expand Down
7 changes: 5 additions & 2 deletions internal/installs/installs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"asdf/internal/config"
"asdf/internal/plugins"
"asdf/internal/toolversions"
)

const (
Expand Down Expand Up @@ -45,15 +46,17 @@ func InstallPath(conf config.Config, plugin plugins.Plugin, versionType, version
if versionType == "path" {
return version
}
return filepath.Join(pluginInstallPath(conf, plugin), version)

return filepath.Join(pluginInstallPath(conf, plugin), toolversions.FormatForFS(versionType, 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" {
return ""
}
return filepath.Join(conf.DataDir, dataDirDownloads, plugin.Name, version)

return filepath.Join(conf.DataDir, dataDirDownloads, plugin.Name, toolversions.FormatForFS(versionType, version))
}

// IsInstalled checks if a specific version of a tool is installed
Expand Down
37 changes: 36 additions & 1 deletion internal/toolversions/toolversions.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Package toolversions handles reading and writing tools and versions from
// asdf's .tool-versions files
// asdf's .tool-versions files. It also handles parsing version strings from
// .tool-versions files and command line arguments.
package toolversions

import (
"fmt"
"os"
"slices"
"strings"
Expand Down Expand Up @@ -81,6 +83,39 @@ func Unique(versions []ToolVersions) (uniques []ToolVersions) {
return uniques
}

// Parse parses a version string into versionType and version components
func Parse(version string) (string, string) {
segments := strings.Split(version, ":")
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
}

// 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 {
case "ref":
return fmt.Sprintf("ref-%s", version)
default:
return version
}
}

// readLines reads all the lines in a given file
// removing spaces and comments which are marked by '#'
func readLines(content string) (lines []string) {
Expand Down
30 changes: 30 additions & 0 deletions internal/toolversions/toolversions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,33 @@ 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("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("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, "")
})
}

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")
})

t.Run("returns version prefixed with 'ref-' when version type is ref", func(t *testing.T) {
assert.Equal(t, FormatForFS("ref", "foobar"), "ref-foobar")
})
}
25 changes: 2 additions & 23 deletions internal/versions/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"asdf/internal/plugins"
"asdf/internal/resolve"
"asdf/internal/shims"
"asdf/internal/toolversions"
)

const (
Expand Down Expand Up @@ -131,7 +132,7 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, version string
return UninstallableVersionError{versionType: "system"}
}

versionType, version := ParseString(version)
versionType, version := toolversions.Parse(version)

if versionType == "path" {
return UninstallableVersionError{versionType: "path"}
Expand Down Expand Up @@ -306,25 +307,3 @@ func parseVersions(rawVersions string) []string {
}
return versions
}

// ParseString parses a version string into versionType and version components
func ParseString(version string) (string, string) {
segments := strings.Split(version, ":")
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
}
20 changes: 0 additions & 20 deletions internal/versions/versions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,26 +308,6 @@ func TestLatest(t *testing.T) {
})
}

func TestParseString(t *testing.T) {
t.Run("returns 'version', and unmodified version when passed semantic version", func(t *testing.T) {
versionType, version := ParseString("1.2.3")
assert.Equal(t, versionType, "version")
assert.Equal(t, version, "1.2.3")
})

t.Run("returns 'ref' and reference version when passed a ref version", func(t *testing.T) {
versionType, version := ParseString("ref:abc123")
assert.Equal(t, versionType, "ref")
assert.Equal(t, version, "abc123")
})

t.Run("returns 'ref' and empty string when passed 'ref:'", func(t *testing.T) {
versionType, version := ParseString("ref:")
assert.Equal(t, versionType, "ref")
assert.Equal(t, version, "")
})
}

func TestAllVersions(t *testing.T) {
pluginName := "list-all-test"
conf, _ := generateConfig(t)
Expand Down
6 changes: 3 additions & 3 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ func TestBatsTests(t *testing.T) {
// runBatsFile(t, dir, "version_commands.bats")
//})

//t.Run("where_command", func(t *testing.T) {
// runBatsFile(t, dir, "where_command.bats")
//})
t.Run("where_command", func(t *testing.T) {
runBatsFile(t, dir, "where_command.bats")
})

t.Run("which_command", func(t *testing.T) {
runBatsFile(t, dir, "which_command.bats")
Expand Down
1 change: 1 addition & 0 deletions test/where_command.bats
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ setup() {
install_dummy_version 1.0
install_dummy_version 2.1
install_dummy_version ref-master
cd "$HOME" || exit
}

teardown() {
Expand Down

0 comments on commit 94eef94

Please sign in to comment.