I’m finishing up my code reading of Capistrano‘s recipes with the recipe that is run most often, deploy
.
The Code and Review
deploy
1 2 3 4 5 6 |
namespace :deploy do task :default do update restart end end |
The deploy
recipe itself is simple and just runs deploy:update
and deploy:restart
.
deploy:update
1 2 3 4 5 6 7 8 |
namespace :deploy do task :update do transaction do update_code symlink end end end |
deploy:update
uses a transaction to make sure both deploy:update_code
and deploy:symlink
run successfully.
deploy:update_code
1 2 3 4 5 6 7 |
namespace :deploy do task :update_code, :except => { :no_release => true } do on_rollback { run "rm -rf #{release_path}; true" } strategy.deploy! finalize_update end end |
Now deploy:update_code
starts to do some interesting work. First it defines an on_rollback
so Capistrano knows how to revert the code updates. Next it calls Capistrano::Deploy::Strategy#deploy!
to do the code update. This class provides a standard interface for how each of the different :deploy_via
options work on the remote servers. Finally, deploy:finalize_update
is called.
deploy:finalize_update
1 2 3 4 5 6 7 8 9 |
namespace :deploy do task :finalize_update, :except => { :no_release => true } do run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true) # mkdir -p is making sure that the directories are there for some SCM's that don't # save empty folders run < { "TZ" => "UTC" } end end |
deploy:finalize_update
does a few housecleaning tasks to make sure all of the files are setup correctly:
- Changes the release files to be group writable if the configuration option for
:group_writable
is set. - Removes the log, system, and tmp/pids directories, which should be symlinks.
- Creates a public and tmp directory in the latest release.
- Symlinks the shared log, system, and pids directories into the latest release.
- Finally, it updates the timestamps of the public assets. This is done so when Rails links to assets they will have a newer url.
Next deploy:finalize_update
and deploy:update_code
return control back to deploy:update
which runs deploy:symlink
next.
deploy:symlink
1 2 3 4 5 6 7 8 9 10 11 12 |
namespace :deploy do task :symlink, :except => { :no_release => true } do on_rollback do if previous_release run "rm -f #{current_path}; ln -s #{previous_release} #{current_path}; true" else logger.important "no previous release to rollback to, rollback of symlink skipped" end end run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}" end |
deploy:symlink
is pretty simple:
- It defines an
on_rollback
to revert errors. - It removes the current path (remember that it’s just a symlink to the deployed release directory).
- It creates a symlink from the latest release (being deployed) to the current path, thus “moving” the symlink target.
Now deploy:update
is done so it returns back to deploy
which now runs deploy:restart
.
deploy:restart
1 2 3 4 5 6 |
namespace :deploy do task :restart, :roles => :app, :except => { :no_release => true } do warn "[DEPRECATED] `deploy:restart` is going to be changed to Passenger mod_rails' method after 2.5.9 - see http://is.gd/2BPeA" try_runner "#{current_path}/script/process/reaper" end end |
The try_runner
method is just a simple wrapper that will try to run a command using sudo but as a different user. In this case, Capistrano is running Rails’ reaper script, which is supposed to restart the application servers. I haven’t used reaper scripts for a long time, I think pre Rails 1.0 days. Instead I just override deploy:restart
with my own configuration.
1 2 3 4 5 6 7 8 |
# Example deploy:restart for Passenger # namespace :deploy do desc "Restart Application" task :restart, :roles => :app do run "touch #{current_path}/tmp/restart.txt" end end |
This concludes my code reading on the Capistrano codebase. I looked at how the cap
command is translated into the recipes and then examined a few of the common Capistrano recipes and what commands they run.
Next week, I’ll be starting to read through a new project. Which one would you be interested in: