Prepend project bin/ to PATH in bundle exec for load-relative Ruby#9478
Open
masak1yu wants to merge 2 commits intoruby:masterfrom
Open
Prepend project bin/ to PATH in bundle exec for load-relative Ruby#9478masak1yu wants to merge 2 commits intoruby:masterfrom
masak1yu wants to merge 2 commits intoruby:masterfrom
Conversation
When Ruby is compiled with `--enable-load-relative` (as done by mise), RubyGems generates binstubs with a `#!/bin/sh` wrapper that resolves `ruby` relative to the binstub directory (`exec "$bindir/ruby"`). When `BUNDLE_PATH` is set to `vendor/bundle`, `set_path` prepends `vendor/bundle/ruby/X.Y.Z/bin` to PATH. `bundle exec` then finds the vendor binstub first, but since its shebang is `#!/bin/sh` (not `#!/usr/bin/env ruby`), `ruby_shebang?` returns false and Bundler falls back to `kernel_exec`. The OS executes the shell wrapper, which looks for `$bindir/ruby` in the vendor bin directory — where no `ruby` binary exists — and fails. This fix prepends the project `bin/` directory (where Bundler generates binstubs with `#!/usr/bin/env ruby`) to PATH before the vendor bin path, so `Bundler.which` finds the correctly-shimmed binstub first. The project bin/ is only added when the directory actually exists. Fixes an interaction between load-relative Ruby builds (mise, some asdf configurations) and `BUNDLE_PATH=vendor/bundle`. Related: ruby#8441 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lves correctly The test was failing because BUNDLE_GEMFILE was set to a relative "Gemfile" path (inherited from the parent describe block), causing Bundler.root to resolve to the process's current working directory instead of bundled_app. This meant bin_path.directory? could not find the test's bin/ directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When Ruby is compiled with
--enable-load-relative(as done by mise), RubyGems generates binstubs with a#!/bin/shshell wrapper that resolvesrubyrelative to the binstub directory:When
BUNDLE_PATHis set tovendor/bundle,set_pathprependsvendor/bundle/ruby/X.Y.Z/bintoPATH.bundle execthen finds the vendor binstub first viaBundler.which, but since its shebang is#!/bin/sh(not#!/usr/bin/env ruby),ruby_shebang?returnsfalseand Bundler falls back tokernel_exec. The OS executes the shell wrapper, which looks for$bindir/rubyin the vendor bin directory — where norubybinary exists — and fails:Fix
This PR prepends the project
bin/directory (where Bundler generates binstubs with#!/usr/bin/env rubyviaenv_shebang: true) toPATHbefore the vendor bin path inSharedHelpers#set_path. This ensuresBundler.whichfinds the correctly-shimmed project binstub first, allowingruby_shebang?to returntrueandkernel_loadto execute the command in-process.The project
bin/is only added when the directory actually exists, avoiding side effects for projects without binstubs.Root Cause Analysis
Three factors combine to produce this bug:
--enable-load-relativeRuby (mise builds):LIBRUBY_RELATIVE=yestriggersbash_prolog_scriptinGem::Installer#shebang, wrapping every vendor binstub in a#!/bin/shprologueset_pathonly prepends vendor bin:vendor/bundle/ruby/X.Y.Z/binis added to PATH butbin/is not, so the broken vendor binstub is found firstruby_shebang?doesn't match#!/bin/sh: The shell wrapper falls through tokernel_exec, which executes it as a shell script that fails to find$bindir/rubyRelated Issues
Test Plan
bin/directory is prepended to PATH before vendor bundle bin when it existsbin/is not added to PATH when the directory doesn't existbundle exec rspecworks on a project withBUNDLE_PATH=vendor/bundleand mise Ruby 3.3.10