From 959968c46d1346db57ec664a0d4ef9a463580368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 26 Sep 2022 02:57:15 +0200 Subject: [PATCH] Support GEM_HOME, add limited support for user-installed gems (#1436) The rehash process will now discover executables in additional locations: - `~/.gem/ruby//bin/*` - `$GEM_HOME/bin` The `rbenv which` (and thus `rbenv exec`) command will also search these locations when looking up a command. This enables shims to dispatch calls to executables added by `gem install --user-install`. Note that this support is limited: - It will only work with C Ruby, as it's difficult to guess the `~/.gem//` directory for other Rubies without actually loading Ruby; - It will only work for RBENV_VERSION values in the format `X.Y.Z` and not "system". --- libexec/rbenv-rehash | 9 +++++ libexec/rbenv-which | 11 ++++++ rbenv.d/exec/gem-rehash/rubygems_plugin.rb | 9 +++++ test/rehash.bats | 45 ++++++++++++++++++++-- test/which.bats | 17 +++++++- 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/libexec/rbenv-rehash b/libexec/rbenv-rehash index c5f8d551..56b8a56b 100755 --- a/libexec/rbenv-rehash +++ b/libexec/rbenv-rehash @@ -104,6 +104,15 @@ list_executable_names() { echo "${file##*/}" done done + # list user-install'ed executables + for file in ~/.gem/ruby/*/bin/*; do + echo "${file##*/}" + done + if [ -n "$GEM_HOME" ]; then + for file in "$GEM_HOME"/bin/*; do + echo "${file##*/}" + done + fi } # The basename of each argument passed to `make_shims` will be diff --git a/libexec/rbenv-which b/libexec/rbenv-which index b354b1e4..f4d0b8ff 100755 --- a/libexec/rbenv-which +++ b/libexec/rbenv-which @@ -41,6 +41,17 @@ if [ "$RBENV_VERSION" = "system" ]; then RBENV_COMMAND_PATH="$(command -v "$RBENV_COMMAND" || true)" else RBENV_COMMAND_PATH="${RBENV_ROOT}/versions/${RBENV_VERSION}/bin/${RBENV_COMMAND}" + if [ ! -x "$RBENV_COMMAND_PATH" ]; then + # discover user-install'ed gem executables + if [ -n "$GEM_HOME" ]; then + user_install_path="${GEM_HOME}/bin/${RBENV_COMMAND}" + else + # FIXME: guessing this path is very fragile and works only for C Ruby + user_install_path="${HOME}/.gem/ruby/${RBENV_VERSION%.*}.0/bin/${RBENV_COMMAND}" + fi + [ -x "$user_install_path" ] && RBENV_COMMAND_PATH="$user_install_path" + unset user_install_path + fi fi OLDIFS="$IFS" diff --git a/rbenv.d/exec/gem-rehash/rubygems_plugin.rb b/rbenv.d/exec/gem-rehash/rubygems_plugin.rb index 7ced31a9..423720f9 100644 --- a/rbenv.d/exec/gem-rehash/rubygems_plugin.rb +++ b/rbenv.d/exec/gem-rehash/rubygems_plugin.rb @@ -37,6 +37,15 @@ else begin Gem.post_install(&hook) Gem.post_uninstall(&hook) + + # Silence the warning that would be printed for --user-install'ed gems: + # + # WARNING: You don't have ~/.gem/ruby//bin in your PATH, + # gem executables will not run. + # + # This warning isn't accurate in the context of rbenv because the executables + # at this location will automatically be available for running through rbenv. + Gem::Installer.path_warning = true if Gem::Installer.respond_to?(:path_warning=) rescue warn "rbenv: error installing gem-rehash hooks (#{$!.class.name}: #{$!.message})" end diff --git a/test/rehash.bats b/test/rehash.bats index 15d7a89b..e0c6627c 100755 --- a/test/rehash.bats +++ b/test/rehash.bats @@ -3,10 +3,11 @@ load test_helper create_executable() { - local bin="${RBENV_ROOT}/versions/${1}/bin" - mkdir -p "$bin" - touch "${bin}/$2" - chmod +x "${bin}/$2" + local exe="${RBENV_ROOT}/versions/${1}/bin/${2}" + [ -n "$2" ] || exe="$1" + mkdir -p "${exe%/*}" + touch "$exe" + chmod +x "$exe" } @test "empty rehash" { @@ -104,6 +105,42 @@ ruby OUT } +@test "user-install" { + create_executable "${HOME}/.gem/ruby/3.0.0/bin/lolcat" + create_executable "${HOME}/.gem/ruby/3.1.0/bin/pinecone" + + assert [ ! -e "${RBENV_ROOT}/shims/lolcat" ] + assert [ ! -e "${RBENV_ROOT}/shims/pinecone" ] + + run rbenv-rehash + assert_success "" + + run ls "${RBENV_ROOT}/shims" + assert_success + assert_output <