mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-24 12:25:28 -07:00
Merge pull request #29 from asdf-vm/tb/plugin-list-command
feat(golang-rewrite): create plugin list command
This commit is contained in:
commit
974b11b0fe
48
cmd/main.go
48
cmd/main.go
@ -48,6 +48,7 @@ func Execute() {
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Printf("error loading config: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return pluginAddCommand(cCtx, conf, logger, args.Get(0), args.Get(1))
|
||||
@ -55,8 +56,18 @@ func Execute() {
|
||||
},
|
||||
&cli.Command{
|
||||
Name: "list",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "urls",
|
||||
Usage: "Show URLs",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "refs",
|
||||
Usage: "Show Refs",
|
||||
},
|
||||
},
|
||||
Action: func(cCtx *cli.Context) error {
|
||||
return nil
|
||||
return pluginListCommand(cCtx, logger)
|
||||
},
|
||||
},
|
||||
&cli.Command{
|
||||
@ -109,3 +120,38 @@ func pluginAddCommand(cCtx *cli.Context, conf config.Config, logger *log.Logger,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluginListCommand(cCtx *cli.Context, logger *log.Logger) error {
|
||||
urls := cCtx.Bool("urls")
|
||||
refs := cCtx.Bool("refs")
|
||||
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Printf("error loading config: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
plugins, err := plugins.List(conf, urls, refs)
|
||||
|
||||
if err != nil {
|
||||
logger.Printf("error loading plugin list: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Add some sort of presenter logic in another file so we
|
||||
// don't clutter up this cmd code with conditional presentation
|
||||
// logic
|
||||
for _, plugin := range plugins {
|
||||
if urls && refs {
|
||||
logger.Printf("%s\t\t%s\t%s\n", plugin.Name, plugin.Url, plugin.Ref)
|
||||
} else if refs {
|
||||
logger.Printf("%s\t\t%s\n", plugin.Name, plugin.Ref)
|
||||
} else if urls {
|
||||
logger.Printf("%s\t\t%s\n", plugin.Name, plugin.Url)
|
||||
} else {
|
||||
logger.Printf("%s\n", plugin.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
62
main_test.go
62
main_test.go
@ -20,113 +20,113 @@ func TestBatsTests(t *testing.T) {
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Build asdf and put in temp directory
|
||||
build_asdf(t, dir)
|
||||
buildAsdf(t, dir)
|
||||
|
||||
// Run tests with the asdf binary in the temp directory
|
||||
|
||||
// Uncomment these as they are implemented
|
||||
//t.Run("current_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "current_command.bats")
|
||||
// runBatsFile(t, dir, "current_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("get_asdf_config_value", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "get_asdf_config_value.bats")
|
||||
// runBatsFile(t, dir, "get_asdf_config_value.bats")
|
||||
//})
|
||||
|
||||
//t.Run("help_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "help_command.bats")
|
||||
// runBatsFile(t, dir, "help_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("info_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "info_command.bats")
|
||||
// runBatsFile(t, dir, "info_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("install_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "install_command.bats")
|
||||
// runBatsFile(t, dir, "install_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("latest_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "latest_command.bats")
|
||||
// runBatsFile(t, dir, "latest_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("list_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "list_command.bats")
|
||||
// runBatsFile(t, dir, "list_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("plugin_add_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "plugin_add_command.bats")
|
||||
// runBatsFile(t, dir, "plugin_add_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("plugin_extension_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "plugin_extension_command.bats")
|
||||
// runBatsFile(t, dir, "plugin_extension_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("plugin_list_all_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "plugin_list_all_command.bats")
|
||||
// runBatsFile(t, dir, "plugin_list_all_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("plugin_remove_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "plugin_remove_command.bats")
|
||||
// runBatsFile(t, dir, "plugin_remove_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("plugin_test_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "plugin_test_command.bats")
|
||||
// runBatsFile(t, dir, "plugin_test_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("plugin_update_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "plugin_update_command.bats")
|
||||
// runBatsFile(t, dir, "plugin_update_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("remove_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "remove_command.bats")
|
||||
// runBatsFile(t, dir, "remove_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("reshim_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "reshim_command.bats")
|
||||
// runBatsFile(t, dir, "reshim_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("reshim_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "reshim_command.bats")
|
||||
// runBatsFile(t, dir, "reshim_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("shim_env_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "shim_env_command.bats")
|
||||
// runBatsFile(t, dir, "shim_env_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("shim_exec", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "shim_exec.bats")
|
||||
// runBatsFile(t, dir, "shim_exec.bats")
|
||||
//})
|
||||
|
||||
//t.Run("shim_versions_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "shim_versions_command.bats")
|
||||
// runBatsFile(t, dir, "shim_versions_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("shim_versions_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "shim_versions_command.bats")
|
||||
// runBatsFile(t, dir, "shim_versions_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("uninstall_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "uninstall_command.bats")
|
||||
// runBatsFile(t, dir, "uninstall_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("update_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "update_command.bats")
|
||||
// runBatsFile(t, dir, "update_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("version_commands", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "version_commands.bats")
|
||||
// runBatsFile(t, dir, "version_commands.bats")
|
||||
//})
|
||||
|
||||
//t.Run("where_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "where_command.bats")
|
||||
// runBatsFile(t, dir, "where_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("which_command", func(t *testing.T) {
|
||||
// run_bats_file(t, dir, "which_command.bats")
|
||||
// runBatsFile(t, dir, "which_command.bats")
|
||||
//})
|
||||
}
|
||||
|
||||
func run_bats_file(t *testing.T, dir, filename string) {
|
||||
func runBatsFile(t *testing.T, dir, filename string) {
|
||||
t.Helper()
|
||||
|
||||
cmd := exec.Command("bats", "--verbose-run", fmt.Sprintf("test/%s", filename))
|
||||
@ -138,9 +138,9 @@ func run_bats_file(t *testing.T, dir, filename string) {
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
// Add dir to asdf test variables
|
||||
asdf_test_home := fmt.Sprintf("HOME=%s/.asdf/", dir)
|
||||
asdf_bin_path := fmt.Sprintf("ASDF_BIN=%s", dir)
|
||||
cmd.Env = append(cmd.Environ(), asdf_bin_path, asdf_test_home)
|
||||
asdfTestHome := fmt.Sprintf("HOME=%s/.asdf/", dir)
|
||||
asdfBinPath := fmt.Sprintf("ASDF_BIN=%s", dir)
|
||||
cmd.Env = append(cmd.Environ(), asdfBinPath, asdfTestHome)
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
@ -153,7 +153,7 @@ func run_bats_file(t *testing.T, dir, filename string) {
|
||||
}
|
||||
}
|
||||
|
||||
func build_asdf(t *testing.T, dir string) {
|
||||
func buildAsdf(t *testing.T, dir string) {
|
||||
cmd := exec.Command("go", "build", "-o", dir)
|
||||
|
||||
err := cmd.Run()
|
||||
|
@ -15,6 +15,69 @@ const dataDirPlugins = "plugins"
|
||||
const invalidPluginNameMsg = "'%q' is invalid. Name may only contain lowercase letters, numbers, '_', and '-'"
|
||||
const pluginAlreadyExists = "plugin named %q already added"
|
||||
|
||||
type Plugin struct {
|
||||
Name string
|
||||
Dir string
|
||||
Ref string
|
||||
Url string
|
||||
}
|
||||
|
||||
func List(config config.Config, urls, refs bool) (plugins []Plugin, err error) {
|
||||
pluginsDir := PluginsDataDirectory(config.DataDir)
|
||||
files, err := os.ReadDir(pluginsDir)
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
if refs || urls {
|
||||
var url string
|
||||
var refString string
|
||||
location := filepath.Join(pluginsDir, file.Name())
|
||||
repo, err := git.PlainOpen(location)
|
||||
|
||||
// TODO: Improve these error messages
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
|
||||
if refs {
|
||||
ref, err := repo.Head()
|
||||
refString = ref.Hash().String()
|
||||
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
}
|
||||
|
||||
if urls {
|
||||
remotes, err := repo.Remotes()
|
||||
url = remotes[0].Config().URLs[0]
|
||||
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
}
|
||||
|
||||
plugins = append(plugins, Plugin{
|
||||
Name: file.Name(),
|
||||
Dir: location,
|
||||
Url: url,
|
||||
Ref: refString,
|
||||
})
|
||||
} else {
|
||||
plugins = append(plugins, Plugin{
|
||||
Name: file.Name(),
|
||||
Dir: filepath.Join(pluginsDir, file.Name()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
func PluginAdd(config config.Config, pluginName, pluginUrl string) error {
|
||||
err := validatePluginName(pluginName)
|
||||
|
||||
@ -64,7 +127,11 @@ func PluginExists(dataDir, pluginName string) (bool, error) {
|
||||
}
|
||||
|
||||
func PluginDirectory(dataDir, pluginName string) string {
|
||||
return filepath.Join(dataDir, dataDirPlugins, pluginName)
|
||||
return filepath.Join(PluginsDataDirectory(dataDir), pluginName)
|
||||
}
|
||||
|
||||
func PluginsDataDirectory(dataDir string) string {
|
||||
return filepath.Join(dataDir, dataDirPlugins)
|
||||
}
|
||||
|
||||
func validatePluginName(name string) error {
|
||||
|
@ -2,7 +2,10 @@ package plugins
|
||||
|
||||
import (
|
||||
"asdf/config"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -15,6 +18,60 @@ const (
|
||||
testPluginName = "lua"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
testDataDir := t.TempDir()
|
||||
conf := config.Config{DataDir: testDataDir}
|
||||
testRepo, err := installMockPluginRepo(testDataDir, testPluginName)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = PluginAdd(conf, testPluginName, testRepo)
|
||||
assert.Nil(t, err)
|
||||
|
||||
t.Run("when urls and refs are set to false returns plugin names", func(t *testing.T) {
|
||||
plugins, err := List(conf, false, false)
|
||||
assert.Nil(t, err)
|
||||
|
||||
plugin := plugins[0]
|
||||
assert.Equal(t, "lua", plugin.Name)
|
||||
assert.NotZero(t, plugin.Dir)
|
||||
assert.Zero(t, plugin.Url)
|
||||
assert.Zero(t, plugin.Ref)
|
||||
})
|
||||
|
||||
t.Run("when urls is set to true returns plugins with repo urls set", func(t *testing.T) {
|
||||
plugins, err := List(conf, true, false)
|
||||
assert.Nil(t, err)
|
||||
|
||||
plugin := plugins[0]
|
||||
assert.Equal(t, "lua", plugin.Name)
|
||||
assert.NotZero(t, plugin.Dir)
|
||||
assert.Zero(t, plugin.Ref)
|
||||
assert.NotZero(t, plugin.Url)
|
||||
})
|
||||
|
||||
t.Run("when refs is set to true returns plugins with current repo refs set", func(t *testing.T) {
|
||||
plugins, err := List(conf, false, true)
|
||||
assert.Nil(t, err)
|
||||
|
||||
plugin := plugins[0]
|
||||
assert.Equal(t, "lua", plugin.Name)
|
||||
assert.NotZero(t, plugin.Dir)
|
||||
assert.NotZero(t, plugin.Ref)
|
||||
assert.Zero(t, plugin.Url)
|
||||
})
|
||||
|
||||
t.Run("when refs and urls are both set to true returns plugins with both set", func(t *testing.T) {
|
||||
plugins, err := List(conf, true, true)
|
||||
assert.Nil(t, err)
|
||||
|
||||
plugin := plugins[0]
|
||||
assert.Equal(t, "lua", plugin.Name)
|
||||
assert.NotZero(t, plugin.Dir)
|
||||
assert.NotZero(t, plugin.Ref)
|
||||
assert.NotZero(t, plugin.Url)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginAdd(t *testing.T) {
|
||||
testDataDir := t.TempDir()
|
||||
|
||||
@ -178,3 +235,101 @@ func touchFile(name string) error {
|
||||
}
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
func installMockPluginRepo(dataDir, name string) (string, error) {
|
||||
// Because the legacy dummy plugin directory is relative to the root of this
|
||||
// project I cannot use the usual testing functions to locate it. To
|
||||
// determine the location of it we compute the module root, which also
|
||||
// happens to be the root of the repo.
|
||||
modRootDir, err := moduleRoot()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
location := dataDir + "/repo-" + name
|
||||
|
||||
// Then we specify the path to the dummy plugin relative to the module root
|
||||
err = runCmd("cp", "-r", filepath.Join(modRootDir, "test/fixtures/dummy_plugin"), location)
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
// Definitely some opportunities to refactor here. This code might be
|
||||
// simplified by switching to the Go git library
|
||||
err = runCmd("git", "-C", location, "init", "-q")
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
err = runCmd("git", "-C", location, "config", "user.name", "\"Test\"")
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
err = runCmd("git", "-C", location, "config", "user.email", "\"test@example.com\"")
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
err = runCmd("git", "-C", location, "add", "-A")
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
err = runCmd("git", "-C", location, "commit", "-q", "-m", fmt.Sprintf("\"asdf %s plugin\"", name))
|
||||
return location, err
|
||||
}
|
||||
|
||||
// helper function to make running commands easier
|
||||
func runCmd(cmdName string, args ...string) error {
|
||||
cmd := exec.Command(cmdName, args...)
|
||||
|
||||
// Capture stdout and stderr
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
err := cmd.Run()
|
||||
|
||||
if err != nil {
|
||||
// If command fails print both stderr and stdout
|
||||
fmt.Println("stdout:", stdout.String())
|
||||
fmt.Println("stderr:", stderr.String())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func moduleRoot() (string, error) {
|
||||
currentDir, err := os.Getwd()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return findModuleRoot(currentDir), nil
|
||||
}
|
||||
|
||||
// Taken from https://github.com/golang/go/blob/9e3b1d53a012e98cfd02de2de8b1bd53522464d4/src/cmd/go/internal/modload/init.go#L1504C1-L1522C2 because that function is in an internal module
|
||||
// and I can't rely on it.
|
||||
func findModuleRoot(dir string) (roots string) {
|
||||
if dir == "" {
|
||||
panic("dir not set")
|
||||
}
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
// Look for enclosing go.mod.
|
||||
for {
|
||||
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
|
||||
return dir
|
||||
}
|
||||
d := filepath.Dir(dir)
|
||||
if d == dir {
|
||||
break
|
||||
}
|
||||
dir = d
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user