From 5ce2fd8a2f2d4f27b100debb7273b3bbad34f3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Fri, 7 Oct 2022 16:16:34 +0200 Subject: [PATCH 1/2] Enable fallback to PATH from rbenv shims Previously, when someone activated rbenv shim `foo`, rbenv would error out if `RBENV_ROOT/versions/RBENV_VERSION/bin/foo` did not exist. This change allows `foo` to be additionally looked up in PATH as fallback. This prevents a case where one of the Ruby versions is shadowing a system-wide command of the same name, preventing the use of the global command across all other Ruby versions. rbenv used to be strict around this, allowing the fallback to avoid ever falling back to system Ruby for invocations like `bundle` if the current Ruby version did not yet install Bundler. The current approach prevents this scenario by explicitly disallowing fallback for the following executables: ruby, rake, gem, bundle, bundler, irb, rdoc, ri. --- libexec/rbenv-exec | 22 +++++++++++++--------- libexec/rbenv-which | 38 +++++++++++++++++++++++++++++++------- test/exec.bats | 20 ++++++++++++++++++++ 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/libexec/rbenv-exec b/libexec/rbenv-exec index e05ce51101..2de3274b70 100755 --- a/libexec/rbenv-exec +++ b/libexec/rbenv-exec @@ -1,17 +1,21 @@ #!/usr/bin/env bash # -# Summary: Run an executable with the selected Ruby version +# Summary: Run an executable with the current Ruby version # -# Usage: rbenv exec [arg1 arg2...] +# Usage: rbenv exec [...] # -# Runs an executable by first preparing PATH so that the selected Ruby -# version's `bin' directory is at the front. +# Runs an executable by first prepending the current Ruby version's +# `bin' directory to PATH. # -# For example, if the currently selected Ruby version is 1.9.3-p327: -# rbenv exec bundle install +# For example, this invocation: # -# is equivalent to: -# PATH="$RBENV_ROOT/versions/1.9.3-p327/bin:$PATH" bundle install +# RBENV_VERSION=3.1.2 rbenv exec bundle install +# +# roughly translates to: +# +# PATH="$RBENV_ROOT/versions/3.1.2/bin:$PATH" bundle install +# +# See `rbenv help which' for information about the fallback mechanism. set -e [ -n "$RBENV_DEBUG" ] && set -x @@ -41,7 +45,7 @@ for script in "${scripts[@]}"; do done shift 1 -if [ "$RBENV_VERSION" != "system" ]; then +if [ "$RBENV_BIN_PATH" = "${RBENV_ROOT}/versions/${RBENV_VERSION}/bin" ]; then export PATH="${RBENV_BIN_PATH}:${PATH}" fi exec -a "$RBENV_COMMAND" "$RBENV_COMMAND_PATH" "$@" diff --git a/libexec/rbenv-which b/libexec/rbenv-which index 80f0df87ea..c917c3b351 100755 --- a/libexec/rbenv-which +++ b/libexec/rbenv-which @@ -6,6 +6,17 @@ # # Displays the full path to the executable that rbenv will invoke when # you run the given command. +# +# If GEM_HOME is set, the result will be `$GEM_HOME/bin/' if +# it exists. +# +# If the current Ruby version is "system", the command will be looked up +# in PATH. +# +# For other Ruby versions, the command is first looked up in +# `$RBENV_ROOT/versions/$RBENV_VERSION/bin'. If it does not exist and if +# the command is not one of the core Ruby executables (e.g. "ruby", +# "gem", "bundle"), it will be looked up in PATH. set -e [ -n "$RBENV_DEBUG" ] && set -x @@ -36,15 +47,28 @@ fi RBENV_VERSION="${RBENV_VERSION:-$(rbenv-version-name)}" -if [ "$RBENV_VERSION" = "system" ]; then - PATH="$(remove_from_path "${RBENV_ROOT}/shims")" \ - RBENV_COMMAND_PATH="$(command -v "$RBENV_COMMAND" || true)" +if [[ -n "$GEM_HOME" && -x "${GEM_HOME}/bin/${RBENV_COMMAND}" ]]; then + RBENV_COMMAND_PATH="${GEM_HOME}/bin/${RBENV_COMMAND}" +elif [ "$RBENV_VERSION" = "system" ]; then + PATH="$(remove_from_path "${RBENV_ROOT}/shims")" RBENV_COMMAND_PATH="$(type -P "$RBENV_COMMAND" || true)" else RBENV_COMMAND_PATH="${RBENV_ROOT}/versions/${RBENV_VERSION}/bin/${RBENV_COMMAND}" -fi - -if [[ ! -x "$RBENV_COMMAND_PATH" && -n "$GEM_HOME" && -x "${GEM_HOME}/bin/${RBENV_COMMAND}" ]]; then - RBENV_COMMAND_PATH="${GEM_HOME}/bin/${RBENV_COMMAND}" + if [ ! -x "$RBENV_COMMAND_PATH" ]; then + # Look up the command in PATH as fallback. + case "$RBENV_COMMAND" in + ruby | rake | gem | bundle | bundler | irb | rdoc | ri ) + # Do not allow fallback to PATH for core Ruby commands. If any of these are missing + # in the current Ruby version, error out instead of allowing a potentially unrelated + # Ruby version to activate and potentially muck up the current project. + ;; + * ) + if PATH="$(remove_from_path "${RBENV_ROOT}/shims")" RBENV_COMMAND_FALLBACK="$(type -P "$RBENV_COMMAND")"; then + RBENV_COMMAND_PATH="$RBENV_COMMAND_FALLBACK" + fi + unset RBENV_COMMAND_FALLBACK + ;; + esac + fi fi OLDIFS="$IFS" diff --git a/test/exec.bats b/test/exec.bats index 1ee7435781..2095be4509 100644 --- a/test/exec.bats +++ b/test/exec.bats @@ -78,6 +78,26 @@ ${RBENV_ROOT}/versions/2.0/bin/ruby OUT } +@test "prepends Ruby to PATH" { + export RBENV_VERSION="2.0" + create_executable "ruby" <" { export RBENV_VERSION="2.0" From 82e5ad053b78adeb12674fe41d635b00750f5976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Sun, 9 Oct 2022 13:56:24 +0200 Subject: [PATCH 2/2] avoid shellcheck warnings in tests --- test/exec.bats | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/exec.bats b/test/exec.bats index 2095be4509..7b33fd8a2e 100644 --- a/test/exec.bats +++ b/test/exec.bats @@ -15,8 +15,7 @@ create_executable() { } @test "fails with invalid version" { - export RBENV_VERSION="2.0" - run rbenv-exec ruby -v + RBENV_VERSION="2.0" run rbenv-exec ruby -v assert_failure "rbenv: version \`2.0' is not installed (set by RBENV_VERSION environment variable)" } @@ -29,6 +28,7 @@ create_executable() { } @test "completes with names of executables" { + # shellcheck disable=SC2030,SC2031 export RBENV_VERSION="2.0" create_executable "ruby" "#!/bin/sh" create_executable "rake" "#!/bin/sh" @@ -49,13 +49,13 @@ hellos=(\$(printf "hello\\tugly world\\nagain")) echo HELLO="\$(printf ":%s" "\${hellos[@]}")" SH - export RBENV_VERSION=system - IFS=$' \t\n' run rbenv-exec env + RBENV_VERSION=system IFS=$' \t\n' run rbenv-exec env assert_success assert_line "HELLO=:hello:ugly:world:again" } @test "forwards all arguments" { + # shellcheck disable=SC2030,SC2031 export RBENV_VERSION="2.0" create_executable "ruby" <" { + # shellcheck disable=SC2030,SC2031 export RBENV_VERSION="2.0" # emulate `ruby -S' behavior