Installing Libraries (Like RBENV) From Source
In my walk-through of the RBENV codebase, we do a lot of experiments which involve modifying the code and then running it. The goal is sometimes to break the code intentionally for educational purposes, and other times just to log what's happening as it happens.
If you'd like to follow along with those, the first step is to install RBENV on your machine.
Even if you've already installed RBENV, you'll want to read this post, since there's a good chance you previously installed it using Homebrew or another package manager, and that won't work for our purposes. When installed via Homebrew, we don't have access to RBENV's git repository, and we'll need that in order to roll back to a common version.
If you'd rather not do this, you can still follow along with the RBENV code via its Github repo, or by cloning said repo to your machine. You'll still be able to perform any experiment we do in this guide which isn't RBENV-specific.
I'm writing this tutorial on a Macbook running macOS, and I'll be using macOS-specific installation instructions and commands throughout.
Ensuring you have no other version managers installed
Admittedly, the best candidates for reading this guide are people who already use RBENV as their Ruby version manager. As you'll soon see, users of other version managers (such as rvm, asdf, chruby, etc.) will face more and bigger hurdles than users of RBENV (more on why in a minute).
With that in mind, we'll need to make sure you don't have a different Ruby version manager installed, such as RVM. If you do, that will represent a blocker to your continuing this guide, since you'll have different version managers competing to manage your Ruby version. This could introduce unexpected behavior and negatively impact your usage of Ruby.
We can check for the most popular Ruby version managers by using the which
command:
$ which asdf
asdf not found
$ which rvm
rvm not found
$ which chruby
chruby not found
$
If you see anything other than "not found" for which asdf
, which rvm
, and which chruby
, you likely have another version manager on your machine. In that case, you'll need to make a decision about which version manager you want to use. RBENV's Github page has a guide on the pros and cons of the various version managers out there.
If you don't have other version managers on your machine, feel free to move on to the next section.
Making sure RBENV is installed correctly
Installing RBENV via brew install rbenv
would be a perfectly fine option for a normal user. But it won't work for our purposes, because it would leave us without access to RBENV's .git
directory, and therefore its git history. That means we couldn't roll back to the specific version of RBENV that I'll be using for this walk-through. It's important that we work off the same codebase, so let's try another technique- installing from source.
To do this, we'll follow the instructions on this version of the RBENV Readme file. First, open your terminal and check whether you already have a directory called ~/.rbenv
, by running ls -la ~/.rbenv
:
$ ls -la ~/.rbenv
total 80
drwxr-xr-x 18 richiethomas staff 576 Jan 9 14:11 .
drwxr-x---+ 36 richiethomas staff 1152 Jan 10 12:23 ..
drwxr-xr-x 13 richiethomas staff 416 Jan 9 14:11 .git
drwxr-xr-x 3 richiethomas staff 96 Jan 9 14:11 .github
-rw-r--r-- 1 richiethomas staff 97 Jan 9 14:10 .gitignore
-rw-r--r-- 1 richiethomas staff 35 Jan 9 14:10 .vimrc
-rw-r--r-- 1 richiethomas staff 3390 Jan 9 14:10 CODE_OF_CONDUCT.md
-rw-r--r-- 1 richiethomas staff 1058 Jan 9 14:10 LICENSE
-rw-r--r-- 1 richiethomas staff 18411 Jan 9 14:11 README.md
drwxr-xr-x 3 richiethomas staff 96 Jan 9 14:10 bin
drwxr-xr-x 4 richiethomas staff 128 Jan 9 14:11 completions
drwxr-xr-x 27 richiethomas staff 864 Jan 9 14:11 libexec
drwxr-xr-x 3 richiethomas staff 96 Dec 23 11:37 rbenv.d
drwxr-xr-x 39 richiethomas staff 1248 Jan 9 14:35 shims
drwxr-xr-x 7 richiethomas staff 224 Jan 9 14:11 src
drwxr-xr-x 28 richiethomas staff 896 Jan 9 14:11 test
-rw-r--r-- 1 richiethomas staff 6 Dec 23 11:38 version
drwxr-xr-x 5 richiethomas staff 160 Dec 23 11:38 versions
$
If this directory does exist, and if the command output includes a .git/
directory in it like it does above, you should be good to go.
Another possibility is that the output exists but looks like this:
$ ls -la ~/.rbenv
total 8
drwxr-xr-x 5 richiethomas staff 160 Jan 10 12:56 .
drwxr-x---+ 37 richiethomas staff 1184 Jan 10 12:56 ..
drwxr-xr-x 15 richiethomas staff 480 Jan 10 12:56 shims
-rw-r--r-- 1 richiethomas staff 6 Jan 10 12:57 version
drwxr-xr-x 3 richiethomas staff 96 Jan 10 12:48 versions
$
Specifically, you run the ls -la
command and you don't see a .git
folder included in the output.
That likely means you previously installed RBENV using a package manager like Homebrew. If this is the case, one option is to simply rename your /.rbenv
directory as /.rbenv-old
or something similar:
$ mv ~/.rbenv/ ~/.rbenv-old
After the old RBENV folder is renamed, we can install RBENV again via source, by following the instructions on this version of the RBENV Readme file. Run the following command:
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
Cloning into '/Users/richiethomas/.rbenv'...
remote: Enumerating objects: 3270, done.
remote: Counting objects: 100% (420/420), done.
remote: Compressing objects: 100% (218/218), done.
remote: Total 3270 (delta 234), reused 327 (delta 188), pack-reused 2850
Receiving objects: 100% (3270/3270), 662.28 KiB | 258.00 KiB/s, done.
Resolving deltas: 100% (2024/2024), done.
$
Once this is done, you should have a fresh installation of RBENV inside ~/.rbenv
, which includes a .git
directory:
$ ls -la ~/.rbenv
total 80
drwxr-xr-x 18 richiethomas staff 576 Jan 10 13:03 .
drwxr-x---+ 37 richiethomas staff 1184 Jan 10 13:03 ..
drwxr-xr-x 12 richiethomas staff 384 Jan 10 13:03 .git
-rw-r--r-- 1 richiethomas staff 47 Jan 10 13:03 .gitattributes
drwxr-xr-x 4 richiethomas staff 128 Jan 10 13:03 .github
-rw-r--r-- 1 richiethomas staff 97 Jan 10 13:03 .gitignore
-rw-r--r-- 1 richiethomas staff 35 Jan 10 13:03 .vimrc
-rw-r--r-- 1 richiethomas staff 3390 Jan 10 13:03 CODE_OF_CONDUCT.md
-rw-r--r-- 1 richiethomas staff 1058 Jan 10 13:03 LICENSE
-rw-r--r-- 1 richiethomas staff 163 Jan 10 13:03 Makefile
-rw-r--r-- 1 richiethomas staff 12636 Jan 10 13:03 README.md
drwxr-xr-x 3 richiethomas staff 96 Jan 10 13:03 bin
drwxr-xr-x 4 richiethomas staff 128 Jan 10 13:03 completions
drwxr-xr-x 27 richiethomas staff 864 Jan 10 13:03 libexec
drwxr-xr-x 3 richiethomas staff 96 Jan 10 13:03 rbenv.d
drwxr-xr-x 3 richiethomas staff 96 Jan 10 13:03 share
drwxr-xr-x 4 richiethomas staff 128 Jan 10 13:03 src
drwxr-xr-x 28 richiethomas staff 896 Jan 10 13:03 test
$
Ensuring your new RBENV install has your old data
Next, we'll copy your currently-installed Ruby versions, gems, etc. to this new installation, ensuring that you can continue using RBENV as you did before:
$ cp -r ~/.rbenv-old/versions \
> ~/.rbenv-old/version \
> ~/.rbenv-old/shims \
> ~/.rbenv-old/rbenv.d \
> ~/.rbenv-old/completions \
> ~/.rbenv
$
Now, the version of RBENV which you installed from source should have any and all gems, installed Ruby versions, selected Ruby version, completions, and hooks that you may have previously installed in your old RBENV version.
If you ever encounter version-related problems with Ruby or RBENV on your machine from now on, you can simply delete the version of RBENV that we just installed, and rename your ~/.rbenv-old
directory back to ~/.rbenv
, and you should be good-to-go.
Rolling back to the correct git commit
Next, let's navigate into this directory via cd ~/.rbenv
.
We want to make sure that we're all looking at the same code, which means looking at the same git commit. So we'll create a new branch, separate from your master
or main
branch, and point that branch to a specific commit, i.e. the one that I used when I started writing this guide (that commit SHA is c4395e58201966d9f90c12bd6b7342e389e7a4cb
).
I called my new branch impostorsguides
, but you can call it whatever is easiest for you to remember:
$ git checkout -b impostorsguides
Switched to a new branch 'impostorsguides'
~/.rbenv (impostorsguides) $ git reset --hard c4395e58201966d9f90c12bd6b7342e389e7a4cb
HEAD is now at c4395e5 Merge pull request #1418 from uraitakahito/patch-0
$
For future reference, the Github link to this specific version of the RBENV codebase can be found here.
Enabling RBENV's shell function
We're getting close to the end, but we still have 2 more steps in the installation instructions. Next we have to add some text to our shell's startup script. This script will create a shell function called rbenv
, which (for our purposes) will do the same job as if we were running the command from a file.
The RBENV Readme file tells you how to add the text to your script. The command you'll copy/paste into your terminal depends on which shell program (bash, zsh, etc.) you're running:
2. Configure your shell to load rbenv: For bash: Ubuntu Desktop users should configure~/.bashrc
:On other platforms, bash is usually configured via ~/.bash_profile:echo 'eval "$(~/.rbenv/bin/rbenv init - bash)"' >> ~/.bashrc
For Zsh:echo 'eval "$(~/.rbenv/bin/rbenv init - bash)"' >> ~/.bash_profile
For Fish shell:echo 'eval "$(~/.rbenv/bin/rbenv init - zsh)"' >> ~/.zshrc
echo 'status --is-interactive; and ~/.rbenv/bin/rbenv init - fish | source' >> ~/.config/fish/config.fish
To find out which shell you're running, run the following command:
$ echo $SHELL
In my case, since I'm on a new Mac, my default terminal is Zsh:
$ echo $SHELL
/bin/zsh
$
Therefore, I'd paste the following into my terminal:
$ echo 'eval "$(~/.rbenv/bin/rbenv init - zsh)"' >> ~/.zshrc
This will add the text eval "$(~/.rbenv/bin/rbenv init - zsh)"
into a file called ~/.zshrc
, which is run every time I open a new terminal tab. If your terminal is different from mine (for example, bash
), the filename will be different from ~/.zshrc
(for example, ~/.bashrc
), but the gist of what's happening is still the same.
Lastly, in order for these changes to take effect, I'll need to do exactly that- open a new terminal tab. When I do so, and I run which rbenv
, I see the following shell command definition:
$ which rbenv
rbenv () {
local command
command="${1:-}"
if [ "$#" -gt 0 ]
then
shift
fi
case "$command" in
(rehash | shell) eval "$(rbenv "sh-$command" "$@")" ;;
(*) command rbenv "$command" "$@" ;;
esac
}
$
If we see the above output, we know RBENV has been successfully installed.
Generating an RBENV shim
The last thing we should do is make sure our Ruby version and its associated gems will be managed by RBENV (not by the system). To do this, we'll attempt to install a Ruby version, followed by a Ruby gem
Run brew install ruby-build
to install the rbenv install
command. This is the program which enables RBENV to install new versions of Ruby.
==> Downloading https://ghcr.io/v2/homebrew/core/ruby-build/manifests/20240119
##################################################################################################################################################### 100.0%
==> Fetching ruby-build
==> Downloading https://ghcr.io/v2/homebrew/core/ruby-build/blobs/sha256:61f4463a727fd0e6434db47da818bc3184c0788cba906e1083743329aa288641
##################################################################################################################################################### 100.0%
==> Pouring ruby-build--20240119.all.bottle.tar.gz
🍺 /opt/homebrew/Cellar/ruby-build/20240119: 599 files, 316.5KB
==> Running `brew cleanup ruby-build`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
Removing: /Users/richiethomas/Library/Caches/Homebrew/ruby-build--20231225... (65.2KB)
When that's done, run rbenv install 3.2.2
(or some other current version of Ruby). This may take a few minutes to complete. When that's done, run rbenv version 3.2.2
(or the version # you just installed) to ensure that this version is the one you're currently using.
You should see something like the following as output:
...
==> Downloading ruby-3.2.2.tar.gz...
-> curl -q -fL -o ruby-3.2.2.tar.gz https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.2.2.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 19.1M 100 19.1M 0 0 8803k 0 0:00:02 0:00:02 --:--:-- 8811k
==> Installing ruby-3.2.2...
ruby-build: using readline from homebrew
ruby-build: using libyaml from homebrew
-> ./configure "--prefix=$HOME/.rbenv/versions/3.2.2" "--with-openssl-dir=$HOME/.rbenv/versions/3.2.2/openssl" --enable-shared --with-readline-dir=/opt/homebrew/opt/readline --with-libyaml-dir=/opt/homebrew/opt/libyaml --with-ext=openssl,psych,+
-> make -j 14
-> make install
==> Installed ruby-3.2.2 to /Users/richiethomas/.rbenv/versions/3.2.2
Next, run rbenv global 3.2.2
to tell RBENV to use your newly-installed Ruby version as the default global version (instead of using the version which came pre-installed on your machine):
$ rbenv global 3.2.2
$ rbenv global
3.2.2
Next, run gem install bundler
to make sure that this version of Ruby has the Bundler gem installed.
Lastly, run which bundle
to make sure that the directory that appears is ~/.rbenv/shims/bundle
. You should see something like:
$ which bundle
/Users/richiethomas/.rbenv/shims/bundle
$
If you see this, that means the bundle command corresponds to a shim in your RBENV directory, and you are good to go!
If, instead, you see something like...
$ which bundle
/usr/bin/bundle
$
...this means your Bundler gem is still controlled by your system, not by RBENV. If that's the case, verify that the ~/.rbenv/shims
folder comes before /usr/bin/
in your $PATH
variable:
$ echo -e ${PATH//:/\\n}
/Users/richiethomas/.rbenv/bin
/Users/richiethomas/.rbenv/shims
/usr/local/lib/ruby/gems/2.6.0/bin
/opt/homebrew/bin
/opt/homebrew/sbin
/usr/local/bin
/System/Cryptexes/App/usr/bin
/usr/bin
/bin
/usr/sbin
/sbin
...
If it doesn't, you likely need to add the following to your ~/.bashrc
file (if you're using Bash as your terminal):
eval "$(~/.rbenv/bin/rbenv init - bash)"
PATH="/Users/richiethomas/.rbenv/bin:$PATH"
Or if you're using Zsh (the default shell on MacOS), you'll add the following to your ~/.zshrc
file:
eval "$(~/.rbenv/bin/rbenv init - zsh)"
PATH="/Users/richiethomas/.rbenv/bin:$PATH"
Then open a new terminal tab and re-run which bundle. If you still see /usr/bin/bundle
, check the file permissions of both the ~/.rbenv/shims
folder and the shims within this folder, by running:
$ ls -la ~/.rbenv
total 80
drwxr-xr-x 18 root staff 576 Jan 20 10:50 .
drwxr-x---+ 41 root staff 1312 Jan 20 11:42 ..
...
drwxr-xr-x 39 root staff 1248 Jan 20 11:16 shims
...
$ ls -la ~/.rbenv/shims
total 296
drwxr-xr-x 39 root staff 1248 Jan 20 11:16 .
drwxr-xr-x 18 root staff 576 Jan 20 10:50 ..
-rwxr-xr-x 1 root staff 409 Jan 20 11:03 bootsnap
-rwxr-xr-x 1 root staff 409 Jan 20 11:03 bundle
-rwxr-xr-x 1 root staff 409 Jan 20 11:03 bundler
...
You should see your UNIX username to the left of staff
, not root
(as above). If you do in fact see root
next to staff
, then it's possible your directory and file permissions are preventing the shim from being executable. In which case, verify that you cloned the RBENV repository via the git clone
command, not sudo git clone
.
After running this last step, I opened a new terminal tab and re-ran which bundle, and I finally saw my expected output: /Users/richiethomas/.rbenv/shims/bundle
.
Summary
Now that we've installed RBENV and are pointing to the right version, you'll be able to replicate any experiments that we run on the RBENV code itself, such as adding log statements to the code so we can see what happens during RBENV's execution.