diff --git a/README.md b/README.md index c0a3f16cdd..3d751731c6 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ $ ruby-build --list # lists latest stable releases for ea $ ruby-build --definitions # lists all definitions, including outdated ones $ ruby-build 3.2.2 ~/.rubies/ruby-3.2.2 # installs Ruby 3.2.2 $ ruby-build -d ruby-3.2.2 ~/.rubies # alternate form for the previous example +$ ruby-build -d 3.2 ~/.rubies/ # installs latest "ruby-3.2" (3.2.10) # As an rbenv plugin $ rbenv install 3.2.2 # installs Ruby 3.2.2 to ~/.rbenv/versions/3.2.2 diff --git a/bin/rbenv-install b/bin/rbenv-install index b5f47800c2..db2d56c5ef 100755 --- a/bin/rbenv-install +++ b/bin/rbenv-install @@ -144,6 +144,12 @@ DEFINITION="${ARGUMENTS[0]}" [ -n "$DEFINITION" ] || DEFINITION="$(rbenv-local 2>/dev/null || true)" [ -n "$DEFINITION" ] || usage 1 >&2 +[ -n "$DEFINITION" ] && RESOLVED_DEFINITION="$(ruby-build --resolve "${DEFINITION}" 2>/dev/null || true)" + +if [ -n "$RESOLVED_DEFINITION" ]; then + DEFINITION="$RESOLVED_DEFINITION" +fi + # Define `before_install` and `after_install` functions that allow # plugin hooks to register a string of code for execution before or # after the installation process. diff --git a/bin/ruby-build b/bin/ruby-build index 480973f6ab..b338c71614 100755 --- a/bin/ruby-build +++ b/bin/ruby-build @@ -2,9 +2,11 @@ # # Usage: ruby-build [-dvpk] [-- ] # ruby-build {--list|--definitions} +# ruby-build {--resolve} # ruby-build --version # # -l, --list List latest stable releases for each Ruby +# --resolve Find the most recent version matching a prefix. # --definitions List all local definitions, including outdated ones # --version Show version of ruby-build # @@ -1401,6 +1403,29 @@ list_maintained_versions() { } | extract_latest_versions | sort_versions | uniq } +filter_version_prefix() +{ + pattern="${1//./\.}" + pattern="${pattern//+/\+}" + grep -e "^${pattern}[-.$]" +} + +resolve() { + prefix="${1%.}" + prefix="${prefix#ruby-}" + + version=$(list_definitions | \ + grep -v -e '-rc[0-9]*$' -e '-preview[0-9]*$' -e '-dev$' | \ + filter_version_prefix "${prefix}" | \ + LC_ALL=C sort -t. -k 1,1 -k 2,2n -k 3,3n -k 4,4n -k 5,5n | \ + tail -n 1) + + [[ -z "${version}" ]] && return 1 + [[ "${1}" = "ruby-"* ]] && version="ruby-${version}" + + echo "${version}" +} + extract_latest_versions() { # sort in this function looks redundunt but it is necessary # rbx-3.99 appears latest unless the sort @@ -1443,6 +1468,9 @@ for option in "${OPTIONS[@]}"; do "l" | "list") EARLY_EXIT=list_maintained_versions ;; + "resolve") + EARLY_EXIT=resolve + ;; "d" | "dir") APPEND_DEFINITION_TO_PREFIX=true ;; @@ -1493,22 +1521,15 @@ if [ "${#EXTRA_ARGUMENTS[@]}" -gt 0 ]; then RUBY_CONFIGURE_OPTS_ARRAY=("${EXTRA_ARGUMENTS[@]}") fi -if [ "$APPEND_DEFINITION_TO_PREFIX" = "true" ]; then - if [ -p "$DEFINITION_PATH" ]; then - echo "ruby-build: using named pipes in combination with \`--dir' is not possible" >&2 - EARLY_EXIT=usage_error - fi - PREFIX_PATH="$PREFIX_PATH/$(basename "$DEFINITION_PATH")" -fi - case "$EARLY_EXIT" in help ) version echo usage 0 ;; -version | list_definitions | list_maintained_versions ) - "$EARLY_EXIT" +version | list_definitions | list_maintained_versions | resolve ) + shift + "$EARLY_EXIT" "$@" exit 0 ;; usage_error ) @@ -1523,6 +1544,8 @@ usage_error ) ;; esac +DEFINITION_PREFIX="" + # expand the argument to full path of the definition file if [[ ! -f "$DEFINITION_PATH" && ! -p "$DEFINITION_PATH" ]]; then for DEFINITION_DIR in "${RUBY_BUILD_DEFINITIONS[@]}"; do @@ -1534,10 +1557,26 @@ if [[ ! -f "$DEFINITION_PATH" && ! -p "$DEFINITION_PATH" ]]; then # If the given definition is like ruby-X.Y.Z, search again with X.Y.Z if [[ "$DEFINITION_PATH" =~ ^ruby-[0-9] ]]; then - DEFINITION_PATH="${DEFINITION_PATH#ruby-}" + UNPREFIXED_DEFINITION_PATH="${DEFINITION_PATH#ruby-}" for DEFINITION_DIR in "${RUBY_BUILD_DEFINITIONS[@]}"; do - if [ -f "${DEFINITION_DIR}/${DEFINITION_PATH}" ]; then - DEFINITION_PATH="${DEFINITION_DIR}/${DEFINITION_PATH}" + if [ -f "${DEFINITION_DIR}/${UNPREFIXED_DEFINITION_PATH}" ]; then + DEFINITION_PREFIX="ruby-" + DEFINITION_PATH="${DEFINITION_DIR}/${UNPREFIXED_DEFINITION_PATH}" + break + fi + done + fi + + # If the given definition isn't a perfect match, we do a prefix search. + if [ ! -f "$DEFINITION_PATH" ]; then + FULLY_QUALIFIED_VERSION=$(resolve "$DEFINITION_PATH") + for DEFINITION_DIR in "${RUBY_BUILD_DEFINITIONS[@]}"; do + if [ -f "${DEFINITION_DIR}/${FULLY_QUALIFIED_VERSION}" ]; then + if [[ "$DEFINITION_PATH" =~ ^ruby-[0-9] ]]; then + DEFINITION_PREFIX="ruby-" + fi + + DEFINITION_PATH="${DEFINITION_DIR}/${FULLY_QUALIFIED_VERSION}" break fi done @@ -1549,6 +1588,14 @@ if [[ ! -f "$DEFINITION_PATH" && ! -p "$DEFINITION_PATH" ]]; then fi fi +if [ "$APPEND_DEFINITION_TO_PREFIX" = "true" ]; then + if [ -p "$DEFINITION_PATH" ]; then + echo "ruby-build: using named pipes in combination with \`--dir' is not possible" >&2 + EARLY_EXIT=usage_error + fi + PREFIX_PATH="$PREFIX_PATH/${DEFINITION_PREFIX}$(basename "$DEFINITION_PATH")" +fi + # normalize the argument if [ "${PREFIX_PATH#/}" = "$PREFIX_PATH" ]; then PREFIX_PATH="${PWD}/${PREFIX_PATH}" diff --git a/test/definitions.bats b/test/definitions.bats old mode 100644 new mode 100755 index dc510d01c6..87b40fdfe9 --- a/test/definitions.bats +++ b/test/definitions.bats @@ -60,6 +60,76 @@ NUM_DEFINITIONS="$(ls "$BATS_TEST_DIRNAME"/../share/ruby-build | wc -l)" assert_success "" } +@test "installing definition by prefix" { + export RUBY_BUILD_DEFINITIONS="${TMP}/definitions" + mkdir -p "${TMP}/definitions" + + echo false > "${TMP}/definitions/1.8.6" + echo false > "${TMP}/definitions/1.9.3" + echo true > "${TMP}/definitions/1.9.10" + echo false > "${TMP}/definitions/1.90.0" + echo false > "${TMP}/definitions/2.0.0" + + run bin/ruby-build "1.9" "${TMP}/install" + assert_success "" +} + +@test "resolve definition by version prefix" { + export RUBY_BUILD_DEFINITIONS="${TMP}/definitions" + mkdir -p "${TMP}/definitions" + + touch "${TMP}/definitions/1.8.6" + touch "${TMP}/definitions/1.9.3" + touch "${TMP}/definitions/1.9.10" + touch "${TMP}/definitions/1.90.0" + touch "${TMP}/definitions/2.0.0" + + run bin/ruby-build --resolve "1.9" "${TMP}/install" + assert_success "1.9.10" +} + +@test "resolve definition with ruby prefix" { + export RUBY_BUILD_DEFINITIONS="${TMP}/definitions" + mkdir -p "${TMP}/definitions" + + touch "${TMP}/definitions/1.8.6" + touch "${TMP}/definitions/1.9.3" + touch "${TMP}/definitions/1.9.10" + touch "${TMP}/definitions/1.90.0" + touch "${TMP}/definitions/2.0.0" + + run bin/ruby-build --resolve "ruby-1.9" "${TMP}/install" + assert_success "ruby-1.9.10" +} + +@test "resolve definition by implementation name" { + export RUBY_BUILD_DEFINITIONS="${TMP}/definitions" + mkdir -p "${TMP}/definitions" + + touch "${TMP}/definitions/foo-1.8.6" + touch "${TMP}/definitions/foo-1.9.3" + touch "${TMP}/definitions/1.9.10" + touch "${TMP}/definitions/1.90.0" + touch "${TMP}/definitions/2.0.0" + + run bin/ruby-build --resolve "foo" "${TMP}/install" + assert_success "foo-1.9.3" +} + +@test "resolve definition by implementation name and version" { + export RUBY_BUILD_DEFINITIONS="${TMP}/definitions" + mkdir -p "${TMP}/definitions" + + touch "${TMP}/definitions/foo-1.8.6" + touch "${TMP}/definitions/foo-1.9.3" + touch "${TMP}/definitions/1.9.10" + touch "${TMP}/definitions/1.90.0" + touch "${TMP}/definitions/2.0.0" + + run bin/ruby-build --resolve "foo-1.8" "${TMP}/install" + assert_success "foo-1.8.6" +} + @test "installing nonexistent definition" { run ruby-build "nonexistent" "${TMP}/install" assert [ "$status" -eq 2 ] diff --git a/test/installer.bats b/test/installer.bats old mode 100644 new mode 100755 diff --git a/test/rbenv.bats b/test/rbenv.bats old mode 100644 new mode 100755 index 2f52961a31..e5bea4c899 --- a/test/rbenv.bats +++ b/test/rbenv.bats @@ -16,7 +16,7 @@ stub_ruby_build() { } @test "install proper" { - stub_ruby_build 'echo ruby-build "$@"' + stub_ruby_build 'echo 2.1.2' 'echo ruby-build "$@"' run rbenv-install 2.1.2 assert_success "ruby-build 2.1.2 ${RBENV_ROOT}/versions/2.1.2" @@ -27,7 +27,7 @@ stub_ruby_build() { } @test "install with flags" { - stub_ruby_build 'echo "ruby-build $(inspect_args "$@")"' + stub_ruby_build 'echo 2.1.2' 'echo "ruby-build $(inspect_args "$@")"' run rbenv-install -kpv 2.1.2 -- --with-configure-opt="hello world" assert_success "ruby-build --keep --verbose --patch 2.1.2 ${RBENV_ROOT}/versions/2.1.2 -- \"--with-configure-opt=hello world\"" @@ -39,7 +39,7 @@ stub_ruby_build() { @test "suggest running rbenv global after install" { rm -rf "$RBENV_ROOT/version" - stub_ruby_build 'echo ruby-build "$@"' + stub_ruby_build 'echo 2.1.2' 'echo ruby-build "$@"' run rbenv-install 2.1.2 assert_success <&2 && exit 2' \ + stub_ruby_build 'exit 1' \ + 'echo ERROR >&2 && exit 2' \ "--definitions : echo 1.8.7 1.9.3-p0 1.9.3-p194 2.1.2 | tr ' ' $'\\n'" run rbenv-install 1.9.3 @@ -127,10 +128,11 @@ OUT @test "Homebrew upgrade instructions" { stub brew "--prefix : echo '${BATS_TEST_DIRNAME%/*}'" - stub_ruby_build 'echo ERROR >&2 && exit 2' \ + stub_ruby_build 'exit 1' \ + 'echo ERROR >&2 && exit 2' \ "--definitions : true" - run rbenv-install 1.9.3 + run rbenv-install 1.9.10 assert_failure assert_output <