Always uses system ruby with chruby
Summary
When using chruby for ruby version management, tasks and LSP commands always use the system ruby (e.g. /usr/bin/ruby)
Steps to trigger the problem:
- Install chruby (with homebrew in my case)
- Add chruby setup to zshrc
- Add a zed task to show which version is being used
- Run the task and note which version of ruby is being used
Example chruby zsh config (step 2)
prefix=$(/opt/homebrew/bin/brew --prefix)
source "${prefix}"/share/chruby/chruby.sh
source "${prefix}"/share/chruby/auto.sh
Example ruby version debug task (step 3)
{
"label": "debug ruby environment",
"command": "chruby; which ruby",
"use_new_terminal": false,
"reveal": "no_focus",
"hide": "never"
}
Actual Behavior:
The commands are being run before chruby has set the ruby version, so it is using the system ruby:
ruby-3.4.1
/usr/bin/ruby
⏵ Task `debug ruby environment` finished successfully
⏵ Command: /bin/zsh -i -c 'chruby; which ruby'
Expected Behavior:
I would expect chruby to have run and set the correct ruby version before any commands are run in the terminal, like a normal fresh terminal session:
~ % chruby
* ruby-3.4.1
~ % which ruby
/Users/tim/.rubies/ruby-3.4.1/bin/ruby
I suspect something about the way chruby hooks into terminal events is not getting a chance to run before Zed runs commands, but that's conjecture.
Notably, I can workaround this for tasks by dropping a hack into my zsh config to manually run chruby in a zed term:
if [ -n "${ZED_TERM}" ]; then
if [ -f .ruby-version ]; then
chruby $(cat .ruby-version)
elif [ -f "${HOME}/.ruby-version" ]; then
chruby $(cat "${HOME}/.ruby-version")
fi
fi
But that doesn't solve the issue for solargraph, ruby-lsp, rubocop, etc. which can be seen still trying to use the system ruby:
ruby-lsp log
Language server error: ruby-lsp
Unable to find the 'ruby-lsp' command.
-- stderr--
rubocop log
Language server error: rubocop
oneshot canceled
-- stderr--
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': Could not find 'bundler' (2.5.10) required by your /Users/tim/git/dv-rails/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.5.10`
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path'
from /usr/bin/bundle:23:in `<main>'
Zed Version and System Specs
Zed: v0.177.11 (Zed) OS: macOS 15.3.2 Memory: 64 GiB Architecture: aarch64
To expand on my conjecture above a little bit, chruby's auto.sh script uses zsh preexec_functions, which is what I suspect Zed is bypassing in the terminal session, but I'm not familiar enough with how that works to really dig into that much further
Here's what `auto.sh` looks like on my version
unset RUBY_AUTO_VERSION
function chruby_auto() {
local dir="$PWD/" version
until [[ -z "$dir" ]]; do
dir="${dir%/*}"
if { read -r version <"$dir/.ruby-version"; } 2>/dev/null || [[ -n "$version" ]]; then
if [[ "$version" == "$RUBY_AUTO_VERSION" ]]; then return
else
RUBY_AUTO_VERSION="$version"
chruby "$version"
return $?
fi
fi
done
if [[ -n "$RUBY_AUTO_VERSION" ]]; then
chruby_reset
unset RUBY_AUTO_VERSION
fi
}
if [[ -n "$ZSH_VERSION" ]]; then
if [[ ! "$preexec_functions" == *chruby_auto* ]]; then
preexec_functions+=("chruby_auto")
fi
elif [[ -n "$BASH_VERSION" ]]; then
trap '[[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto' DEBUG
fi
This is potentially a bug with chruby itself (this looks promising), but I'm bringing it up here because, again, I'm not super familiar with how this works, but it seems like there's a potential here for Zed to better support tools that do rely on these zsh hooks.
I'm curious what the behavior of this in a normal terminal:
/bin/zsh -i -c 'chruby; which ruby'
Notably, the -i is launching an interactive shell and so won't load .zprofile, .zlogin or /etc/zprofile -- I wonder if some of your init hooks are configured there (or in .profile which is loaded for login shells, but only if .zprofile and .zlogin are absent) instead of in .zshrc?
Re: https://github.com/postmodern/chruby/issues/465 I'm not super familiar with preexec_functions vs precmd_functions in ZSH so I'm not much help there.
Blind suggestion: try using bash as the shell for these tasks?
I've tried to use bash (instead of zsh) as a terminal and that was successful (well, that the builtin Terminal can load chruby and configure right ruby version automatically) with:
"terminal": {
"shell": {
"with_arguments": {
"program": "/bin/bash",
"args": ["--login"]
}
}
},
But I have to start zed from terminal (of course, since it inherits the PATH etc.). If I start it as an app (macOS) it will not use bash shell configured for the terminal to run any of the LSPs.
Even if I add env to that settings to for example configure SHELL to /bin/bash Zed still uses zsh to get env:
2025-05-10T21:28:49+02:00 INFO [util] set environment variables from shell:/bin/zsh, path:/usr/local/heroku/bin:/Users/mikz/.qlty/bin:/Users/mikz/Library/Preferences/netlify/helper/bin:/usr/local/opt/[email protected]/bin:/Users/mikz/perl5/bin:/opt/homebrew/bin:/opt/homebrew/sbin:./node_modules/.bin:./lua_modules/bin:/Users/mikz/.bin:/Users/mikz/.cargo/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/X11/bin:/Library/Apple/usr/bin:/usr/local/MacGPG2/bin:/Applications/Wireshark.app/Contents/MacOS:/Applications/VMware Fusion.app/Contents/Public:/usr/local/share/dotnet:~/.dotnet/tools:/Users/mikz/Developer/go/bin:/Library/RubyMotion/bin:/Users/mikz/.dotnet/tools:/Users/mikz/.lmstudio/bin:/Users/mikz/.local/bin:/Users/mikz/.lmstudio/bin
2025-05-10T21:28:49+02:00 INFO [project::environment] using project environment variables shell launched in "/Users/mikz/Developer/gema/kkm". PATH="/usr/local/heroku/bin:/Users/mikz/.qlty/bin:/Users/mikz/Library/Preferences/netlify/helper/bin:/usr/local/opt/[email protected]/bin:/Users/mikz/perl5/bin:/opt/homebrew/bin:/opt/homebrew/sbin:./node_modules/.bin:./lua_modules/bin:/Users/mikz/.bin:/Users/mikz/.cargo/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/X11/bin:/Library/Apple/usr/bin:/usr/local/MacGPG2/bin:/Applications/Wireshark.app/Contents/MacOS:/Applications/VMware Fusion.app/Contents/Public:/usr/local/share/dotnet:~/.dotnet/tools:/Users/mikz/Developer/go/bin:/Library/RubyMotion/bin:/Users/mikz/.dotnet/tools:/Users/mikz/.lmstudio/bin:/Users/mikz/.local/bin:/Users/mikz/.lmstudio/bin"
@notpeter to answer /bin/zsh -i -c 'chruby; which ruby' - it works as expected:
/bin/zsh -i -c 'chruby; which ruby'
ruby-3.1.4
ruby-3.2.2
ruby-3.3.0
ruby-3.3.6
* ruby-3.3.7
ruby-3.4.1
ruby-3.4.2
.rubies
/Users/mikz/.rubies/ruby-3.3.7/bin/ruby
I was able to start ruby-lsp with the correct ruby version autoselected by chruby with a small custom shim:
Put this somewhere in your path and chmod +x <filename>:
#!/usr/bin/env zsh
. /opt/homebrew/opt/chruby/share/chruby/chruby.sh
. /opt/homebrew/opt/chruby/share/chruby/auto.sh
eval ruby-lsp
Then you can point to it in the lsp config:
"lsp": {
"ruby-lsp": {
"binary": {
"path": "/Users/*/.dotfiles/bin/chruby_ruby-rsp",
"arguments": []
}
}
}
Hi! This issue has been fixed in v0.13.2. We can close this issue as resolved. Thanks!