Geek Blog for Little Red String

Geek stuff, amature programming, Linux, Ruby, open source

Rails Deployment Without Capistrano

I’ve been looking for a way to easily deploy my rails application on Railsplayground and the last two times I tried using Capistrano I spent a whole day trying to get it to work with marginal success. After putting together some “big bang” recipe I was left troubleshooting undescriptive errors and possible, known bugs in Capistrano. I imagine that Capistrano is a great tool for some but for me it’s either overkill (my deployment is pretty simple) or I have some kind of mental block. So at risk of embracing a “not invented here” philosophy I set out to create a less magical, custom deployment script. After all, I know everything that has to be done since I’ve manually deployed the application several times.

I discovered that I’m not the only one that finds Capistrano a bit too much to handle for a simple deployment. The makers of Vlad decided to create an alternative to Capistrano that has fewer features but is easier to understand. The best part of Vlad is that the simplicity of the code makes it easy to understand what’s going on and therefore easy to build onto. A post by Kevin Clark showed me how Vlad could be used to create custom deployment tasks and further validated my feelings about Capistrano.

Here are the steps that I need to preform when deploying my application:


  1. Sync my local directory of symlinked (not under version control) stuff to a remote directory. This includes images, some javascript libraries and the vendor directory.
  2. Sync my source code (under version control) to a remote directory.
  3. Copy some environment specific files over (database.yml and environment.rb).

  4. Restart my mongrel server

?

Here We Go

Install Vlad on your local machine:

sudo gem install vlad

First we need to get Vlad to do it’s update task. This will create a release of my app on server on symlink it to ‘current’.

At the end of Rakefile

...
begin
  require 'rubygems'
  require 'vlad'
  Vlad.load(:scm => :git, :app => nil, :web => nil)
rescue LoadError
  raise "vlad didn't load!"
end

I’m using git for source control and although I am using mongrel, which Vlad has a recipe for, I’m not using a mongrel cluster which Vlad sets up by default. I don’t have any control over my Apache server with my shared hosting so I set :web => nil.

Create deploy.rb with

set :application, "appname"
set :domain, "username@server.com"
set :deploy_to, "/home/username/appname/"
set :repository, "/home/username/appname/git_repo"

Here I’m just filling out my specific server information. The folder “git_repo” probably doesn’t exist yet so create it, initialize the repository and make sure that we can push to it. Here is a guide that can help if you are unsure of how to do this.

Now it’s time for:

rake vlad:setup

This will setup some directories for you (from the Vlad source: deploy_to, releases_path, scm_path, shared_path).

Now we get to have some fun (coding is more fun that troubleshooting Capistrano) and make whatever tasks we deem necessary.

namespace :vlad do

  # I have a database file called database.yml.online that needs to be copied over 
  # database.yml.
  desc "copy the database.yml file over"
  remote_task :copy_prod_db_file do 
    run "cp -f #{release_path}/config/database.yml.online #{release_path}/config/database.yml"
  end

  # Same deal as with database.yml.
  desc "copy the environment.rb file over"
  remote_task :copy_prod_env_file do 
    run "cp -f #{release_path}/config/environment.rb.online #{release_path}/config/environment.rb"
  end


  # I'll let this task save me the trouble of pushing to my remote git repo manually.
  desc "push source to remote server"


  task :push_source do
    system "git push remote_server_name"
  end

  # I keep my shared folder, 'shared', (with images javascript libraries and vendor) in a 
  # folder separate from Vlad's shared folder in the app_path.  This may be stupid but it
  # makes just copying over my relative symlinks easy (../shared/some_asset).  Maybe
  # I'll combine this with Vlad's shared folder someday and have Vlad create the links.    
  desc "rsync the 'shared' folder on server with local 'shared' folder."
  task :sync_shared do
    system "rsync -az -e ssh  ../shared #{domain}:#{deploy_to}/shared "
  end
  

  desc "Start the app server"
  remote_task :start do
    run "echo 'running command: mongrel_rails start -e production -p 4054 -d'"
    run "cd #{current_path} && mongrel_rails start -e production -p 4054 -d"
  end

 
  desc "Restart the app server"
  remote_task :restart do    
    run "cd #{current_path} && mongrel_rails restart"
  end


  desc "Stop the app server"
  remote_task :stop do
    run "cd #{current_path} && mongrel_rails stop"
  end

  # Nothing's going to go wrong right?  Still this taks will help troubleshoot if 
  # something does go wrong.  
  desc "Grab tail from production log"
  remote_task :grab_log_tail do
    run "tail -n 100 #{current_path}/log/production.log"
  end

  
  # I use acts_as_ferret and I don't want to have to rebuild the index each time 
  # I deploy.
  desc "Link the current index folder to the shared index folder (ferret index)."
  remote_task :link_index do
    run "rm -r #{current_path}/index"
    run "ln -s #{shared_path}/index #{current_path}/index"
  end
  

  # Here's what to run when you're ready to deploy.  You can see each step in order.  
  task :deploy => [:sync_shared, :push_source, :stop, :update, 
                   :copy_prod_db_file, :copy_prod_env_file, :link_index, 
                   :start]
end

Obviously there is some refactoring that could be done (I’m looking at you, :copy_prod_db_file and :copy_prod_env_file) but since the code is so small I’m not going to worry about it. I would really recommend this approach to someone that is struggling with Capistrano and has a relatively simple deployment situation. We all love reusing code, but in this case it would take me longer to learn to use and to troubleshoot Capistrano1 than to write my own deployment script. Of course Vlad really helped get me started.

Footnotes

1 Actually I already did spend more time with Capistrano than I did setting up this deployment technique and writing this blog entry.]