Fix rbenv-built rubies silently running as Homebrew's libruby - #75
Merged
Conversation
The brew-lib rpath baked into rbenv builds (for libcrypt on Linuxbrew) is searched before the ruby's own lib dir; since rubies differing only in teeny share a libruby soname, a same-minor brew Ruby hijacks the build — a 4.0.5 install runs as 4.0.6 and bundler fails the Gemfile ruby pin. Rpath the build's own lib dir first, verify the installed binary reports the requested version, and force-rebuild hijacked installs. Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
codecov/patch flagged the two changed lines no test executed: the missing-extension rebuild branch and the real rbenv install call. Cover the latter with a fake rbenv on a restricted PATH instead of mocking, so the build-env plumbing actually runs. Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
Author
|
/learn the root cause here, not the code change: rbenv-built rubies with a Homebrew lib dir rpathed ahead of their own libdir get their libruby hijacked by a same-minor brew Ruby (shared soname libruby.so.X.Y), so the binary silently runs as the other version — bundler then fails the Gemfile ruby pin with a mismatch that looks like a project bug. Diagnosis cue: command -v ruby resolves to the right binstub but ruby -v reports another version. Only bites Linux (macOS dyld links libruby by absolute install name).
|
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
Root cause of the cellbound-3d#151 CI failure (
Your Ruby version is 4.0.6, but your Gemfile specified 4.0.5), diagnosed with a throwaway workflow on the ephemeral hosted runner: dev provisioned rbenv Ruby 4.0.5 correctly,shadowenv execapplied the right PATH,command -v rubyresolved to the 4.0.5 binstub — andruby -vstill printed 4.0.6.The mechanism is a same-soname shared-library hijack that dev builds into every rbenv Ruby itself:
ruby_build_envbakes-Wl,-rpath,<brew>/libinto the build (needed so Linuxbrew-linked libs likelibcrypt.so.2resolve at runtime).libruby.so.4.0), and Linuxbrew's lib dir carries its own Ruby's libruby.macOS is unaffected (dyld links libruby by absolute install name), which is why this only ever bites Linux boxes whose linuxbrew Ruby shares the minor version. cellbound-3d#107's "bundle exec runs under Linuxbrew Ruby" symptom is the adjacent seam, but this one corrupts even correctly-shadowenv'd invocations.
Changes
ruby_build_envnow rpaths the build's own lib dir ahead of brew's, so its libruby always wins; brew's dir stays for libcrypt et al. (Signature gains the version to compute the install prefix.)ensure_ruby_installed!treats a wrong reported version like a crippled extension: force-rebuild an existing hijacked install, and loudly abort (with the fix command) if the freshly built ruby still runs as another version — instead of surfacing later as a cryptic bundler error.Test plan
reported_ruby_version,verify_reported_version!abort, hijack force-rebuild path, healthy fast path (red before the fix, green after)dev upproceeds past bundlerMade with Cursor