diff --git a/cmd/cmd.go b/cmd/cmd.go index 1eb201ed..f6ac885f 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -7,6 +7,7 @@ import ( "os" "asdf/config" + "asdf/internal/info" "asdf/plugins" "github.com/urfave/cli/v2" @@ -19,7 +20,7 @@ Manage all your runtime versions with one tool! Complete documentation is available at https://asdf-vm.com/` // Execute defines the full CLI API and then runs it -func Execute() { +func Execute(version string) { logger := log.New(os.Stderr, "", 0) log.SetFlags(0) @@ -37,11 +38,23 @@ func Execute() { Usage: "The multiple runtime version manager", UsageText: usageText, Commands: []*cli.Command{ - // TODO: Flesh out all these commands + { + Name: "info", + Action: func(_ *cli.Context) error { + conf, err := config.LoadConfig() + if err != nil { + logger.Printf("error loading config: %s", err) + return err + } + + return infoCommand(conf, version) + }, + }, { Name: "plugin", Action: func(_ *cli.Context) error { - log.Print("Foobar") + logger.Println("Unknown command: `asdf plugin`") + os.Exit(1) return nil }, Subcommands: []*cli.Command{ @@ -184,6 +197,10 @@ func pluginListCommand(cCtx *cli.Context, logger *log.Logger) error { return nil } +func infoCommand(conf config.Config, version string) error { + return info.Print(conf, version) +} + func pluginUpdateCommand(cCtx *cli.Context, logger *log.Logger, pluginName, ref string) error { updateAll := cCtx.Bool("all") if !updateAll && pluginName == "" { diff --git a/internal/info/info.go b/internal/info/info.go new file mode 100644 index 00000000..7ee1c2ac --- /dev/null +++ b/internal/info/info.go @@ -0,0 +1,74 @@ +// Package info exists to print important info about this asdf installation to STDOUT for use in debugging and bug reports. +package info + +import ( + "fmt" + "io" + "os" + "text/tabwriter" + + "asdf/config" + "asdf/execute" + "asdf/plugins" +) + +// Print info output to STDOUT +func Print(conf config.Config, version string) error { + return Write(conf, version, os.Stdout) +} + +// Write info output to an io.Writer +func Write(conf config.Config, version string, writer io.Writer) error { + fmt.Fprintln(writer, "OS:") + uname := execute.NewExpression("uname -a", []string{}) + uname.Stdout = writer + err := uname.Run() + if err != nil { + return err + } + + fmt.Fprintln(writer, "\nSHELL:") + shellVersion := execute.NewExpression("$SHELL --version", []string{}) + shellVersion.Stdout = writer + err = shellVersion.Run() + if err != nil { + return err + } + + fmt.Fprintln(writer, "\nBASH VERSION:") + bashVersion := execute.NewExpression("echo $BASH_VERSION", []string{}) + bashVersion.Stdout = writer + err = bashVersion.Run() + if err != nil { + return err + } + + fmt.Fprintln(writer, "\nASDF VERSION:") + fmt.Fprintf(writer, "%s\n", version) + + fmt.Fprintln(writer, "\nASDF INTERNAL VARIABLES:") + fmt.Fprintf(writer, "ASDF_DEFAULT_TOOL_VERSIONS_FILENAME=%s\n", conf.DefaultToolVersionsFilename) + fmt.Fprintf(writer, "ASDF_DATA_DIR=%s\n", conf.DataDir) + fmt.Fprintf(writer, "ASDF_CONFIG_FILE=%s\n", conf.ConfigFile) + + fmt.Fprintln(writer, "\nASDF INSTALLED PLUGINS:") + plugins, err := plugins.List(conf, true, true) + if err != nil { + fmt.Fprintf(writer, "error loading plugin list: %s", err) + return err + } + + pluginsTable(plugins, writer) + + return nil +} + +func pluginsTable(plugins []plugins.Plugin, output io.Writer) error { + writer := tabwriter.NewWriter(output, 10, 4, 1, ' ', 0) + + for _, plugin := range plugins { + fmt.Fprintf(writer, "%s\t%s\t%s\n", plugin.Name, plugin.URL, plugin.Ref) + } + + return writer.Flush() +} diff --git a/internal/info/info_test.go b/internal/info/info_test.go new file mode 100644 index 00000000..57ba8403 --- /dev/null +++ b/internal/info/info_test.go @@ -0,0 +1,33 @@ +package info + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "asdf/config" + + "github.com/stretchr/testify/assert" +) + +func TestWrite(t *testing.T) { + testDataDir := t.TempDir() + err := os.MkdirAll(filepath.Join(testDataDir, "plugins"), 0o777) + assert.Nil(t, err) + + conf := config.Config{DataDir: testDataDir} + var stdout strings.Builder + + err = Write(conf, "0.15.0", &stdout) + assert.Nil(t, err) + output := stdout.String() + + // Simple format assertions + assert.True(t, strings.Contains(output, "OS:\n")) + assert.True(t, strings.Contains(output, "BASH VERSION:\n")) + assert.True(t, strings.Contains(output, "SHELL:\n")) + assert.True(t, strings.Contains(output, "ASDF VERSION:\n")) + assert.True(t, strings.Contains(output, "INTERNAL VARIABLES:\n")) + assert.True(t, strings.Contains(output, "ASDF INSTALLED PLUGINS:\n")) +} diff --git a/main.go b/main.go index 5348942f..aeed03d2 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,9 @@ import ( "asdf/cmd" ) +var version = "v0.15.0" + // Placeholder for the real code func main() { - cmd.Execute() + cmd.Execute(version) } diff --git a/main_test.go b/main_test.go index dd2726c9..4ba3974b 100644 --- a/main_test.go +++ b/main_test.go @@ -23,10 +23,6 @@ func TestBatsTests(t *testing.T) { // runBatsFile(t, dir, "current_command.bats") //}) - //t.Run("get_asdf_config_value", func(t *testing.T) { - // runBatsFile(t, dir, "get_asdf_config_value.bats") - //}) - //t.Run("help_command", func(t *testing.T) { // runBatsFile(t, dir, "help_command.bats") //}) @@ -79,10 +75,6 @@ func TestBatsTests(t *testing.T) { // runBatsFile(t, dir, "reshim_command.bats") //}) - //t.Run("reshim_command", func(t *testing.T) { - // runBatsFile(t, dir, "reshim_command.bats") - //}) - //t.Run("shim_env_command", func(t *testing.T) { // runBatsFile(t, dir, "shim_env_command.bats") //}) @@ -95,10 +87,6 @@ func TestBatsTests(t *testing.T) { // runBatsFile(t, dir, "shim_versions_command.bats") //}) - //t.Run("shim_versions_command", func(t *testing.T) { - // runBatsFile(t, dir, "shim_versions_command.bats") - //}) - //t.Run("uninstall_command", func(t *testing.T) { // runBatsFile(t, dir, "uninstall_command.bats") //}) diff --git a/plugins/plugins.go b/plugins/plugins.go index 08d2ddfd..ed90dfb2 100644 --- a/plugins/plugins.go +++ b/plugins/plugins.go @@ -194,6 +194,11 @@ func Add(config config.Config, pluginName, pluginURL string) error { return err } + err = os.MkdirAll(PluginDownloadDirectory(config.DataDir, plugin.Name), 0o777) + if err != nil { + return err + } + env := map[string]string{"ASDF_PLUGIN_SOURCE_URL": plugin.URL, "ASDF_PLUGIN_PATH": plugin.Dir} plugin.RunCallback("post-plugin-add", []string{}, env, os.Stdout, os.Stderr) @@ -221,8 +226,16 @@ func Remove(config config.Config, pluginName string) error { } pluginDir := PluginDirectory(config.DataDir, pluginName) + downloadDir := PluginDownloadDirectory(config.DataDir, pluginName) - return os.RemoveAll(pluginDir) + err = os.RemoveAll(downloadDir) + err2 := os.RemoveAll(pluginDir) + + if err != nil { + return err + } + + return err2 } // Update a plugin to a specific ref, or if no ref provided update to latest @@ -269,6 +282,12 @@ func PluginDirectory(dataDir, pluginName string) string { return filepath.Join(DataDirectory(dataDir), pluginName) } +// PluginDownloadDirectory returns the directory a plugin will be placing +// downloads of version source code +func PluginDownloadDirectory(dataDir, pluginName string) string { + return filepath.Join(dataDir, "downloads", pluginName) +} + // DataDirectory returns the path to the plugin directory inside the data // directory func DataDirectory(dataDir string) string { diff --git a/plugins/plugins_test.go b/plugins/plugins_test.go index 573c37fb..e3d26afc 100644 --- a/plugins/plugins_test.go +++ b/plugins/plugins_test.go @@ -151,6 +151,19 @@ func TestAdd(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 5, len(entries)) }) + + t.Run("when parameters are valid creates plugin download dir", func(t *testing.T) { + testDataDir := t.TempDir() + conf := config.Config{DataDir: testDataDir} + + err := Add(conf, testPluginName, testRepo) + assert.Nil(t, err) + + // Assert download dir exists + downloadDir := PluginDownloadDirectory(testDataDir, testPluginName) + _, err = os.Stat(downloadDir) + assert.Nil(t, err) + }) } func TestRemove(t *testing.T) { @@ -182,6 +195,19 @@ func TestRemove(t *testing.T) { assert.NotNil(t, err) assert.True(t, os.IsNotExist(err)) }) + + t.Run("removes plugin download dir when passed name of installed plugin", func(t *testing.T) { + err := Add(conf, testPluginName, testRepo) + assert.Nil(t, err) + + err = Remove(conf, testPluginName) + assert.Nil(t, err) + + downloadDir := PluginDownloadDirectory(testDataDir, testPluginName) + _, err = os.Stat(downloadDir) + assert.NotNil(t, err) + assert.True(t, os.IsNotExist(err)) + }) } func TestUpdate(t *testing.T) { diff --git a/test/fixtures/dummy_plugin/bin/debug b/test/fixtures/dummy_plugin/bin/debug index 7e89bf37..8629c24f 100755 --- a/test/fixtures/dummy_plugin/bin/debug +++ b/test/fixtures/dummy_plugin/bin/debug @@ -1,3 +1,3 @@ #!/usr/bin/env bash -echo $@ +echo "$@"