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] 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 e05ce51..2de3274 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 80f0df8..c917c3b 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 1ee7435..2095be4 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"