mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-19 09:55:01 -07:00
Merge pull request #70 from asdf-vm/tb/asdf-help-command
feat(golang-rewrite): implement `asdf help` command
This commit is contained in:
commit
ec8985af8f
39
cmd/cmd.go
39
cmd/cmd.go
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"asdf/internal/config"
|
||||
"asdf/internal/exec"
|
||||
"asdf/internal/help"
|
||||
"asdf/internal/info"
|
||||
"asdf/internal/installs"
|
||||
"asdf/internal/plugins"
|
||||
@ -65,6 +66,14 @@ func Execute(version string) {
|
||||
return execCommand(logger, command, args)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "help",
|
||||
Action: func(cCtx *cli.Context) error {
|
||||
toolName := cCtx.Args().Get(0)
|
||||
toolVersion := cCtx.Args().Get(1)
|
||||
return helpCommand(logger, version, toolName, toolVersion)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "info",
|
||||
Action: func(_ *cli.Context) error {
|
||||
@ -408,6 +417,36 @@ func infoCommand(conf config.Config, version string) error {
|
||||
return info.Print(conf, version)
|
||||
}
|
||||
|
||||
func helpCommand(logger *log.Logger, asdfVersion, tool, version string) error {
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Printf("error loading config: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if tool != "" {
|
||||
if version != "" {
|
||||
err := help.PrintToolVersion(conf, tool, version)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err := help.PrintTool(conf, tool)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err = help.Print(asdfVersion)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func pluginUpdateCommand(cCtx *cli.Context, logger *log.Logger, pluginName, ref string) error {
|
||||
updateAll := cCtx.Bool("all")
|
||||
if !updateAll && pluginName == "" {
|
||||
|
120
internal/help/help.go
Normal file
120
internal/help/help.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Package help contains functions responsible for generating help output for
|
||||
// asdf and asdf plugins.
|
||||
package help
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"asdf/internal/config"
|
||||
"asdf/internal/plugins"
|
||||
"asdf/internal/versions"
|
||||
)
|
||||
|
||||
//go:embed help.txt
|
||||
var helpText string
|
||||
|
||||
const quote = "\"Late but latest\"\n-- Rajinikanth"
|
||||
|
||||
// Print help output to STDOUT
|
||||
func Print(asdfVersion string) error {
|
||||
return Write(asdfVersion, os.Stdout)
|
||||
}
|
||||
|
||||
// PrintTool write tool help output to STDOUT
|
||||
func PrintTool(conf config.Config, toolName string) error {
|
||||
return WriteToolHelp(conf, toolName, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
// PrintToolVersion write help for specific tool version to STDOUT
|
||||
func PrintToolVersion(conf config.Config, toolName, toolVersion string) error {
|
||||
return WriteToolVersionHelp(conf, toolName, toolVersion, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
// Write help output to an io.Writer
|
||||
func Write(asdfVersion string, writer io.Writer) error {
|
||||
_, err := writer.Write([]byte(fmt.Sprintf("version: %s", asdfVersion)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte(helpText))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte(quote))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteToolHelp output to an io.Writer
|
||||
func WriteToolHelp(conf config.Config, toolName string, writer io.Writer, errWriter io.Writer) error {
|
||||
return writePluginHelp(conf, toolName, "", writer, errWriter)
|
||||
}
|
||||
|
||||
// WriteToolVersionHelp output to an io.Writer
|
||||
func WriteToolVersionHelp(conf config.Config, toolName, toolVersion string, writer io.Writer, errWriter io.Writer) error {
|
||||
return writePluginHelp(conf, toolName, toolVersion, writer, errWriter)
|
||||
}
|
||||
|
||||
func writePluginHelp(conf config.Config, toolName, toolVersion string, writer io.Writer, errWriter io.Writer) error {
|
||||
plugin := plugins.New(conf, toolName)
|
||||
env := map[string]string{
|
||||
"ASDF_INSTALL_PATH": plugin.Dir,
|
||||
}
|
||||
|
||||
if toolVersion != "" {
|
||||
versionType, version := versions.ParseString(toolVersion)
|
||||
env["ASDF_INSTALL_VERSION"] = version
|
||||
env["ASDF_INSTALL_TYPE"] = versionType
|
||||
}
|
||||
|
||||
if err := plugin.Exists(); err != nil {
|
||||
errWriter.Write([]byte(fmt.Sprintf("No plugin named %s\n", plugin.Name)))
|
||||
return err
|
||||
}
|
||||
|
||||
err := plugin.RunCallback("help.overview", []string{}, env, writer, errWriter)
|
||||
if _, ok := err.(plugins.NoCallbackError); ok {
|
||||
// No such callback, print err msg
|
||||
errWriter.Write([]byte(fmt.Sprintf("No documentation for plugin %s\n", plugin.Name)))
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = plugin.RunCallback("help.deps", []string{}, env, writer, errWriter)
|
||||
if _, ok := err.(plugins.NoCallbackError); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
err = plugin.RunCallback("help.config", []string{}, env, writer, errWriter)
|
||||
if _, ok := err.(plugins.NoCallbackError); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
err = plugin.RunCallback("help.links", []string{}, env, writer, errWriter)
|
||||
if _, ok := err.(plugins.NoCallbackError); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
66
internal/help/help.txt
Normal file
66
internal/help/help.txt
Normal file
@ -0,0 +1,66 @@
|
||||
MANAGE PLUGINS
|
||||
asdf plugin add <name> [<git-url>] Add a plugin from the plugin repo OR,
|
||||
add a Git repo as a plugin by
|
||||
specifying the name and repo url
|
||||
asdf plugin list [--urls] [--refs] List installed plugins. Optionally show
|
||||
git urls and git-ref
|
||||
asdf plugin list all List plugins registered on asdf-plugins
|
||||
repository with URLs
|
||||
asdf plugin remove <name> Remove plugin and package versions
|
||||
asdf plugin update <name> [<git-ref>] Update a plugin to latest commit on
|
||||
default branch or a particular git-ref
|
||||
asdf plugin update --all Update all plugins to latest commit on
|
||||
default branch
|
||||
|
||||
|
||||
MANAGE TOOLS
|
||||
asdf current Display current version set or being
|
||||
used for all packages
|
||||
asdf current <name> Display current version set or being
|
||||
used for package
|
||||
asdf global <name> <version> Set the package global version
|
||||
asdf global <name> latest[:<version>] Set the package global version to the
|
||||
latest provided version
|
||||
asdf help <name> [<version>] Output documentation for plugin and tool
|
||||
asdf install Install all the package versions listed
|
||||
in the .tool-versions file
|
||||
asdf install <name> Install one tool at the version
|
||||
specified in the .tool-versions file
|
||||
asdf install <name> <version> Install a specific version of a package
|
||||
asdf install <name> latest[:<version>] Install the latest stable version of a
|
||||
package, or with optional version,
|
||||
install the latest stable version that
|
||||
begins with the given string
|
||||
asdf latest <name> [<version>] Show latest stable version of a package
|
||||
asdf latest --all Show latest stable version of all the
|
||||
packages and if they are installed
|
||||
asdf list <name> [version] List installed versions of a package and
|
||||
optionally filter the versions
|
||||
asdf list all <name> [<version>] List all versions of a package and
|
||||
optionally filter the returned versions
|
||||
asdf local <name> <version> Set the package local version
|
||||
asdf local <name> latest[:<version>] Set the package local version to the
|
||||
latest provided version
|
||||
asdf shell <name> <version> Set the package version to
|
||||
`ASDF_${LANG}_VERSION` in the current shell
|
||||
asdf uninstall <name> <version> Remove a specific version of a package
|
||||
asdf where <name> [<version>] Display install path for an installed
|
||||
or current version
|
||||
asdf which <command> Display the path to an executable
|
||||
|
||||
|
||||
UTILS
|
||||
asdf exec <command> [args...] Executes the command shim for current version
|
||||
asdf env <command> [util] Runs util (default: `env`) inside the
|
||||
environment used for command shim execution.
|
||||
asdf info Print OS, Shell and ASDF debug information.
|
||||
asdf version Print the currently installed version of ASDF
|
||||
asdf reshim <name> <version> Recreate shims for version of a package
|
||||
asdf shim-versions <command> List the plugins and versions that
|
||||
provide a command
|
||||
asdf update Update asdf to the latest stable release
|
||||
asdf update --head Update asdf to the latest on the master branch
|
||||
|
||||
RESOURCES
|
||||
GitHub: https://github.com/asdf-vm/asdf
|
||||
Docs: https://asdf-vm.com
|
133
internal/help/help_test.go
Normal file
133
internal/help/help_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
package help
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"asdf/internal/config"
|
||||
"asdf/internal/plugins"
|
||||
"asdf/repotest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
version = "0.15.0"
|
||||
testPluginName = "lua"
|
||||
)
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
testDataDir := t.TempDir()
|
||||
err := os.MkdirAll(filepath.Join(testDataDir, "plugins"), 0o777)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var stdout strings.Builder
|
||||
|
||||
err = Write(version, &stdout)
|
||||
assert.Nil(t, err)
|
||||
output := stdout.String()
|
||||
|
||||
// Simple format assertions
|
||||
assert.Contains(t, output, "version: ")
|
||||
assert.Contains(t, output, "MANAGE PLUGINS\n")
|
||||
assert.Contains(t, output, "MANAGE TOOLS\n")
|
||||
assert.Contains(t, output, "UTILS\n")
|
||||
assert.Contains(t, output, "RESOURCES\n")
|
||||
}
|
||||
|
||||
func TestWriteToolHelp(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
|
||||
t.Run("when plugin implements all help callbacks", func(t *testing.T) {
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
|
||||
err := WriteToolHelp(conf, plugin.Name, &stdout, &stderr)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Empty(t, stderr.String())
|
||||
expected := "Dummy plugin documentation\n\nDummy plugin is a plugin only used for unit tests\n"
|
||||
assert.Equal(t, stdout.String(), expected)
|
||||
})
|
||||
|
||||
t.Run("when plugin does not have help.overview callback", func(t *testing.T) {
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
plugin := installPlugin(t, conf, "dummy_legacy_plugin", "legacy-plugin")
|
||||
|
||||
err := WriteToolHelp(conf, plugin.Name, &stdout, &stderr)
|
||||
|
||||
assert.EqualError(t, err, "Plugin named legacy-plugin does not have a callback named help.overview")
|
||||
assert.Empty(t, stdout.String())
|
||||
assert.Equal(t, stderr.String(), "No documentation for plugin legacy-plugin\n")
|
||||
})
|
||||
|
||||
t.Run("when plugin does not exist", func(t *testing.T) {
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
|
||||
err := WriteToolHelp(conf, "non-existent", &stdout, &stderr)
|
||||
|
||||
assert.EqualError(t, err, "Plugin named non-existent not installed")
|
||||
assert.Empty(t, stdout.String())
|
||||
assert.Equal(t, stderr.String(), "No plugin named non-existent\n")
|
||||
})
|
||||
}
|
||||
|
||||
func TestWriteToolVersionHelp(t *testing.T) {
|
||||
conf, plugin := generateConfig(t)
|
||||
|
||||
t.Run("when plugin implements all help callbacks", func(t *testing.T) {
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
|
||||
err := WriteToolVersionHelp(conf, plugin.Name, "1.2.3", &stdout, &stderr)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Empty(t, stderr.String())
|
||||
expected := "Dummy plugin documentation\n\nDummy plugin is a plugin only used for unit tests\n\nDetails specific for version 1.2.3\n"
|
||||
assert.Equal(t, stdout.String(), expected)
|
||||
})
|
||||
|
||||
t.Run("when plugin does not have help.overview callback", func(t *testing.T) {
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
plugin := installPlugin(t, conf, "dummy_legacy_plugin", "legacy-plugin")
|
||||
|
||||
err := WriteToolVersionHelp(conf, plugin.Name, "1.2.3", &stdout, &stderr)
|
||||
|
||||
assert.EqualError(t, err, "Plugin named legacy-plugin does not have a callback named help.overview")
|
||||
assert.Empty(t, stdout.String())
|
||||
assert.Equal(t, stderr.String(), "No documentation for plugin legacy-plugin\n")
|
||||
})
|
||||
|
||||
t.Run("when plugin does not exist", func(t *testing.T) {
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
|
||||
err := WriteToolVersionHelp(conf, "non-existent", "1.2.3", &stdout, &stderr)
|
||||
|
||||
assert.EqualError(t, err, "Plugin named non-existent not installed")
|
||||
assert.Empty(t, stdout.String())
|
||||
assert.Equal(t, stderr.String(), "No plugin named non-existent\n")
|
||||
})
|
||||
}
|
||||
|
||||
func generateConfig(t *testing.T) (config.Config, plugins.Plugin) {
|
||||
t.Helper()
|
||||
testDataDir := t.TempDir()
|
||||
conf, err := config.LoadConfig()
|
||||
assert.Nil(t, err)
|
||||
conf.DataDir = testDataDir
|
||||
|
||||
return conf, installPlugin(t, conf, "dummy_plugin", testPluginName)
|
||||
}
|
||||
|
||||
func installPlugin(t *testing.T, conf config.Config, fixture, pluginName string) plugins.Plugin {
|
||||
_, err := repotest.InstallPlugin(fixture, conf.DataDir, pluginName)
|
||||
assert.Nil(t, err)
|
||||
|
||||
return plugins.New(conf, pluginName)
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
package resolve
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@ -72,7 +73,7 @@ func findVersionsInDir(conf config.Config, plugin plugins.Plugin, directory stri
|
||||
|
||||
// findVersionsInEnv returns the version from the environment if present
|
||||
func findVersionsInEnv(pluginName string) ([]string, string, bool) {
|
||||
envVariableName := "ASDF_" + strings.ToUpper(pluginName) + "_VERSION"
|
||||
envVariableName := variableVersionName(pluginName)
|
||||
versionString := os.Getenv(envVariableName)
|
||||
if versionString == "" {
|
||||
return []string{}, envVariableName, false
|
||||
@ -118,3 +119,7 @@ func parseVersion(rawVersions string) []string {
|
||||
}
|
||||
return versions
|
||||
}
|
||||
|
||||
func variableVersionName(toolName string) string {
|
||||
return fmt.Sprintf("ASDF_%s_VERSION", strings.ToUpper(toolName))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package resolve
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@ -206,3 +207,29 @@ func TestFindVersionsInEnv(t *testing.T) {
|
||||
os.Unsetenv("ASDF_LUA_VERSION")
|
||||
})
|
||||
}
|
||||
|
||||
func TestVariableVersionName(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
input: "ruby",
|
||||
output: "ASDF_RUBY_VERSION",
|
||||
},
|
||||
{
|
||||
input: "lua",
|
||||
output: "ASDF_LUA_VERSION",
|
||||
},
|
||||
{
|
||||
input: "foo-bar",
|
||||
output: "ASDF_FOO-BAR_VERSION",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("input: %s, output: %s", tt.input, tt.output), func(t *testing.T) {
|
||||
assert.Equal(t, tt.output, variableVersionName(tt.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ func installPlugin(t *testing.T, conf config.Config, fixture, pluginName string)
|
||||
_, err := repotest.InstallPlugin(fixture, conf.DataDir, pluginName)
|
||||
assert.Nil(t, err)
|
||||
|
||||
return plugins.New(conf, testPluginName)
|
||||
return plugins.New(conf, pluginName)
|
||||
}
|
||||
|
||||
func installVersion(t *testing.T, conf config.Config, plugin plugins.Plugin, version string) {
|
||||
|
@ -23,9 +23,9 @@ func TestBatsTests(t *testing.T) {
|
||||
runBatsFile(t, dir, "current_command.bats")
|
||||
})
|
||||
|
||||
//t.Run("help_command", func(t *testing.T) {
|
||||
// runBatsFile(t, dir, "help_command.bats")
|
||||
//})
|
||||
t.Run("help_command", func(t *testing.T) {
|
||||
runBatsFile(t, dir, "help_command.bats")
|
||||
})
|
||||
|
||||
t.Run("info_command", func(t *testing.T) {
|
||||
runBatsFile(t, dir, "info_command.bats")
|
||||
|
@ -75,7 +75,7 @@ EOF
|
||||
[ "$status" -eq 0 ]
|
||||
[[ $output == 'version: v'* ]]
|
||||
[[ $output == *$'MANAGE PLUGINS\n'* ]]
|
||||
[[ $output == *$'MANAGE PACKAGES\n'* ]]
|
||||
[[ $output == *$'MANAGE TOOLS\n'* ]]
|
||||
[[ $output == *$'UTILS\n'* ]]
|
||||
[[ $output == *$'"Late but latest"\n-- Rajinikanth' ]]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user