When I wrote about deploying PHP sites with Capistrano, step 6 was a bit of a throwaway — hey, here's a quick hack for handling multistage deployment. I'd been using variants of that technique for quite some time but figured there must be a better way. There is: Capistrano multistage. There is even documentation for this. But it misses a few steps.
How to setup multistage deployment with Capistrano
- Install capistrano and capistrano multistage
- Capify your project
- Set up the
deploy/directory - Create the deployment recipes
In my Rails-based example, we'll have two stages: staging and production. You can name your stages whatever you want. However, don’t name one of your stages stage — that's a reserved word. Call it staging.
Install capistrano and capistrano multistage
gem install capistrano
gem install capistrano-ext
Capify your project
capify .
Set up the deploy directory
mkdir config/deploy
touch config/deploy/staging.rb
touch config/deploy/production.rb
Create the deployment recipes
You’re going to edit three files: deploy.rb, staging.rb and production.rb:
deploy.rb
set :stages, %w(staging production) set :default_stage, "production" require 'capistrano/ext/multistage'
default_run_options[:pty] = true
set :application, "myproject" set :use_sudo, false set :keep_releases, 5
set :repository, "https://somewhere/svn/myproject/trunk" set :scm, :subversion set :scm_username, "me" set :deploy_via, :export
# this is useful in a shared hosting environment, where you have your own JAVA_HOME or GEM_HOME. # otherwise, just set RAILS_ENV set(:rake) { "JAVA_HOME=#{java_home} GEM_HOME=#{gem_home} RAILS_ENV=#{rails_env} /usr/bin/env rake" }
# since :domain is defined in another file (staging.rb and production.rb), # we need to delay its assignment until they're loaded set(:domain) { "#{domain}" } role(:web) { domain } role(:app) { domain } role(:db, :primary => true) { domain }
namespace :deploy do task :start do ; end task :stop do ; end task :restart, :roles => :app, :except => { :no_release => true } do run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}" end end
# for some reason, this isn't enabled by default after "deploy:update", "deploy:cleanup"
# here is an example task which uses rake, as defined above after "deploy:migrate", "load_sample_fixtures" desc "load sample fixtures" task :load_sample_fixtures do run "cd #{current_release}; FIXTURES=samples #{rake} db:fixtures:load" end
staging.rb
set :deploy_to, "/path/to/#{application}-stage" # My Rails app uses RJB, so it needs to know where Java lives set :java_home, "/path/to/java-6-openjdk" set :domain, "testing.myserver.ca" set :user, "paul" set :rails_env, "staging" # I am root on my staging server and have all the right gems installed # so I don't need GEM_HOME to be overridden set :gem_home, nil
production.rb
set :deploy_to, "/home/myuser/#{application}" set :java_home, "/home/myuser/sw/jdk" set :domain, "www.myserver.ca" set :user, "myuser" set :rails_env, "production" set :gem_home, "/home/myuser/ruby/gems/"
# in a shared hosting environment, you often need to specify your own passenger configuration desc "copy the .htaccess file (passenger configuration); setup_load_paths.rb (sets GEM_HOME)" namespace :deploy do task :copy_htaccess do run "cp #{current_release}/config/htaccess_production #{current_release}/public/.htaccess" run "mv #{current_release}/config/production_setup_load_paths.rb #{current_release}/config/setup_load_paths.rb" end end
after "deploy:update_code", "deploy:copy_htaccess"
Now, you’re set. You can deploy with cap deploy (for the default stage, production), or cap staging deploy. TextMate's Capistrano plugin handles this gracefully, presenting you with a dialog listing the available stages.
Hi Paul,
Thanks for the post, really useful. It would have been perfect for my if I didn’t spend an hour figuring out why it was running the staging server in production env. I found somewhere else that the apache config needed “RailsEnv staging” within the VirtualHost.
Thought it might be useful to others.
Thanks again!
@Marc You can alternatively put “RailsEnv staging” in public/.htaccess that is likely what the copy_htaccess task is for :)
Kris: exactly!
Pingback: Ambienti di sviluppo multipli con Capistrano | FAndrea