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/execenv"
"asdf/internal/execute" "asdf/internal/execute"
"asdf/internal/help" "asdf/internal/help"
"asdf/internal/hook"
"asdf/internal/info" "asdf/internal/info"
"asdf/internal/installs" "asdf/internal/installs"
"asdf/internal/plugins" "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) 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)) 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. If a tool managed by asdf provides scripts that don't have a
shebang line one will need to be added to them. 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
Installation of version 0.15.0 is much simpler than previous versions of asdf. It's just three steps: 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 // GetExecutablePath returns the path of the executable
func GetExecutablePath(conf config.Config, plugin plugins.Plugin, shimName string, version toolversions.Version) (string, error) { 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) executables, err := ToolExecutables(conf, plugin, version)
if err != nil { if err != nil {
return "", err return "", err
} }
executable := ""
for _, executablePath := range executables { for _, executablePath := range executables {
executableName := filepath.Base(executablePath) if filepath.Base(executablePath) == shimName {
if executableName == shimName { executable = executablePath
return executablePath, nil
} }
} }
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") return "", fmt.Errorf("executable not found")
} }
@ -195,19 +200,24 @@ func GetToolsAndVersionsFromShimFile(shimPath string) (versions []toolversions.T
return versions, err 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 stdOut strings.Builder
var stdErr strings.Builder var stdErr strings.Builder
installPath := installs.InstallPath(conf, plugin, version) installPath := installs.InstallPath(conf, plugin, version)
env := map[string]string{"ASDF_INSTALL_TYPE": "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 { if err != nil {
return "", err 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 // 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) { t.Run("returns custom path when plugin has exec-path callback", func(t *testing.T) {
// Create exec-path callback // 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) path, err := GetExecutablePath(conf, plugin, "dummy", version)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, filepath.Base(filepath.Dir(path)), "custom") 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) 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() t.Helper()
execPath := filepath.Join(plugin.Dir, "bin", "exec-path") 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) err := os.WriteFile(execPath, []byte(contents), 0o777)
assert.Nil(t, err) assert.Nil(t, err)

View File

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

View File

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