availability: January 2017
Rails projects tend to suffer from the 'it works on my PC syndrome'. People seem to struggle with keeping their application environment under control so it can be reproduced on another system. One of the reasons is that rails and ruby evolve fast to new concepts and ideas. While this is clearly a good thing, it does require additional steps to keep a rails project under control. There exist a lot of documentation in books and blogs, but that also suffers from the same problem: the recipe working for version X of rails, might not work anymore in the new version.
This document will try to document how to control it, additionally listing older approaches and indicating that they have become deprecated.
NOTE:
export PROJ_NAME=projectX
export PROJ_PATH=pwd
/$PROJ_NAME
cd $PROJ_NAME
STEP 1: control your RUBY
The first thing to decide is the ruby version you will use. This is usually a step to people tend to skip, they will use whatever ruby that is in the path
Lets see where the ruby binary is installed
RUBY_BINARY=To fix the ruby version you want to use the environment variable RUBY_HOME If this variable is set, everything will be executed from this environment variablewhich ruby
echo $RUBY_BINARY RUBY_VERSION=ruby --version
echo $RUBY_VERSION
export RUBY_HOME='/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr' echo $RUBY_HOME export PATH=$RUBY_HOME/bin:$PATHLet's write this variable to a file so that we can set it again everytime we need it
echo "export RUBY_HOME=$RUBY_HOME" >> ENV.$PROJ_NAMESTEP 2: control your RAILS Just as we decided which version of ruby, we have to do the same with our rails version Again in most documents you will find a 'gem install rails' installing gems either in the system environment or the per user environment @REF: http://blog.divergentsoftware.com/2007/11/gemhome-is-where-partys-at.html @REF: https://we.riseup.net/rails/subversion-for-rails If you want to protect this project from other gem installs and gem updates we can dedicate a directory for this The path where gem install writes it's gems is in GEM_HOME
export GEM_HOME=$PROJ_PATH/gem mkdir $GEM_HOMEThe path where the are read is in GEM_PATH. We set this to be excluvely to the gems in this directory. So no use of other f.i. system gems
export GEM_PATH=$GEM_HOMELet's write this again to our ENV file
echo "export GEM_HOME=$GEM_HOME" >> ENV.$PROJ_NAME echo "export GEM_PATH=$GEM_PATH" >> ENV.$PROJ_NAMELet get the version we want
RAILS_VERSION="2.1" gem install rails -v $RAILS_VERSIONlet's check if the files are indeed installed in the right place
ls $GEMHOME/To check the path of the installed versions
gem list -dSTEP 3: let's setup a subversion to check our code in @REF: https://wincent.com/wiki/Checking_a_new_Rails_application_into_an_existing_Subversion_repository Chances are you have one allready but, this should show the basic setup of setting up yourself The place where you store your repository = REPO_PATH
REPO_PATH=$HOME/subversion-repository #REPO_URL="file://"$REPO_PATH/$PROJ_NAME REPO_URL="svn://localhost/"$PROJ_NAMEGo within the repository path
cd $REPO_PATHand create the svn repository for our project = $PROJ_NAME
svnadmin create ./$PROJ_NAMEAdd some userT with password passwordT
export SVN_USER="userT" export SVN_PASSWORD="passwordT" echo "$SVN_USER = $SVN_PASSWORD" >> $REPO_PATH/$PROJ_NAME/conf/passwd echo "auth-access = write" >> $REPO_PATH/$PROJ_NAME/conf/svnserve.conf echo "password-db = passwd" >> $REPO_PATH/$PROJ_NAME/conf/svnserve.conf echo "realm = Project $PROJ_NAME" >> $REPO_PATH/$PROJ_NAME/conf/svnserve.confStart the listener to listen on the network
svnserve -d -r $REPO_PATHThis command will create a trunk, tags, branches directory and commit the version of the project
svn --username=$SVN_USER --password=$SVN_PASSWORD mkdir --message="standard project start" "$REPO_URL/trunk" "$REPO_URL/branches" "$REPO_URL/tags"STEP 4: Create our first rails skeleton for the application Let's go to the our project directory we created in STEP 0
cd $PROJ_PATHRemember to set your RUBY_HOME, GEM_HOME, GEM_PATH We can use our project ENV to set things up again
. ENV.$PROJ_NAMEWithin our PROJ_PATH we will checkout the trunk to the rails directory Remember that REPO_URL includes PROJ_NAME (we assume that each project will have it's own trunk, tags, release)
svn checkout "$REPO_URL/trunk" rails cd railsSet the database type (DB_TYPE) we would like to use
DB_TYPE=mysql rails . -d $DB_TYPETake the generated and add it to the files to check in
svn add --force .BUT: we don't want everything to be checked in so we need to set a set of ignores @REF = Addison Wesley - The Rails Way, 2008 : page 811
svn propset svn:ignore -R ".DS_Store" . --forceWe don't want to have the log files to be checked in
svn revert log/ svn propset svn:ignore ".log" logWe don't want to have generated documentation to be checked in
svn propset svn:ignore "" doc@REF = http://www.sepcot.com/blog/2008/04/svn-rake-tasks-1 We don't want to have generated documentation to be checked in
svn propset svn:ignore "" doc/api svn propset svn:ignore "" doc/app@REF = Addison Wesley - The Rails Way, 2008 : page 811
svn propset svn:ignore "" tmp svn propset svn:ignore "" tmp/sessions tmp/cache tmp/sockets svn propset svn:ignore ".q" db/ --force svn propset svn:ignore "*.db" db/ --force
cp config/database.yml config/database.yml.template svn add config/database.yml.template svn revert config/database.yml svn propset svn:ignore "database.yml" configWe can set some scripts as executable so that they can be started using GUI tools This goes for all dispatchers
svn propset svn:executable "" public/dispatch.and for all scripts inside the script directory
svn propset svn:executable "" find script -type f | grep -v '.svn'
Make sure we set the correct native eol style
svn propset svn:eol-style native public/dispatch.@REF = Pragmatic Programmers - Deploying Rails Application : p 28
svn propset svn:ignore "engine_files" public@REF = http://blog.teksol.info/2006/03/09/subversion-primer-for-rails-projects We want to make sure that the default page is not shown, so that it never looks like a default installation
svn revert public/index.html@REF = http://blog.teksol.info/2006/03/09/subversion-primer-for-rails-projects
svn status svn commit -m "initial rails checkin with ignores"Step 5: Freezing rails To handle this, rails seem to have moved through different solutions Option 1 (DEPRECATED): define the vendor/rails directory in your project as an SVN External, but the rails repository moved to GIT and so the latest versions can't be used @REF = http://blog.teksol.info/2006/03/09/subversion-primer-for-rails-projects @REF = Wrox - Professional Ruby on Rails - page 55
#svn propset svn:externals "rails http://dev.rubyonrails.org/svn/rails/branches/ 2-2-stable" vendor #svn update vendor #svn commit -m "vendor integration"Option 2 (BACKWARDS COMPATIBLE): to solve this, there exist a gem called 'Piston' that allows you to handle git repositories and integrate them into a subversion repository @REF = http://blog.logeek.fr/2008/1/4/how-to-use-piston-to-ease-your-upgrades @REF = http://piston.rubyforge.org/
#rails . -fOption 4 (RECENT VERSIONS): rails now includes a rake task to freeze it into your project directory This will download the rails tar.gz from the edge site and we can tell to use a specific version using RELEASE= @REF = http://support.tigertech.net/freeze-rails @REF = http://codeclimber.blogspot.com/2008/09/freeze-those-rails.html
rake rails:freeze:edge RELEASE=$RAILS_VERSION svn add vendor/rails svn commit -m "frozen rails to version $RAILS_VERSION"Should we set RAILS_HOME after freezing? @REF = http://snippets.dzone.com/posts/show/4730
pwd
/vendor/rails/railties/bin:$PATHrake rails:freeze:gems svn add vendor/gems svn commit -m "frozen rails gems $RAILS_VERSION"Step 7: Freezing non-rails gems Option 1 (DEPRECATED): the first solution that appeared was to unpack of a gem in vendor , copy the files to the correct place and remove the unpack
# cd vendorOption 2 (CUSTOM): instead of copying the files, the gems are left unpacked in the vendor directory and the then the config.load_paths is adapted to include dynamically include the libs of all subdirs @REF = Oreilly - Advanced Rails - Large Projects p. 312gem unpack gemname
cp -Rf gemname-n.n.n/lib/* .
cp -Rf gemname-n.n.n/MIT-LICENSE LICENSE-gemname
cp -Rf gemname-n.n.n/README README-gemname
svn add aaaa bbbb cccc dddd.rb LICENSE-gemname README-gemname
svn propset version "n.n.n (Gem)" gemname.rb
rm -Rf gemname-n.n.n
cd ..
# cd vendorOption 3 (CUSTOM) : do the same unpack of the gem in the vendors directory but use the GEM_PATH to include the paths where the gems are unpacked @REF = http://errtheblog.com/posts/50-vendor-everythingmkdir gems
cd gems
gem unpack gemname
cat ../../config/environment.rb |\
sed -e 's@# config.load_paths += %W( #{RAILS_ROOT}/extras )@config.load_paths + = Dir["#{RAILS_ROOT}/vendor/gems/**"].map do |dir|\n\tFile.directory?(lib = "#{dir}/lib") ? lib : dir\nend@' > ../../config/environment.rb
cd ..
cd ..
# cd vendorOption 4 (BACKWARDS COMPATIBLE) : gemsonrails @REF = http://gemsonrails.rubyforge.org/mkdir gems
cd gems
gem unpack gemname
cd ..
cd ..
echo "ENV['GEM_PATH'] = File.join(RAILS_ROOT, 'vendor', 'gems')" >> config/boot.rb
export GEM_NAME="hpricot" export GEM_VERSION="0.6.0" cat config/environment.rb | sed -e 's@# config.gem "bj"@config.gem "$GEM_NAME", :lib => "$GEM_NAME", :version => "$GEM_VERSION"@' > config/environment.rb.$$ mv config/environment.rb.$$ config/environment.rbTo use the rake task unpack, we first need to install the gem then we can execute the gem installation. Note: because we still have the GEMS_PATH and GEM_HOME set, the install will be specific to our project
rake gems:installNow that it is installed we can unpack the gems, this will go in the vendor/gems directory
rake gems:unpack:dependencies svn add vendor/gems svn commit -m "frozen additional gem $GEM_NAME to version $GEM_VERSION"To list the gems state there is a rake task (this only seems to work ok in Rails version 2.3.1 (otherwise you seem to get random nil.dependencies errors)
rake gemsOption 6: warbler
rake gems:buildOption 2: puts the binaries in a place where you want to
mkdir native-lib ARCHDIR=Next we search these directories for extconf.rb Then execute ruby extconf.rb , which will create a makefile, we do a make Then we install this to the native-lib directory we created, otherwise it would install itself in the system ruby directorypwd
/native-lib/ruby -e "puts RUBY_PLATFORM"
export ARCHDIR
find vendor/gems/ -name "extconf.rb" | xargs dirname | xargs -I {} ksh 'cd {} ; ruby extconf.rb; make; make RUBYARCHDIR=$ARCHDIR install'We add the native-lib directory to the load_paths in config.environment so that these can be found when executing
cat config/environment.rb | sed -e 's@# config.load_paths += %W( #{RAILS_ROOT}/extras )@ config.load_paths += %W( #{RAILS_ROOT}/native-lib/#{RUBY_PLATFORM} )@' > config/environment.rb.$$ mv config/environment.rb.$$ config/environment.rbNow you can argue that we file is generated , so we might not need to check in But on some systems there isn't a compiler available (on Windows, no standard C compiler is installed f.i. , the same goes for stripped production installations)
svn add $ARCHDIR svn commit -m "commited native libs"Step 9: Checking it all out on another system
namespace :svn dothen you can repeat this tasks with:enddesc "Sets the correct ignores and other stuff for a correct checkin of rails project in an svn repository" task :propset do system 'svn propset svn:ignore -R ".DS_Store" . --force' system 'svn propset svn:ignore "*.log" log/' system 'svn propset svn:ignore "*.db" db/' system 'svn propset svn:ignore "*.sqlite3" db/' system 'svn propset svn:ignore "database.yml" config/' system 'svn propset svn:ignore "*" doc/' system 'svn propset svn:ignore "*" doc/api' system 'svn propset svn:ignore "*" doc/app' system 'svn propset svn:ignore "*" tmp/' system 'svn propset svn:ignore "*" tmp/sessions tmp/cache tmp/sockets' system 'svn propset svn:executable "*" public/dispatch.*' system 'svn propset svn:eol-style native public/dispatch.*' system 'svn propset svn:ignore "engine_files" public/ ' system 'svn propset svn:ignore index.html public/' end
$ rake -T |grep svn rake svn:propset # Sets the correct ignores and other stuff for a correct checkin of rails project in an svn repositoryStep 10: Do you first scaffold. If you supply the -svn , the files generated will automatically be add for the next commit
./script/generate scaffold -svn svn commit -m "first scaffold"