Merge pull request #87 from asdf-vm/tb/more-shim-exec-fixes-2

feat(golang-rewrite): get remaining `shim_exec.bats` tests passing
This commit is contained in:
Trevor Brown 2024-12-03 08:29:01 -05:00 committed by Trevor Brown
commit 609d60686b
6 changed files with 145 additions and 91 deletions

View File

@ -17,6 +17,7 @@ import (
"asdf/internal/execenv"
"asdf/internal/execute"
"asdf/internal/help"
"asdf/internal/hook"
"asdf/internal/info"
"asdf/internal/installs"
"asdf/internal/plugins"
@ -453,6 +454,12 @@ func execCommand(logger *log.Logger, command string, args []string) error {
env = execenv.MergeEnv(execenv.SliceToMap(os.Environ()), env)
err = hook.RunWithOutput(conf, fmt.Sprintf("pre_%s_%s", plugin.Name, filepath.Base(executable)), args, os.Stdout, os.Stderr)
if err != nil {
os.Exit(1)
return err
}
return exec.Exec(executable, args, execute.MapToSlice(env))
}

View File

@ -57,6 +57,17 @@ In practice this isn't much of a problem. Most shell scripts DO contain a
shebang line. If a tool managed by asdf provides scripts that don't have a
shebang line one will need to be added to them.
### Custom shim templates are no longer supported
This was a rarely used feature. The only plugin maintained by the core team
that used it was the Elixir plugin, and it no longer needs it. This feature
was originally added so that shim that get evaluated by a program rather than
executed contain code that is suitable for evaluation by a particular program
(in the case of Elixir this was the `iex` shell). Upon further investigation
it seems this feature only exists because the `PATH` for executables was
sometimes improperly set to include the **shims** rather than the other
**executables** for the selected version(s).
## Installation
Installation of version 0.15.0 is much simpler than previous versions of asdf. It's just three steps:

View File

@ -160,23 +160,28 @@ func ExecutableOnPath(path, command string) (string, error) {
// GetExecutablePath returns the path of the executable
func GetExecutablePath(conf config.Config, plugin plugins.Plugin, shimName string, version toolversions.Version) (string, error) {
path, err := getCustomExecutablePath(conf, plugin, shimName, version)
if err == nil {
return path, err
}
executables, err := ToolExecutables(conf, plugin, version)
if err != nil {
return "", err
}
executable := ""
for _, executablePath := range executables {
executableName := filepath.Base(executablePath)
if executableName == shimName {
return executablePath, nil
if filepath.Base(executablePath) == shimName {
executable = executablePath
}
}
path, err := getCustomExecutablePath(conf, plugin, shimName, version, executable)
if err == nil {
return path, err
}
if executable != "" {
return executable, nil
}
return "", fmt.Errorf("executable not found")
}
@ -195,19 +200,24 @@ func GetToolsAndVersionsFromShimFile(shimPath string) (versions []toolversions.T
return versions, err
}
func getCustomExecutablePath(conf config.Config, plugin plugins.Plugin, shimName string, version toolversions.Version) (string, error) {
func getCustomExecutablePath(conf config.Config, plugin plugins.Plugin, shimName string, version toolversions.Version, executablePath string) (string, error) {
var stdOut strings.Builder
var stdErr strings.Builder
installPath := installs.InstallPath(conf, plugin, version)
env := map[string]string{"ASDF_INSTALL_TYPE": "version"}
err := plugin.RunCallback("exec-path", []string{installPath, shimName}, env, &stdOut, &stdErr)
relativePath, err := filepath.Rel(installPath, executablePath)
if err != nil {
return "", err
}
return filepath.Join(installPath, stdOut.String()), err
err = plugin.RunCallback("exec-path", []string{installPath, shimName, relativePath}, env, &stdOut, &stdErr)
if err != nil {
return "", err
}
return filepath.Join(installPath, strings.TrimSpace(stdOut.String())), err
}
// RemoveAll removes all shim scripts

View File

@ -121,11 +121,26 @@ func TestGetExecutablePath(t *testing.T) {
t.Run("returns custom path when plugin has exec-path callback", func(t *testing.T) {
// Create exec-path callback
installDummyExecPathScript(t, conf, plugin, version, "dummy")
installDummyExecPathScript(t, conf, plugin, version, "dummy", "echo 'bin/custom/dummy'")
path, err := GetExecutablePath(conf, plugin, "dummy", version)
assert.Nil(t, err)
assert.Equal(t, filepath.Base(filepath.Dir(path)), "custom")
// Doesn't contain any trailing whitespace (newlines as the last char are common)
assert.Equal(t, path, strings.TrimSpace(path))
})
t.Run("returns default path when plugin has exec-path callback that prints third argument", func(t *testing.T) {
// Create exec-path callback
installDummyExecPathScript(t, conf, plugin, version, "dummy", "echo \"$3\"")
path, err := GetExecutablePath(conf, plugin, "dummy", version)
assert.Nil(t, err)
assert.Equal(t, filepath.Base(path), "dummy")
assert.Equal(t, filepath.Base(filepath.Dir(path)), "bin")
// Doesn't contain any trailing whitespace (newlines as the last char are common)
assert.Equal(t, path, strings.TrimSpace(path))
})
}
@ -426,10 +441,10 @@ func generateConfig(t *testing.T) (config.Config, plugins.Plugin) {
return conf, installPlugin(t, conf, "dummy_plugin", testPluginName)
}
func installDummyExecPathScript(t *testing.T, conf config.Config, plugin plugins.Plugin, version toolversions.Version, name string) {
func installDummyExecPathScript(t *testing.T, conf config.Config, plugin plugins.Plugin, version toolversions.Version, name, script string) {
t.Helper()
execPath := filepath.Join(plugin.Dir, "bin", "exec-path")
contents := fmt.Sprintf("#!/usr/bin/env bash\necho 'bin/custom/%s'", name)
contents := fmt.Sprintf("#!/usr/bin/env bash\n%s\n", script)
err := os.WriteFile(execPath, []byte(contents), 0o777)
assert.Nil(t, err)

View File

@ -79,9 +79,9 @@ func TestBatsTests(t *testing.T) {
runBatsFile(t, dir, "shim_env_command.bats")
})
//t.Run("shim_exec", func(t *testing.T) {
// runBatsFile(t, dir, "shim_exec.bats")
//})
t.Run("shim_exec", func(t *testing.T) {
runBatsFile(t, dir, "shim_exec.bats")
})
t.Run("shim_versions_command", func(t *testing.T) {
runBatsFile(t, dir, "shim_versions_command.bats")

View File

@ -243,91 +243,96 @@ teardown() {
[ "$output" = "System" ]
}
@test "shim exec should use custom exec-env for tool" {
run asdf install dummy 2.0.0
echo "export FOO=sourced" >"$ASDF_DIR/plugins/dummy/bin/exec-env"
mkdir "$ASDF_DIR/plugins/dummy/shims"
echo 'echo $FOO custom' >"$ASDF_DIR/plugins/dummy/shims/foo"
chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
run asdf reshim dummy 2.0.0
# These tests are disabled because the custom shims templates feature is no
# longer supported.
#
#@test "shim exec should use custom exec-env for tool" {
# run asdf install dummy 2.0.0
# echo '#!/usr/bin/env bash
# export FOO=sourced' >"$ASDF_DIR/plugins/dummy/bin/exec-env"
# mkdir "$ASDF_DIR/plugins/dummy/shims"
# echo '#!/usr/bin/env bash
# echo $FOO custom' >"$ASDF_DIR/plugins/dummy/shims/foo"
# chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
# run asdf reshim dummy 2.0.0
echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
run "$ASDF_DIR/shims/foo"
[ "$output" = "sourced custom" ]
}
# echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
# run "$ASDF_DIR/shims/foo"
# [ "$output" = "sourced custom" ]
#}
@test "shim exec with custom exec-env using ASDF_INSTALL_PATH" {
run asdf install dummy 2.0.0
echo 'export FOO=$ASDF_INSTALL_PATH/foo' >"$ASDF_DIR/plugins/dummy/bin/exec-env"
mkdir "$ASDF_DIR/plugins/dummy/shims"
echo 'echo $FOO custom' >"$ASDF_DIR/plugins/dummy/shims/foo"
chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
run asdf reshim dummy 2.0.0
#@test "shim exec with custom exec-env using ASDF_INSTALL_PATH" {
# run asdf install dummy 2.0.0
# echo 'export FOO=$ASDF_INSTALL_PATH/foo' >"$ASDF_DIR/plugins/dummy/bin/exec-env"
# mkdir "$ASDF_DIR/plugins/dummy/shims"
# echo 'echo $FOO custom' >"$ASDF_DIR/plugins/dummy/shims/foo"
# chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
# run asdf reshim dummy 2.0.0
echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
run "$ASDF_DIR/shims/foo"
[ "$output" = "$ASDF_DIR/installs/dummy/2.0.0/foo custom" ]
}
# echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
# run "$ASDF_DIR/shims/foo"
# [ "$output" = "$ASDF_DIR/installs/dummy/2.0.0/foo custom" ]
#}
@test "shim exec doest not use custom exec-env for system version" {
run asdf install dummy 2.0.0
echo "export FOO=sourced" >"$ASDF_DIR/plugins/dummy/bin/exec-env"
mkdir "$ASDF_DIR/plugins/dummy/shims"
echo 'echo $FOO custom' >"$ASDF_DIR/plugins/dummy/shims/foo"
chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
run asdf reshim dummy 2.0.0
#@test "shim exec doest not use custom exec-env for system version" {
# run asdf install dummy 2.0.0
# echo "export FOO=sourced" >"$ASDF_DIR/plugins/dummy/bin/exec-env"
# mkdir "$ASDF_DIR/plugins/dummy/shims"
# echo 'echo $FOO custom' >"$ASDF_DIR/plugins/dummy/shims/foo"
# chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
# run asdf reshim dummy 2.0.0
echo "dummy system" >"$PROJECT_DIR/.tool-versions"
# echo "dummy system" >"$PROJECT_DIR/.tool-versions"
mkdir "$PROJECT_DIR/sys/"
echo 'echo x$FOO System' >"$PROJECT_DIR/sys/foo"
chmod +x "$PROJECT_DIR/sys/foo"
# mkdir "$PROJECT_DIR/sys/"
# echo 'echo x$FOO System' >"$PROJECT_DIR/sys/foo"
# chmod +x "$PROJECT_DIR/sys/foo"
run env "PATH=$PATH:$PROJECT_DIR/sys" "$ASDF_DIR/shims/foo"
[ "$output" = "x System" ]
}
# run env "PATH=$PATH:$PROJECT_DIR/sys" "$ASDF_DIR/shims/foo"
# [ "$output" = "x System" ]
#}
@test "shim exec should prepend the plugin paths on execution" {
run asdf install dummy 2.0.0
#@test "shim exec should prepend the plugin paths on execution" {
# run asdf install dummy 2.0.0
mkdir "$ASDF_DIR/plugins/dummy/shims"
echo 'which dummy' >"$ASDF_DIR/plugins/dummy/shims/foo"
chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
run asdf reshim dummy 2.0.0
# mkdir "$ASDF_DIR/plugins/dummy/shims"
# echo 'which dummy' >"$ASDF_DIR/plugins/dummy/shims/foo"
# chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
# run asdf reshim dummy 2.0.0
echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
# echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
run "$ASDF_DIR/shims/foo"
[ "$output" = "$ASDF_DIR/installs/dummy/2.0.0/bin/dummy" ]
}
# run "$ASDF_DIR/shims/foo"
# [ "$output" = "$ASDF_DIR/installs/dummy/2.0.0/bin/dummy" ]
#}
@test "shim exec should be able to find other shims in path" {
cp -rf "$ASDF_DIR/plugins/dummy" "$ASDF_DIR/plugins/gummy"
#@test "shim exec should be able to find other shims in path" {
# cp -rf "$ASDF_DIR/plugins/dummy" "$ASDF_DIR/plugins/gummy"
echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
echo "gummy 2.0.0" >>"$PROJECT_DIR/.tool-versions"
# echo "dummy 2.0.0" >"$PROJECT_DIR/.tool-versions"
# echo "gummy 2.0.0" >>"$PROJECT_DIR/.tool-versions"
run asdf install
# run asdf install
mkdir "$ASDF_DIR/plugins/"{dummy,gummy}/shims
# mkdir "$ASDF_DIR/plugins/"{dummy,gummy}/shims
echo 'which dummy' >"$ASDF_DIR/plugins/dummy/shims/foo"
chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
# echo 'which dummy' >"$ASDF_DIR/plugins/dummy/shims/foo"
# chmod +x "$ASDF_DIR/plugins/dummy/shims/foo"
echo 'which gummy' >"$ASDF_DIR/plugins/dummy/shims/bar"
chmod +x "$ASDF_DIR/plugins/dummy/shims/bar"
# echo 'which gummy' >"$ASDF_DIR/plugins/dummy/shims/bar"
# chmod +x "$ASDF_DIR/plugins/dummy/shims/bar"
touch "$ASDF_DIR/plugins/gummy/shims/gummy"
chmod +x "$ASDF_DIR/plugins/gummy/shims/gummy"
# touch "$ASDF_DIR/plugins/gummy/shims/gummy"
# chmod +x "$ASDF_DIR/plugins/gummy/shims/gummy"
run asdf reshim
# run asdf reshim
run "$ASDF_DIR/shims/foo"
[ "$output" = "$ASDF_DIR/installs/dummy/2.0.0/bin/dummy" ]
# run "$ASDF_DIR/shims/foo"
# [ "$output" = "$ASDF_DIR/installs/dummy/2.0.0/bin/dummy" ]
run "$ASDF_DIR/shims/bar"
[ "$output" = "$ASDF_DIR/shims/gummy" ]
}
# run "$ASDF_DIR/shims/bar"
# [ "$output" = "$ASDF_DIR/shims/gummy" ]
#}
@test "shim exec should remove shim_path from path on system version execution" {
run asdf install dummy 2.0.0
@ -335,7 +340,8 @@ teardown() {
echo "dummy system" >"$PROJECT_DIR/.tool-versions"
mkdir "$PROJECT_DIR/sys/"
echo 'which dummy' >"$PROJECT_DIR/sys/dummy"
echo '#!/usr/bin/env bash
which dummy' >"$PROJECT_DIR/sys/dummy"
chmod +x "$PROJECT_DIR/sys/dummy"
run env "PATH=$PATH:$PROJECT_DIR/sys" "$ASDF_DIR/shims/dummy"
@ -369,7 +375,8 @@ teardown() {
echo "dummy 1.0" >"$PROJECT_DIR/.tool-versions"
mkdir "$custom_path"
echo "echo CUSTOM" >"$custom_path/foo"
echo '#!/usr/bin/env bash
echo CUSTOM' >"$custom_path/foo"
chmod +x "$custom_path/foo"
run asdf reshim dummy 1.0
@ -384,11 +391,13 @@ teardown() {
exec_path="$ASDF_DIR/plugins/dummy/bin/exec-path"
custom_dummy="$ASDF_DIR/installs/dummy/1.0/custom/dummy"
echo "echo custom/dummy" >"$exec_path"
echo '#!/usr/bin/env bash
echo custom/dummy' >"$exec_path"
chmod +x "$exec_path"
mkdir "$(dirname "$custom_dummy")"
echo "echo CUSTOM" >"$custom_dummy"
echo '#!/usr/bin/env bash
echo CUSTOM' >"$custom_dummy"
chmod +x "$custom_dummy"
echo "dummy 1.0" >"$PROJECT_DIR/.tool-versions"
@ -401,7 +410,8 @@ teardown() {
run asdf install dummy 1.0
exec_path="$ASDF_DIR/plugins/dummy/bin/exec-path"
echo 'echo $3 # always same path' >"$exec_path"
echo '#!/usr/bin/env bash
echo "$3" # always same path' >"$exec_path"
chmod +x "$exec_path"
echo "dummy 1.0" >"$PROJECT_DIR/.tool-versions"
@ -415,12 +425,12 @@ teardown() {
echo dummy 1.0 >"$PROJECT_DIR/.tool-versions"
cat >"$HOME/.asdfrc" <<-'EOM'
pre_dummy_dummy = echo PRE $version $1 $2
pre_dummy_dummy = echo PRE $1 $2
EOM
run "$ASDF_DIR/shims/dummy" hello world
[ "$status" -eq 0 ]
echo "$output" | grep "PRE 1.0 hello world"
echo "$output" | grep "PRE hello world"
echo "$output" | grep "This is Dummy 1.0! world hello"
}
@ -430,15 +440,16 @@ EOM
mkdir "$HOME/hook"
pre_cmd="$HOME/hook/pre"
echo 'echo $* && false' >"$pre_cmd"
echo '#!/usr/bin/env bash
echo $* && false' >"$pre_cmd"
chmod +x "$pre_cmd"
cat >"$HOME/.asdfrc" <<'EOM'
pre_dummy_dummy = pre $1 no $plugin_name $2
pre_dummy_dummy = pre $1 $2
EOM
run env PATH="$PATH:$HOME/hook" "$ASDF_DIR/shims/dummy" hello world
[ "$output" = "hello no dummy world" ]
[ "$output" = "hello world" ]
[ "$status" -eq 1 ]
}