From 52aa20936e72a0858d2e8b6f889069159bb32bf3 Mon Sep 17 00:00:00 2001 From: Leo Arnold Date: Mon, 25 May 2026 17:49:04 +0200 Subject: [PATCH] Install Bundler in older Ruby versions In Ruby versions prior to 2.6 is not installed by default. For the skilled `asdf` user, the solution may _seem_ pretty simple ```shell gem install bundler asdf reshim ruby ``` This will run into yet another problem because `gem` command will simply select the latest available version of `bundler` and then tell you that it is not compatible with your Ruby version. Depending on the version of RubyGems, it may not even suggest a compatible version of `bundler. For the casual `asdf` user, things get even more confusing because they probably also have a contemporary version of Ruby installed (which as the `bundle` command shimmed), so `asdf` will suggest to use the modern Ruby version (instead of telling you how to get `bundler` installed): ```shell $ bundle No version is set for command bundle Consider adding one of the following versions in your config file at /home/johndoe/.tool-versions ruby 4.0.5 ``` Therefore it makes sense to have the `asdf` Ruby plugin install `bundler` by default. Any compatible version of Bundler will do since `Gemfile.lock` specifies the Bundler version required by the project, and the `bundle` command will respond to this. In this commit, we choose to address the issue in the following way: - Ruby v1.8 and below: This is expert territory and will be skipped. Ruby v1.8.7 reached end of life on 2014-07-31 and many projects using this version of below may not even use `bundler` in the first place. - Ruby v2.6 and above: The `bundler` gem is installed by default. We still check for the gem, but the test should pass and nothing needs to be done. - Ruby v1.9 to v2.5: Here we need to install a version of Bundler. Version 1.17.3 is the last v1 release of Bundler and widely accepted as the trusty version of Bundler to use when in doubt, so we will do just that. With this change, `bundler` will be installed and shimmed even when using legacy versions of Ruby, so even casual users of `asdf` will have a pleasent experience. --- bin/install | 51 ++++++++++++++++++++++++++++++++++++++++++--------- lib/utils.sh | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/bin/install b/bin/install index 117d853..d837be2 100755 --- a/bin/install +++ b/bin/install @@ -81,10 +81,48 @@ get_absolute_path() { ) } +gem_command() { + "${ASDF_INSTALL_PATH}/bin/gem" "$@" +} + +# Before Ruby 2.6 Bundler was not installed by default +ensure_bundler_installed() { + echo -n "Checking: Bundler is ... " + + if gem_command list -i bundler >/dev/null 2>&1; then + echo "INSTALLED" + return 0 + else + echo "MISSING" + fi + + if semantic_version_lt "$ASDF_INSTALL_VERSION" "1.9"; then + echo "Skipping installation of Bundler because requested Ruby version is prior to 1.9" + return 0 + fi + + # Version 1.17.3 is compatible with all Ruby versions 1.9 and above. + # This will ensure that some version of Bundler is installed and shimmed. + install_ruby_gem bundler --version 1.17.3 +} + +install_ruby_gem() { + local gem_name=$1 + local args=("${@:2}") + + # shellcheck disable=SC2145 + echo -n "Running: gem install $gem_name ${args[@]:-} ... " + + if output=$(gem_command install "$gem_name" "${args[@]+"${args[@]}"}" 2>&1); then + echo -e "SUCCESS" + else + echo -e "FAIL: $output" + fi +} + install_default_gems() { local args=() local default_gems="${ASDF_GEM_DEFAULT_PACKAGES_FILE:=$HOME/.default-gems}" - local gem="${ASDF_INSTALL_PATH}/bin/gem" if [ ! -f "$default_gems" ]; then return @@ -114,15 +152,9 @@ install_default_gems() { args=() fi - # shellcheck disable=SC2145 - echo -n "Running: gem install $gem_name ${args[@]:-} ... " + install_ruby_gem "$gem_name" "${args[@]}" - if output=$("$gem" install "$gem_name" "${args[@]+"${args[@]}"}" 2>&1); then - echo -e "SUCCESS" - else - echo -e "FAIL: $output" - fi - # echo here adds trailing newline, which is necessary if file lacks one, + # echo (inside install_ruby_gem) adds trailing newline, which is necessary if file lacks one, # empty lines are already skipped some multiple trailing newlines are not a problem. done < <( cat "$default_gems" @@ -131,4 +163,5 @@ install_default_gems() { } install_ruby "$ASDF_INSTALL_TYPE" "$ASDF_INSTALL_VERSION" "$ASDF_INSTALL_PATH" +ensure_bundler_installed install_default_gems diff --git a/lib/utils.sh b/lib/utils.sh index 6e863bf..38d7048 100644 --- a/lib/utils.sh +++ b/lib/utils.sh @@ -76,3 +76,35 @@ ruby_build_source_dir() { ruby_build_path() { echo "$(ruby_build_dir)/bin/ruby-build" } + +# Compare semantic version numbers +# Success if first argument is lower than second argument +semantic_version_lt() { + local IFS=. + local -a v1 v2 + local i + + read -ra v1 <<< "$1" + read -ra v2 <<< "$2" + + # Pad shorter version with zeros + for ((i=${#v1[@]}; i<${#v2[@]}; i++)); do + v1[i]=0 + done + for ((i=${#v2[@]}; i<${#v1[@]}; i++)); do + v2[i]=0 + done + + # Compare each numeric part + for ((i=0; i<${#v1[@]}; i++)); do + # Force base-10 interpretation so values like 08 are not treated as invalid octal numbers + if ((10#${v1[i]} < 10#${v2[i]})); then + return 0 + fi + if ((10#${v1[i]} > 10#${v2[i]})); then + return 1 + fi + done + + return 1 +}