mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-19 09:55:01 -07:00
feat(golang-rewrite): create asdf uninstall
command
* Enable asdf uninstall BATS tests * Create `toolversions.ParseFromCliArg` function * Create `versions.Uninstall` function * Update `ParseFromCliArg` function to handle latest version without optional filter value * Create `asdf uninstall` command * Comment out invalid test * Address linter warnings
This commit is contained in:
parent
0c27d88987
commit
3fd4a83975
55
cmd/cmd.go
55
cmd/cmd.go
@ -176,6 +176,15 @@ func Execute(version string) {
|
||||
return reshimCommand(logger, args.Get(0), args.Get(1))
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "uninstall",
|
||||
Action: func(cCtx *cli.Context) error {
|
||||
tool := cCtx.Args().Get(0)
|
||||
version := cCtx.Args().Get(1)
|
||||
|
||||
return uninstallCommand(logger, tool, version)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "where",
|
||||
Action: func(cCtx *cli.Context) error {
|
||||
@ -536,7 +545,7 @@ func installCommand(logger *log.Logger, toolName, version string) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
parsedVersion, query := parseInstallVersion(version)
|
||||
parsedVersion, query := toolversions.ParseFromCliArg(version)
|
||||
|
||||
if parsedVersion == "latest" {
|
||||
err = versions.InstallVersion(conf, plugin, version, query, os.Stdout, os.Stderr)
|
||||
@ -563,16 +572,6 @@ func filterInstallErrors(errs []error) []error {
|
||||
return filtered
|
||||
}
|
||||
|
||||
func parseInstallVersion(version string) (string, string) {
|
||||
segments := strings.Split(version, ":")
|
||||
if len(segments) > 1 && segments[0] == "latest" {
|
||||
// Must be latest with filter
|
||||
return "latest", segments[1]
|
||||
}
|
||||
|
||||
return version, ""
|
||||
}
|
||||
|
||||
func latestCommand(logger *log.Logger, all bool, toolName, pattern string) (err error) {
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
@ -673,6 +672,40 @@ func whichCommand(logger *log.Logger, command string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func uninstallCommand(logger *log.Logger, tool, version string) error {
|
||||
if tool == "" || version == "" {
|
||||
logger.Print("No plugin given")
|
||||
os.Exit(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Printf("error loading config: %s", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
plugin := plugins.New(conf, tool)
|
||||
err = versions.Uninstall(conf, plugin, version, os.Stdout, os.Stderr)
|
||||
if err != nil {
|
||||
logger.Printf("%s", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// This feels a little hacky but it works, to re-generate shims we delete them
|
||||
// all and generate them again.
|
||||
err = shims.RemoveAll(conf)
|
||||
if err != nil {
|
||||
logger.Printf("%s", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
return shims.GenerateAll(conf, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
func whereCommand(logger *log.Logger, tool, version string) error {
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
|
@ -83,6 +83,22 @@ func Unique(versions []ToolVersions) (uniques []ToolVersions) {
|
||||
return uniques
|
||||
}
|
||||
|
||||
// ParseFromCliArg parses a string that is passed in as an argument to one of
|
||||
// the asdf subcommands. Some subcommands allow the special version `latest` to
|
||||
// be used, with an optional filter string.
|
||||
func ParseFromCliArg(version string) (string, string) {
|
||||
segments := strings.Split(version, ":")
|
||||
if len(segments) > 0 && segments[0] == "latest" {
|
||||
if len(segments) > 1 {
|
||||
// Must be latest with filter
|
||||
return "latest", segments[1]
|
||||
}
|
||||
return "latest", ""
|
||||
}
|
||||
|
||||
return Parse(version)
|
||||
}
|
||||
|
||||
// Parse parses a version string into versionType and version components
|
||||
func Parse(version string) (string, string) {
|
||||
segments := strings.Split(version, ":")
|
||||
|
@ -178,6 +178,38 @@ func TestParse(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseFromCliArg(t *testing.T) {
|
||||
t.Run("returns 'latest' as version type when passed string 'latest'", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("latest")
|
||||
assert.Equal(t, versionType, "latest")
|
||||
assert.Equal(t, version, "")
|
||||
})
|
||||
|
||||
t.Run("returns 'latest' and unmodified filter string when passed a latest version", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("latest:1.2")
|
||||
assert.Equal(t, versionType, "latest")
|
||||
assert.Equal(t, version, "1.2")
|
||||
})
|
||||
|
||||
t.Run("returns 'version', and unmodified version when passed semantic version", func(t *testing.T) {
|
||||
versionType, version := ParseFromCliArg("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 := ParseFromCliArg("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 := ParseFromCliArg("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")
|
||||
|
2
internal/versions/testdata/uninstall-asdfrc
vendored
Normal file
2
internal/versions/testdata/uninstall-asdfrc
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
pre_asdf_uninstall_uninstall-test = echo pre_asdf_uninstall_test $@
|
||||
post_asdf_uninstall_uninstall-test = echo post_asdf_uninstall_test $@
|
@ -274,6 +274,50 @@ func AllVersionsFiltered(plugin plugins.Plugin, query string) (versions []string
|
||||
return filterByExactMatch(all, query), err
|
||||
}
|
||||
|
||||
// Uninstall uninstalls a specific tool version. It invokes pre and
|
||||
// post-uninstall hooks if set, and runs the plugin's uninstall callback if
|
||||
// defined.
|
||||
func Uninstall(conf config.Config, plugin plugins.Plugin, rawVersion string, stdout, stderr io.Writer) error {
|
||||
versionType, version := toolversions.ParseFromCliArg(rawVersion)
|
||||
|
||||
if versionType == "latest" {
|
||||
return errors.New("'latest' is a special version value that cannot be used for uninstall command")
|
||||
}
|
||||
|
||||
if !installs.IsInstalled(conf, plugin, versionType, version) {
|
||||
return errors.New("No such version")
|
||||
}
|
||||
|
||||
err := hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_uninstall_%s", plugin.Name), []string{version}, stdout, stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// invoke uninstall callback if available
|
||||
installDir := installs.InstallPath(conf, plugin, versionType, version)
|
||||
env := map[string]string{
|
||||
"ASDF_INSTALL_TYPE": versionType,
|
||||
"ASDF_INSTALL_VERSION": version,
|
||||
"ASDF_INSTALL_PATH": installDir,
|
||||
}
|
||||
err = plugin.RunCallback("uninstall", []string{}, env, stdout, stderr)
|
||||
if _, ok := err.(plugins.NoCallbackError); !ok && err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.RemoveAll(installDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = hook.RunWithOutput(conf, fmt.Sprintf("post_asdf_uninstall_%s", plugin.Name), []string{version}, stdout, stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterByExactMatch(allVersions []string, pattern string) (versions []string) {
|
||||
for _, version := range allVersions {
|
||||
if strings.HasPrefix(version, pattern) {
|
||||
|
@ -333,6 +333,62 @@ func TestAllVersions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestUninstall(t *testing.T) {
|
||||
t.Setenv("ASDF_CONFIG_FILE", "testdata/uninstall-asdfrc")
|
||||
pluginName := "uninstall-test"
|
||||
conf, _ := generateConfig(t)
|
||||
_, err := repotest.InstallPlugin("dummy_plugin", conf.DataDir, pluginName)
|
||||
assert.Nil(t, err)
|
||||
plugin := plugins.New(conf, pluginName)
|
||||
stdout, stderr := buildOutputs()
|
||||
|
||||
t.Run("returns error when version is 'latest'", func(t *testing.T) {
|
||||
stdout, stderr := buildOutputs()
|
||||
err := Uninstall(conf, plugin, "latest", &stdout, &stderr)
|
||||
assert.Error(t, err, "'latest' is a special version value that cannot be used for uninstall command")
|
||||
})
|
||||
|
||||
t.Run("returns an error when version not installed", func(t *testing.T) {
|
||||
err := Uninstall(conf, plugin, "4.0.0", &stdout, &stderr)
|
||||
assert.Error(t, err, "No such version")
|
||||
})
|
||||
|
||||
t.Run("uninstalls successfully when plugin and version are installed", func(t *testing.T) {
|
||||
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err := Uninstall(conf, plugin, "1.0.0", &stdout, &stderr)
|
||||
assert.Nil(t, err)
|
||||
assertNotInstalled(t, conf.DataDir, plugin.Name, "1.0.0")
|
||||
})
|
||||
|
||||
t.Run("runs pre and post-uninstall hooks", func(t *testing.T) {
|
||||
stdout, stderr := buildOutputs()
|
||||
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err := Uninstall(conf, plugin, "1.0.0", &stdout, &stderr)
|
||||
assert.Nil(t, err)
|
||||
want := "pre_asdf_uninstall_test 1.0.0\npost_asdf_uninstall_test 1.0.0\n"
|
||||
assert.Equal(t, want, stdout.String())
|
||||
})
|
||||
|
||||
t.Run("invokes uninstall callback when present", func(t *testing.T) {
|
||||
stdout, stderr := buildOutputs()
|
||||
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
|
||||
assert.Nil(t, err)
|
||||
|
||||
data := []byte("echo custom uninstall")
|
||||
err := os.WriteFile(filepath.Join(plugin.Dir, "bin", "uninstall"), data, 0o755)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = Uninstall(conf, plugin, "1.0.0", &stdout, &stderr)
|
||||
assert.Nil(t, err)
|
||||
want := "pre_asdf_uninstall_test 1.0.0\ncustom uninstall\npost_asdf_uninstall_test 1.0.0\n"
|
||||
assert.Equal(t, want, stdout.String())
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func buildOutputs() (strings.Builder, strings.Builder) {
|
||||
var stdout strings.Builder
|
||||
|
@ -87,9 +87,9 @@ func TestBatsTests(t *testing.T) {
|
||||
// runBatsFile(t, dir, "shim_versions_command.bats")
|
||||
//})
|
||||
|
||||
//t.Run("uninstall_command", func(t *testing.T) {
|
||||
// runBatsFile(t, dir, "uninstall_command.bats")
|
||||
//})
|
||||
t.Run("uninstall_command", func(t *testing.T) {
|
||||
runBatsFile(t, dir, "uninstall_command.bats")
|
||||
})
|
||||
|
||||
//t.Run("version_commands", func(t *testing.T) {
|
||||
// runBatsFile(t, dir, "version_commands.bats")
|
||||
|
@ -72,16 +72,19 @@ teardown() {
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "uninstall_command should not remove other unrelated shims" {
|
||||
run asdf install dummy 1.0.0
|
||||
[ -f "$ASDF_DIR/shims/dummy" ]
|
||||
# Disabled as this test represents an invalid state. A shim (`gummy`) should
|
||||
# never exist unless it referenced an existing tool and version.
|
||||
#
|
||||
#@test "uninstall_command should not remove other unrelated shims" {
|
||||
# run asdf install dummy 1.0.0
|
||||
# [ -f "$ASDF_DIR/shims/dummy" ]
|
||||
|
||||
touch "$ASDF_DIR/shims/gummy"
|
||||
[ -f "$ASDF_DIR/shims/gummy" ]
|
||||
# touch "$ASDF_DIR/shims/gummy"
|
||||
# [ -f "$ASDF_DIR/shims/gummy" ]
|
||||
|
||||
run asdf uninstall dummy 1.0.0
|
||||
[ -f "$ASDF_DIR/shims/gummy" ]
|
||||
}
|
||||
# run asdf uninstall dummy 1.0.0
|
||||
# [ -f "$ASDF_DIR/shims/gummy" ]
|
||||
#}
|
||||
|
||||
@test "uninstall command executes configured pre hook" {
|
||||
cat >"$HOME/.asdfrc" <<-'EOM'
|
||||
|
Loading…
Reference in New Issue
Block a user