The Dev Blog

Putting Family Management on Rails!

Deploying to Staging and Production with Capistrano

Posted by Guy Naor Thu, 28 Sep 2006 17:07:30 GMT

After an application is released, deployment should be in stages. To a test server, a staging server and a production one. I hope you have a staging server...

But capistrano has no knowledge of the difference between a staging and a production server. To make it easy to deploy using capistrano, a small change is needed to deploy.rb.

We add an if statement based off of an environment variable, and with it select the servers to deploy to. I also use this same variable to protect my production servers from accidental data changes, by adding an if to all data altering recipies I use only in staging (like reloading the DB and such). This way there's no way to run those commands on the production server.

Now, to deploy on staging: env DEPLOY='STAGING' cap deploy

And on production: env DEPLOY='PRODUCTION' cap deploy

Here's a small snippet of code with the change:

if ENV['DEPLOY'] == 'PRODUCTION'
   puts "*** Deploying to the \033[1;41m  PRODUCTION  \033[0m servers!"
   role :web, "www.your.domain.com"
   role :app, "app.your.domain.com
   role :db,  "db.your.domain.com", :primary => true
else
   puts "*** Deploying to the \033[1;42m  STAGING  \033[0m server!"
   role :web, "www.staging.domain.com"
   role :app, "app.staging.domain.com
   role :db,  "db.staging.domain.com", :primary => true
end

Posted in  | 4 comments | no trackbacks

del.icio.us:Deploying to Staging and Production with Capistrano digg:Deploying to Staging and Production with Capistrano spurl:Deploying to Staging and Production with Capistrano wists:Deploying to Staging and Production with Capistrano simpy:Deploying to Staging and Production with Capistrano newsvine:Deploying to Staging and Production with Capistrano blinklist:Deploying to Staging and Production with Capistrano furl:Deploying to Staging and Production with Capistrano reddit:Deploying to Staging and Production with Capistrano fark:Deploying to Staging and Production with Capistrano blogmarks:Deploying to Staging and Production with Capistrano Y!:Deploying to Staging and Production with Capistrano smarking:Deploying to Staging and Production with Capistrano magnolia:Deploying to Staging and Production with Capistrano segnalo:Deploying to Staging and Production with Capistrano

Comments

  1. Maxim Kulkin said about 12 hours later:

    Nice post.

    I used similar technique, but I had two capistrano recipes: for production and for staging. All tasks were contained in production deployment recipe and staging recipe just "required" production one and changed some variables (deployto, railsenv, etc.). This way deploying to staging or production is a matter of selecting a deployment recipe file, not some command line argument (which I think is easy to use).

    I had a small application so both production and staging were hosted on the same server. So I needed different databases. Thank to Rails, adding new database config is trivial:

    == config/database.yml

    staging:
      host: myhost.example.com
      database: myapp_staging
      ...
    

    Also, to be formal - a separate environment setup file in config/environments/.

    The interesting part of the setup is managing code repositories. I used Darcs, but I think the same technique can be applied to Subversion as well. E.g. you are working on adding some new feature and want to give your client ability to test it and get some feedback from him. So you deploy your latest source code to staging server. But then you receive a bug report for production site and need to fix it ASAP. You make the changes and need to push it to both staging and production sites. But here is the problem: you don't want to push unfinished features (that were previously pushed to staging) to production.

    I solved this problem with having 3 repositories: main repository (were all code is stored), staging repository (where to you can push features for staging from main repository) and production repository (to where you can push tested and confirmed features FROM STAGING REPOSITORY). The need for separate repositories is because of Darcs don't have branches: branching is just making new repositories. I think, in Subversion you can have one repository with branches for staging and production.

  2. Guy Naor said about 17 hours later:

    The different recipies is a nice idea, but then you need to manage 2 sets of recipies. So it's not completely DRY. And I have many non-standard recipies, as I manage everything with capistrano.

    As for the different repositories, every time I deploy anything to production, I create a tag on my SVN repository, and when needed I use this tagged release to update the production server. In svn tags are like branches. You can modify them.

    I am using a modified update_code recipe (my servers don't have access to my SVN repository, and so I export the release locally and then upload a tar to the server.), and you can pass a parameter to the recipe (or set an ENV variable) with the revision to use. Here's part of the recipe:

        # Selected revision, defaulting to HEAD
        rel_ver = ENV['REL_VER'] || 'HEAD'
    
        # export requested revision
        system("svn export -r #{rel_ver} -q #{configuration.repository} #{temp_dest}")

    Now all you have to do is:

    env REL_VER=1234 cap deploy
    
  3. Maxim Kulkin said 14 days later:

    About DRY with 2 recipes: I just "require" one in another and redefine some variables. Something like that:

    == deploy_staging.rb

    require 'config/deploy_production'
    
    set :deploy_to, '/path/to/#{application}_staging'
    set :repository, 'http://myhost/mystagingrepository'
    set :rails_env, :staging
    
  4. Guy Naor said 14 days later:

    That's actually much better if you need different recipes.

    Though in my case because they are all similar aside from the deploy to stuff, a small if makes it simpler.

    Also, I forgot to mention the usage of svnmerge.py to do all the code pushing to production/staging for changes that I need between releases. For those small bug fixes.

    Now I'm using a pretty complex script that tag a release, tag all the dependencies and put them all under svnmerge.py control. Then a change I want to put there is either put in place, or I svmmerge.py merge from the trunk. Makes everything much more controllable.

Trackbacks

Use the following link to trackback from your own site:
http://devblog.famundo.com/articles/trackback/43

Comments are disabled

Subscribe to The Dev Blog