Merge pull request #88 from asdf-vm/tb/install-command-tests

feat(golang-rewrite): get all `install_command.bats` tests passing
This commit is contained in:
Trevor Brown 2024-12-05 09:21:20 -05:00 committed by Trevor Brown
commit 5e542da7b5
7 changed files with 118 additions and 75 deletions

View File

@ -102,9 +102,16 @@ func Execute(version string) {
},
{
Name: "install",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "keep-download",
Usage: "Whether or not to keep download directory after successful install",
},
},
Action: func(cCtx *cli.Context) error {
args := cCtx.Args()
return installCommand(logger, args.Get(0), args.Get(1))
keepDownload := cCtx.Bool("keep-download")
return installCommand(logger, args.Get(0), args.Get(1), keepDownload)
},
},
{
@ -697,7 +704,7 @@ func formatUpdateResult(logger *log.Logger, pluginName, updatedToRef string, err
logger.Printf("updated %s to ref %s\n", pluginName, updatedToRef)
}
func installCommand(logger *log.Logger, toolName, version string) error {
func installCommand(logger *log.Logger, toolName, version string, keepDownload bool) error {
conf, err := config.LoadConfig()
if err != nil {
logger.Printf("error loading config: %s", err)
@ -714,8 +721,12 @@ func installCommand(logger *log.Logger, toolName, version string) error {
errs := versions.InstallAll(conf, dir, os.Stdout, os.Stderr)
if len(errs) > 0 {
for _, err := range errs {
os.Stderr.Write([]byte(err.Error()))
os.Stderr.Write([]byte("\n"))
// Don't print error if no version set, this just means the current
// dir doesn't use a particular plugin that is installed.
if _, ok := err.(versions.NoVersionSetError); !ok {
os.Stderr.Write([]byte(err.Error()))
os.Stderr.Write([]byte("\n"))
}
}
filtered := filterInstallErrors(errs)
@ -731,15 +742,23 @@ func installCommand(logger *log.Logger, toolName, version string) error {
if version == "" {
err = versions.Install(conf, plugin, dir, os.Stdout, os.Stderr)
if err != nil {
if _, ok := err.(versions.NoVersionSetError); ok {
logger.Printf("No versions specified for %s in config files or environment", toolName)
os.Exit(1)
}
return err
}
} else {
parsedVersion := toolversions.ParseFromCliArg(version)
if parsedVersion.Type == "latest" {
err = versions.InstallVersion(conf, plugin, version, parsedVersion.Value, os.Stdout, os.Stderr)
err = versions.InstallVersion(conf, plugin, parsedVersion, os.Stdout, os.Stderr)
} else {
err = versions.InstallOneVersion(conf, plugin, version, os.Stdout, os.Stderr)
// Adding this here to get tests passing. The other versions.Install*
// calls here could have a keepDownload argument added as well. PR
// welcome!
err = versions.InstallOneVersion(conf, plugin, version, keepDownload, os.Stdout, os.Stderr)
}
if err != nil {

View File

@ -8,6 +8,11 @@ rather than a set of scripts.
## Breaking Changes
### `download` is now a required callback for plugins
Previously `download` was optional, now it is required. If a plugin lacks this
callback any installs of any version of that plugin will fail.
### Hyphenated commands have been removed
asdf version 0.14.1 and earlier supported by hyphenated and non-hyphenated

View File

@ -1,3 +1,4 @@
pre_asdf_download_lua = echo pre_asdf_download_lua $@
pre_asdf_install_lua = echo pre_asdf_install_lua $@
post_asdf_install_lua = echo post_asdf_install_lua $@
always_keep_download = yes

View File

@ -93,7 +93,7 @@ func Install(conf config.Config, plugin plugins.Plugin, dir string, stdOut io.Wr
}
for _, version := range versions.Versions {
err := InstallOneVersion(conf, plugin, version, stdOut, stdErr)
err := InstallOneVersion(conf, plugin, version, false, stdOut, stdErr)
if err != nil {
return err
}
@ -105,24 +105,25 @@ func Install(conf config.Config, plugin plugins.Plugin, dir string, stdOut io.Wr
// InstallVersion installs a version of a specific tool, the version may be an
// exact version, or it may be `latest` or `latest` a regex query in order to
// select the latest version matching the provided pattern.
func InstallVersion(conf config.Config, plugin plugins.Plugin, version string, pattern string, stdOut io.Writer, stdErr io.Writer) error {
func InstallVersion(conf config.Config, plugin plugins.Plugin, version toolversions.Version, stdOut io.Writer, stdErr io.Writer) error {
err := plugin.Exists()
if err != nil {
return err
}
if version == latestVersion {
version, err = Latest(plugin, pattern)
resolvedVersion := ""
if version.Type == latestVersion {
resolvedVersion, err = Latest(plugin, version.Value)
if err != nil {
return err
}
}
return InstallOneVersion(conf, plugin, version, stdOut, stdErr)
return InstallOneVersion(conf, plugin, resolvedVersion, false, stdOut, stdErr)
}
// InstallOneVersion installs a specific version of a specific tool
func InstallOneVersion(conf config.Config, plugin plugins.Plugin, versionStr string, stdOut io.Writer, stdErr io.Writer) error {
func InstallOneVersion(conf config.Config, plugin plugins.Plugin, versionStr string, keepDownload bool, stdOut io.Writer, stdErr io.Writer) error {
err := plugin.Exists()
if err != nil {
return err
@ -192,6 +193,22 @@ func InstallOneVersion(conf config.Config, plugin plugins.Plugin, versionStr str
if err != nil {
return fmt.Errorf("failed to run post-install hook: %w", err)
}
// delete download dir
keep, err := conf.AlwaysKeepDownload()
if err != nil {
return err
}
if keep || keepDownload {
return nil
}
err = os.RemoveAll(downloadDir)
if err != nil {
return fmt.Errorf("failed to remove download dir: %w", err)
}
return nil
}

View File

@ -9,6 +9,7 @@ import (
"asdf/internal/config"
"asdf/internal/plugins"
"asdf/internal/toolversions"
"asdf/repotest"
"github.com/stretchr/testify/assert"
@ -130,14 +131,16 @@ func TestInstallVersion(t *testing.T) {
t.Run("returns error when plugin doesn't exist", func(t *testing.T) {
conf, _ := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallVersion(conf, plugins.New(conf, "non-existent"), "1.2.3", "", &stdout, &stderr)
version := toolversions.Version{Type: "version", Value: "1.2.3"}
err := InstallVersion(conf, plugins.New(conf, "non-existent"), version, &stdout, &stderr)
assert.IsType(t, plugins.PluginMissing{}, err)
})
t.Run("installs latest version of tool when version is 'latest'", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallVersion(conf, plugin, "latest", "", &stdout, &stderr)
version := toolversions.Version{Type: "latest", Value: ""}
err := InstallVersion(conf, plugin, version, &stdout, &stderr)
assert.Nil(t, err)
assertVersionInstalled(t, conf.DataDir, plugin.Name, "2.0.0")
@ -147,7 +150,8 @@ func TestInstallVersion(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallVersion(conf, plugin, "latest", "^1.", &stdout, &stderr)
version := toolversions.Version{Type: "latest", Value: "^1."}
err := InstallVersion(conf, plugin, version, &stdout, &stderr)
assert.Nil(t, err)
assertVersionInstalled(t, conf.DataDir, plugin.Name, "1.1.0")
@ -160,14 +164,14 @@ func TestInstallOneVersion(t *testing.T) {
t.Run("returns error when plugin doesn't exist", func(t *testing.T) {
conf, _ := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugins.New(conf, "non-existent"), "1.2.3", &stdout, &stderr)
err := InstallOneVersion(conf, plugins.New(conf, "non-existent"), "1.2.3", false, &stdout, &stderr)
assert.IsType(t, plugins.PluginMissing{}, err)
})
t.Run("returns error when passed a path version", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, "path:/foo/bar", &stdout, &stderr)
err := InstallOneVersion(conf, plugin, "path:/foo/bar", false, &stdout, &stderr)
assert.ErrorContains(t, err, "uninstallable version: path")
})
@ -175,7 +179,7 @@ func TestInstallOneVersion(t *testing.T) {
t.Run("returns error when plugin version is 'system'", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, "system", &stdout, &stderr)
err := InstallOneVersion(conf, plugin, "system", false, &stdout, &stderr)
assert.IsType(t, UninstallableVersionError{}, err)
})
@ -183,7 +187,7 @@ func TestInstallOneVersion(t *testing.T) {
version := "other-dummy"
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, version, &stdout, &stderr)
err := InstallOneVersion(conf, plugin, version, false, &stdout, &stderr)
assert.Errorf(t, err, "failed to run install callback: exit status 1")
want := "pre_asdf_download_lua other-dummy\npre_asdf_install_lua other-dummy\nDummy couldn't install version: other-dummy (on purpose)\n"
@ -195,19 +199,19 @@ func TestInstallOneVersion(t *testing.T) {
t.Run("returns error when version already installed", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err := InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
assertVersionInstalled(t, conf.DataDir, plugin.Name, "1.0.0")
// Install a second time
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err = InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.NotNil(t, err)
})
t.Run("creates download directory", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err := InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
downloadPath := filepath.Join(conf.DataDir, "downloads", plugin.Name, "1.0.0")
@ -219,7 +223,7 @@ func TestInstallOneVersion(t *testing.T) {
t.Run("creates install directory", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err := InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
installPath := filepath.Join(conf.DataDir, "installs", plugin.Name, "1.0.0")
@ -231,7 +235,7 @@ func TestInstallOneVersion(t *testing.T) {
t.Run("runs pre-download, pre-install and post-install hooks when installation successful", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err := InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
assert.Equal(t, "", stderr.String())
want := "pre_asdf_download_lua 1.0.0\npre_asdf_install_lua 1.0.0\npost_asdf_install_lua 1.0.0\n"
@ -241,7 +245,7 @@ func TestInstallOneVersion(t *testing.T) {
t.Run("installs successfully when plugin exists but version does not", func(t *testing.T) {
conf, plugin := generateConfig(t)
stdout, stderr := buildOutputs()
err := InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err := InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
// Check download directory
@ -263,7 +267,7 @@ func TestInstallOneVersion(t *testing.T) {
assert.Nil(t, err)
plugin := plugins.New(conf, testPluginName)
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err = InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
// no-download install script prints 'install'
@ -354,7 +358,7 @@ func TestUninstall(t *testing.T) {
})
t.Run("uninstalls successfully when plugin and version are installed", func(t *testing.T) {
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err = InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
err := Uninstall(conf, plugin, "1.0.0", &stdout, &stderr)
@ -364,7 +368,7 @@ func TestUninstall(t *testing.T) {
t.Run("runs pre and post-uninstall hooks", func(t *testing.T) {
stdout, stderr := buildOutputs()
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err = InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
err := Uninstall(conf, plugin, "1.0.0", &stdout, &stderr)
@ -375,7 +379,7 @@ func TestUninstall(t *testing.T) {
t.Run("invokes uninstall callback when present", func(t *testing.T) {
stdout, stderr := buildOutputs()
err = InstallOneVersion(conf, plugin, "1.0.0", &stdout, &stderr)
err = InstallOneVersion(conf, plugin, "1.0.0", false, &stdout, &stderr)
assert.Nil(t, err)
data := []byte("echo custom uninstall")

View File

@ -31,9 +31,9 @@ func TestBatsTests(t *testing.T) {
runBatsFile(t, dir, "info_command.bats")
})
//t.Run("install_command", func(t *testing.T) {
// runBatsFile(t, dir, "install_command.bats")
//})
t.Run("install_command", func(t *testing.T) {
runBatsFile(t, dir, "install_command.bats")
})
t.Run("latest_command", func(t *testing.T) {
runBatsFile(t, dir, "latest_command.bats")

View File

@ -153,29 +153,31 @@ EOM
[ ! -f "$ASDF_DIR/installs/dummy/1.1.0/version" ]
}
@test "install_command fails if the plugin is not installed" {
cd "$PROJECT_DIR"
echo 'other_dummy 1.0.0' >"$PROJECT_DIR/.tool-versions"
# `asdf install` now enumerates installed plugins, so if a plugin defined in a
# .tool-versions file is not installed `asdf install` now skips it.
#@test "install_command fails if the plugin is not installed" {
# cd "$PROJECT_DIR"
# echo 'other_dummy 1.0.0' >"$PROJECT_DIR/.tool-versions"
run asdf install
[ "$status" -eq 1 ]
[ "$output" = "other_dummy plugin is not installed" ]
}
# run asdf install
# [ "$status" -eq 1 ]
# [ "$output" = "other_dummy plugin is not installed" ]
#}
@test "install_command fails if the plugin is not installed without collisions" {
cd "$PROJECT_DIR"
printf "dummy 1.0.0\ndum 1.0.0" >"$PROJECT_DIR/.tool-versions"
# Not clear how this test differs from those above
#@test "install_command fails if the plugin is not installed without collisions" {
# cd "$PROJECT_DIR"
# printf "dummy 1.0.0\ndum 1.0.0" >"$PROJECT_DIR/.tool-versions"
run asdf install
[ "$status" -eq 1 ]
[ "$output" = "dum plugin is not installed" ]
}
# run asdf install
# [ "$status" -eq 1 ]
# [ "$output" = "dum plugin is not installed" ]
#}
@test "install_command fails when tool is specified but no version of the tool is configured in config file" {
echo 'dummy 1.0.0' >"$PROJECT_DIR/.tool-versions"
run asdf install other-dummy
run asdf install dummy
[ "$status" -eq 1 ]
[ "$output" = "No versions specified for other-dummy in config files or environment" ]
[ "$output" = "No versions specified for dummy in config files or environment" ]
[ ! -f "$ASDF_DIR/installs/dummy/1.0.0/version" ]
}
@ -183,7 +185,7 @@ EOM
printf 'dummy 1.0.0\nother-dummy 2.0.0' >"$PROJECT_DIR/.tool-versions"
run asdf install dummy other-dummy
[ "$status" -eq 1 ]
[ "$output" = "Dummy couldn't install version: other-dummy (on purpose)" ]
[ "$(head -n1 <<<"$output")" = "Dummy couldn't install version: other-dummy (on purpose)" ]
[ ! -f "$ASDF_DIR/installs/dummy/1.0.0/version" ]
[ ! -f "$ASDF_DIR/installs/other-dummy/2.0.0/version" ]
}
@ -217,7 +219,8 @@ EOM
@test "install_command doesn't install system version" {
run asdf install dummy system
[ "$status" -eq 0 ]
[ "$status" -eq 1 ]
[ "$output" = "error installing version: uninstallable version: system" ]
[ ! -f "$ASDF_DIR/installs/dummy/system/version" ]
}
@ -230,9 +233,11 @@ EOM
[ "$output" = "will install dummy 1.0.0" ]
}
# This test has been changed because variables like $version and $plugin_name
# only worked because asdf was a Bash script and leaked those variables.
@test "install command executes configured post plugin install hook" {
cat >"$HOME/.asdfrc" <<-'EOM'
post_asdf_install_dummy = echo HEY $version FROM $plugin_name
post_asdf_install_dummy = echo HEY $1 FROM dummy
EOM
run asdf install dummy 1.0.0
@ -281,7 +286,12 @@ EOM
}
@test "install_command keeps the download directory when --keep-download flag is provided" {
run asdf install dummy 1.1.0 --keep-download
# Original code:
# run asdf install dummy 1.1.0 --keep-download
# Flags should be allowed anywhere, but unfortunately the CLI arg parser
# I'm using only allows them before positional arguments. Hence I've had to
# update this test. But we should fix this soon.
run asdf install --keep-download dummy 1.1.0
[ "$status" -eq 0 ]
[ -d "$ASDF_DIR/downloads/dummy/1.1.0" ]
[ "$(cat "$ASDF_DIR/installs/dummy/1.1.0/version")" = "1.1.0" ]
@ -300,27 +310,14 @@ EOM
[ "$status" -eq 1 ]
[ ! -d "$ASDF_DIR/downloads/dummy-broken/1.1.0" ]
[ ! -d "$ASDF_DIR/installs/dummy-broken/1.1.0" ]
[ "$output" = "Download failed!" ]
[ "$(head -n1 <<<"$output")" = "Download failed!" ]
}
@test "install_command prints info message if plugin does not support preserving download data if --keep-download flag is provided" {
run asdf install dummy-no-download 1.0.0 --keep-download
[ "$status" -eq 0 ]
[[ "$output" == *'asdf: Warn:'*'not be preserved'* ]]
}
@test "install_command prints info message if plugin does not support preserving download data if always_keep_download setting is true" {
echo 'always_keep_download = yes' >"$HOME/.asdfrc"
run asdf install dummy-no-download 1.0.0
[ "$status" -eq 0 ]
[[ "$output" == *'asdf: Warn:'*'not be preserved'* ]]
}
@test "install_command does not print info message if --keep-download flag is not provided and always_keep_download setting is false" {
run asdf install dummy-no-download 1.0.0
[ "$status" -eq 0 ]
[[ "$output" != *'asdf: Warn:'*'not be preserved'* ]]
}
# Download callback is now required
#@test "install_command prints info message if plugin does not support preserving download data if configured" {
# install_dummy_plugin_no_download
#
# run asdf install dummy-no-download 1.0.0
# [ "$status" -eq 0 ]
# [[ "$output" == *'asdf: Warn:'*'not be preserved'* ]]
#}