Making chruby and binstubs play nice
Like a gentleman I use chruby and Bundler to manage Ruby versions and gems in my projects.
Instead of typing bundle exec
to run gem executables within a project, I prefer saving keystrokes and using an executable’s name on its own1. I also want to avoid installing another tool like Gem home.
So off to binstubs land I go. Bundler generates them for you and these days Rails even ships with a few as standard. These stub files live in your project and ensure the right set of gems for your project are loaded when they’re executed.
Security risks aside I could just prepend my path with ./bin:
and walk away—except that chruby auto-switching spoils the party. When I enter a project directory with a .ruby-version
file, chruby prepends the current Ruby version paths at the beginning of PATH thereby matching before my previously prepended ./bin:
.
Chruby recommends using rubygems-bundler but I don’t want to install another gem to get this to work. So I tweaked my zsh setup to use preexec_functions
like chruby to patch my PATH. I add my function to preexec_functions
after chruby loads so that my code patches the PATH after chruby does its work.
As for security I use the same scheme as Tim Pope. Add a git alias for marking a git repository as trusted and then only add a project’s bin directory to PATH if it is marked as such.
Now I just mark a repo as trusted via git trust
, and its local binstubs are automatically added to my path.
Changes in my .zshenv:
# Remove the need for bundle exec ... or ./bin/...
# by adding ./bin to path if the current project is trusted
function set_local_bin_path() {
# Replace any existing local bin paths with our new one
export PATH="${1:-""}`echo "$PATH"|sed -e 's,[^:]*\.git/[^:]*bin:,,g'`"
}
function add_trusted_local_bin_to_path() {
if [[ -d "$PWD/.git/safe" ]]; then
# We're in a trusted project directory so update our local bin path
set_local_bin_path "$PWD/.git/safe/../../bin:"
fi
}
# Make sure add_trusted_local_bin_to_path runs after chruby so we
# prepend the default chruby gem paths
if [[ -n "$ZSH_VERSION" ]]; then
if [[ ! "$preexec_functions" == *add_trusted_local_bin_to_path* ]]; then
preexec_functions+=("add_trusted_local_bin_to_path")
fi
fi
The git trust alias from my .gitconfig
:
[alias]
# Mark a repo as trusted
trust = "!mkdir -p .git/safe"
-
Even though I’ve aliased
bundle exec
tobe
in my shell I still feel like an animal when I have to type it. ↩︎