diff --git a/cmd/main.go b/cmd/main.go index 934f750f..86ae6ebb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -71,10 +71,10 @@ func Execute() { }, }, &cli.Command{ - Name: "Lorem", + Name: "remove", Action: func(cCtx *cli.Context) error { - log.Print("Foobar") - return nil + args := cCtx.Args() + return pluginRemoveCommand(cCtx, logger, args.Get(0)) }, }, &cli.Command{ @@ -113,7 +113,7 @@ func pluginAddCommand(cCtx *cli.Context, conf config.Config, logger *log.Logger, // TODO: implement return cli.Exit("Not implemented yet", 1) } else { - err := plugins.PluginAdd(conf, pluginName, pluginRepo) + err := plugins.Add(conf, pluginName, pluginRepo) if err != nil { logger.Printf("error adding plugin: %s", err) } @@ -121,6 +121,21 @@ func pluginAddCommand(cCtx *cli.Context, conf config.Config, logger *log.Logger, return nil } +func pluginRemoveCommand(cCtx *cli.Context, logger *log.Logger, pluginName string) error { + conf, err := config.LoadConfig() + if err != nil { + logger.Printf("error loading config: %s", err) + return err + } + + err = plugins.Remove(conf, pluginName) + + if err != nil { + logger.Printf("error removing plugin: %s", err) + } + return err +} + func pluginListCommand(cCtx *cli.Context, logger *log.Logger) error { urls := cCtx.Bool("urls") refs := cCtx.Bool("refs") diff --git a/main_test.go b/main_test.go index 67f88eb7..4a8c1fe4 100644 --- a/main_test.go +++ b/main_test.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "os" "os/exec" "strings" "testing" @@ -12,12 +11,7 @@ import ( // new Golang implementation matches the existing Bash implementation. func TestBatsTests(t *testing.T) { - // Create temp directory - dir, err := os.MkdirTemp("", "golang-bats-tests") - if err != nil { - t.Fatal("Failed to create temp dir") - } - defer os.RemoveAll(dir) + dir := t.TempDir() // Build asdf and put in temp directory buildAsdf(t, dir) @@ -138,9 +132,9 @@ func runBatsFile(t *testing.T, dir, filename string) { cmd.Stderr = &stderr // Add dir to asdf test variables - asdfTestHome := fmt.Sprintf("HOME=%s/.asdf/", dir) + asdfTestHome := fmt.Sprintf("HOME=%s", dir) asdfBinPath := fmt.Sprintf("ASDF_BIN=%s", dir) - cmd.Env = append(cmd.Environ(), asdfBinPath, asdfTestHome) + cmd.Env = []string{asdfBinPath, asdfTestHome} err := cmd.Run() if err != nil { diff --git a/plugins/main.go b/plugins/main.go index 13d68de9..7292640b 100644 --- a/plugins/main.go +++ b/plugins/main.go @@ -78,7 +78,7 @@ func List(config config.Config, urls, refs bool) (plugins []Plugin, err error) { return plugins, nil } -func PluginAdd(config config.Config, pluginName, pluginUrl string) error { +func Add(config config.Config, pluginName, pluginUrl string) error { err := validatePluginName(pluginName) if err != nil { @@ -112,6 +112,28 @@ func PluginAdd(config config.Config, pluginName, pluginUrl string) error { return nil } +func Remove(config config.Config, pluginName string) error { + err := validatePluginName(pluginName) + + if err != nil { + return err + } + + exists, err := PluginExists(config.DataDir, pluginName) + + if err != nil { + return fmt.Errorf("unable to check if plugin exists: %w", err) + } + + if !exists { + return fmt.Errorf("no such plugin: %s", pluginName) + } + + pluginDir := PluginDirectory(config.DataDir, pluginName) + + return os.RemoveAll(pluginDir) +} + func PluginExists(dataDir, pluginName string) (bool, error) { pluginDir := PluginDirectory(dataDir, pluginName) fileInfo, err := os.Stat(pluginDir) diff --git a/plugins/main_test.go b/plugins/main_test.go index 5f5ce877..fa0d3589 100644 --- a/plugins/main_test.go +++ b/plugins/main_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { testRepo, err := installMockPluginRepo(testDataDir, testPluginName) assert.Nil(t, err) - err = PluginAdd(conf, testPluginName, testRepo) + err = Add(conf, testPluginName, testRepo) assert.Nil(t, err) t.Run("when urls and refs are set to false returns plugin names", func(t *testing.T) { @@ -72,7 +72,7 @@ func TestList(t *testing.T) { }) } -func TestPluginAdd(t *testing.T) { +func TestAdd(t *testing.T) { testDataDir := t.TempDir() t.Run("when given an invalid plugin name prints an error", func(t *testing.T) { @@ -80,7 +80,7 @@ func TestPluginAdd(t *testing.T) { for _, invalid := range invalids { t.Run(invalid, func(t *testing.T) { - err := PluginAdd(config.Config{}, invalid, testRepo) + err := Add(config.Config{}, invalid, testRepo) expectedErrMsg := "is invalid. Name may only contain lowercase letters, numbers, '_', and '-'" if !strings.Contains(err.Error(), expectedErrMsg) { @@ -94,14 +94,14 @@ func TestPluginAdd(t *testing.T) { conf := config.Config{DataDir: testDataDir} // Add plugin - err := PluginAdd(conf, testPluginName, testRepo) + err := Add(conf, testPluginName, testRepo) if err != nil { t.Fatal("Expected to be able to add plugin") } // Add it again to trigger error - err = PluginAdd(conf, testPluginName, testRepo) + err = Add(conf, testPluginName, testRepo) if err == nil { t.Fatal("expected error got nil") @@ -116,7 +116,7 @@ func TestPluginAdd(t *testing.T) { t.Run("when plugin name is valid but URL is invalid prints an error", func(t *testing.T) { conf := config.Config{DataDir: testDataDir} - err := PluginAdd(conf, "foo", "foobar") + err := Add(conf, "foo", "foobar") assert.ErrorContains(t, err, "unable to clone plugin: repository not found") }) @@ -125,7 +125,7 @@ func TestPluginAdd(t *testing.T) { testDataDir := t.TempDir() conf := config.Config{DataDir: testDataDir} - err := PluginAdd(conf, testPluginName, testRepo) + err := Add(conf, testPluginName, testRepo) assert.Nil(t, err, "Expected to be able to add plugin") @@ -141,6 +141,38 @@ func TestPluginAdd(t *testing.T) { }) } +func TestRemove(t *testing.T) { + testDataDir := t.TempDir() + conf := config.Config{DataDir: testDataDir} + + err := Add(conf, testPluginName, testRepo) + assert.Nil(t, err) + + t.Run("returns error when plugin with name does not exist", func(t *testing.T) { + err := Remove(conf, "nonexistant") + assert.NotNil(t, err) + assert.ErrorContains(t, err, "no such plugin") + }) + + t.Run("returns error when invalid plugin name is given", func(t *testing.T) { + err := Remove(conf, "foo/bar/baz") + assert.NotNil(t, err) + + expectedErrMsg := "is invalid. Name may only contain lowercase letters, numbers, '_', and '-'" + assert.ErrorContains(t, err, expectedErrMsg) + }) + + t.Run("removes plugin when passed name of installed plugin", func(t *testing.T) { + err := Remove(conf, testPluginName) + assert.Nil(t, err) + + pluginDir := PluginDirectory(testDataDir, testPluginName) + _, err = os.Stat(pluginDir) + assert.NotNil(t, err) + assert.True(t, os.IsNotExist(err)) + }) +} + func TestPluginExists(t *testing.T) { testDataDir := t.TempDir() pluginDir := PluginDirectory(testDataDir, testPluginName) diff --git a/test/plugin_add_command.bats b/test/plugin_add_command.bats index fa034813..6b3517de 100644 --- a/test/plugin_add_command.bats +++ b/test/plugin_add_command.bats @@ -32,7 +32,7 @@ teardown() { run asdf plugin add "plugin-with-w" "${BASE_DIR}/repo-plugin-with-w" [ "$status" -eq 0 ] - run asdf plugin-list + run asdf plugin list [ "$output" = "plugin-with-w" ] LANG="$ORIGINAL_LANG" diff --git a/test/test_helpers.bash b/test/test_helpers.bash index e693e534..40dc6c33 100644 --- a/test/test_helpers.bash +++ b/test/test_helpers.bash @@ -7,14 +7,19 @@ bats_require_minimum_version 1.7.0 setup_asdf_dir() { if [ "$BATS_TEST_NAME" = 'test_shim_exec_should_use_path_executable_when_specified_version_path-3a-3cpath-3e' ]; then - BASE_DIR="$(mktemp -dt "asdf_with_no_spaces.XXXX")" + BASE_DIR="$HOME/asdf_with_no_spaces/" else - BASE_DIR="$(mktemp -dt "asdf with spaces.XXXX")" + BASE_DIR="$HOME/asdf with spaces/" fi + # We don't call mktemp anymore so we need to create this sub directory manually + mkdir "$BASE_DIR" + # HOME is now defined by the Golang test code in main_test.go #HOME="$BASE_DIR/home" - ASDF_DIR="$HOME/.asdf" + ASDF_DIR="$BASE_DIR/.asdf" + ASDF_DATA_DIR="$BASE_DIR/.asdf" + export ASDF_DATA_DIR mkdir -p "$ASDF_DIR/plugins" mkdir -p "$ASDF_DIR/installs" mkdir -p "$ASDF_DIR/shims"