Rails From Thin Air on RHEL 5

I’m completing a draconian exercise which is teaching me many lessons about installation and configuration whether I like it or not. The best I can do, as I struggle along, is to write about my experience. I certainly don’t want to have to do this again from scratch, so after I complete each step I’m adding it to a script.

The scene: I work for a very big company. Like many big companies, there are many rules in place that govern access to particular machines. I am starting a brand new Rails project and have had to overcome a number of obstacles to ready my team for deployment. Here are the bullet points:

  • A cluster of Red Hat Enterprise Linux Servers, release 5.6 (Tikanga)
  • Bash
  • wget
  • No root access
  • No ability to use sudo, ever

The requirement, a working Ruby and Rails project, has a handful of dependencies: libxml2, libxslt, bz2, SQLite3 version >= 3.6, Python version >= 2.6. None of these are installed with RHEL 5 out of the box, so I’m going to build all of them.

Setting up in vendor/
First I’ll create a directory underneath my vendor/ directory in RAILS_HOME because ”vendor everything still applies.” I call it setup/. Here is where I will put everything my server needs to run my application. I’ll have my installation script and src/ and local/ directories.

joe@warpaint:~/dev/tddominate/vendor$ ls
setup  setup_env.bat  src

Next it’s time to create a bash script. I set it to exit on any command failure with set -e and create some variables, beginning with SETUP_DIR. SETUP_DIR is set to be the directory where I’ve written my script, regardless of the location from which the script is executed (thanks to StackExchange for help with this):

set -e
SETUP_DIR=”$( cd “$( dirname “${BASH_SOURCE[0]}” )” && pwd )”

Ruby and Rubygems
Ruby and Rubygems are easy installs for me despite my inability to use RVM on these machines (permission issue).

tar xzvf ruby-1.9.3-p327.tar.gz
cd ruby-1.9.3-p327
./configure –prefix=/my/location/ruby 15 make
make install

echo “export PATH=/my/location/ruby/bin:$PATH” >> ~/.bashrc
echo “export HTTP_PROXY=http://proxy.net:8080” >> ~/.bashrc
echo “export HTTPS_PROXY=https://proxy.net:8080” >> ~/.bashrc
. ~/.bashrc

tar xvf rubygems-1.8.24.tar
cd rubygems-1.8.24
ruby setup.rb –prefix=/my/location

gem install bundler

I set the HTTP_PROXY and HTTPS_PROXY environment variables so that rubygems can do its thing.

Prepare for Nokogiri (building libxml2 and libxslt from source)
OK, enough of the easy stuff. Enter libxml and libxslt. With no rpm and no sudo, I’m going to have to get my hands dirty:

wget ftp://xmlsoft.org/libxml2/libxml2-2.7.8.tar.gz
tar xzvf libxml2-2.7.8.tar.gz
cd $SETUP_DIR_SRC/libxml2-2.7.8
./configure –prefix=$SETUP_DIR_LOCAL ; make ; make install

wget ftp://xmlsoft.org/libxml2/libxslt-1.1.28.tar.gz
tar xzvf libxslt-1.1.28.tar.gz
cd $SETUP_DIR_SRC/libxslt-1.1.28
./configure –prefix=$SETUP_DIR_LOCAL \ 
    –with-libxml-prefix=$SETUP_DIR_LOCAL \ 
    –with-libxml-include-prefix=$SETUP_DIR_LOCAL/include \ 
make ; make install

If you prayed to the right Linux gods, this will work for you with a little tinkering. Next I’ll add the right configuration to bundler for install time. Note that the libxml2 specified is the source directory and the libxslt specified is the installation directory.

bundle config build.nokogiri –with-xml2-dir=$SETUP_DIR_SRC/libxml2-2.7.8 –with-xslt-dir=$SETUP_DIR_LOCAL

Runtime for ExecJS
Now you’re all set to bundle install. Your gems install flawlessly. You’re tempted to celebrate. Then you run rspec. Or rails s. Or rake. And you still have problems. Something in the way of:

Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes.

I still have work to do.

Sam Stephenson and Josh Peek created something awesome with ExecJS. They are the ones that make executing Javascript through Ruby possible. Under the hood, in almost everything we do in Rails applications these days, ExecJS is there. It’s a workhorse akin to Nokogiri. There’s just one thing they ask of you to get started - that you actually have a Javascript runtime! Your Gemfile is already giving you a hint:

# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem ‘therubyracer’

As noted, there are other runtime options, but in order to build the dependencies – in this case libv8 – I need to upgrade my version of Python to 2.7. On a debian system, Python does not come with bz2 support out of the box. It has to be configured to do this. Red Hat 5 does not come with the bz2 developer library, just the binaries. So I’ll download and build it. 

There is a specific way to build bz2 so that it shares its libraries with others (like Python) and trying to figure it out nearly drained my remaining life force. I have to make and install bz2 twice, strange though it may seem. This is so it knows to build a shared library that on which Python depends.  Since I am building bz2 and installing it in a custom location (using the PREFIX option), I  then need to tell Python where it’s been installed. Borrowing heavily from a script and blog post by @rajaleen, here’s what I did:

I export my PREFIX as an environment variable:

export PREFIX=/path/to/custom_install

Make and install bz2 twice

make -f Makefile_libbz2_so
make install PREFIX=$PREFIX
make install PREFIX=$PREFIX

I set the necessary environment variables for Python compilation:

export C_INCLUDE_PATH=/path/to/custom_install/include
export LIBRARY_PATH=/path/to/custom_install/lib

Finally, I use the –enable-shared option when configuring Python. This tells Python to build both its static and dynamic libraries:

cd Python-2.7.3
./configure –prefix=$PREFIX –enable-shared
make install

Now our gem install libv8 works flawlessly and I  uncomment gem ‘therubyracer’ from our Gemfile and add a requirement for libv8:

gem ‘libv8’ 
gem ‘therubyracer’ 

SQLite3 is laughably outdated
bundle install works flawlessly once again, and when I run rspec, the specs run! But they are failing for strange reasons:

Failure/Error: environment = Environment.make! ActiveRecord::StatementInvalid: SQLite3::SQLException: near “SAVEPOINT”: syntax error: SAVEPOINT active_record_1

From the SQLite3 documentation:  “SAVEPOINTs are a method of creating transactions, similar to BEGIN and COMMIT, except that the SAVEPOINT and RELEASE commands are named and may be nested.” SAVEPOINTs have been supported since version 3.6.8. However, my RHEL 5 machine (and I believe all others as well) comes bundled with version 3.3.6, which was released in June of 2006. Time to upgrade.

Actually, this was part of the process was pleasantly easy with only one catch. I’m going to configure and install SQLite3 with our –prefix the same as always:

wget http://www.sqlite.org/sqlite-autoconf-3071501.tar.gz
tar xzvf sqlite-autoconf-3071501.tar.gz
cd sqlite-autoconf-3071501
./configure –prefix=$PREFIX
make install

When I run rspec, I still see the same errors. This is because the sqlite3 gem needs to be reconfigured. My method: uninstall/reinstall, and tell bundler about my configurations:

bundle exec gem uninstall sqlite3
bundle config build.sqlite3 –with-sqlite3-dir=$PREFIX
bundle install

One potential roadblock that may occur is the following when you try to run your specs:

sqlite3-ruby-1.3.1/lib/sqlite3/sqlite3_native.so: undefined symbol: 

The solution comes courtesy of Aaron Patterson. To make the sqlite3 gem play nicely, I  need to add my PREFIX/lib directory to the LD_LIBRARY_PATH:


Finally, I’m at the finish line. I run rspec:


Finished in 1.02 seconds
31 examples, 0 failures, 0 pending

Ahh, that’s the stuff! Happy coding!