Task Dependencies in Capistrano 2.0 1

Posted by ryan Fri, 21 Sep 2007 04:33:00 GMT

I've been tooling around with Capistrano 2.0 for the past couple of days. I've decided that the more mature Capistrano gets, the more it seems to be, at its core, a remote rake system with a really good suite of predefined tasks specific to Rails deployment issues. Some things Rake has that Cap 2 seems to be lacking are the ability to define dependent tasks, as well as tasks that are executed only once (the first time), with subsequent invocations being skipped.

So, here it is: http://github.com/ryankinderman/cap_task_dependencies

Just put that wherever you want (for Rails, vendor or vendor/plugins seems to make sense), and then require capistrano.rb in your deploy.rb file or wherever else it might make sense for you.

Here's an example deploy.rb that uses the two new bits of functionality with the URL above exported to /vendor/cap_task_dependencies:

File: config/deploy.rb

require File.expand_path(File.dirname(__DIR__) + "/../vendor/cap_task_dependencies/capistrano")

namespace :prerequisites do
  task :some_task1, :once => true do
    # this task will only be invoked once
  end
end

task :some_task2, :once => true do
  # this task will only be invoked once
end

task :dependent_task, 
  :depends => ["prerequisites:some_task1", :some_task2] do
  # this task will be invoked as many times as it's called,
  # and it will call some_task1 and some_task2 each time, but
  # they will only be invoked once each
end

task :combo_task do
  # combo_task combines two tasks but still, prerequisites:some_task1
  # and some_task2 will be invoked only once each. 
  # prerequisites:some_task1 will be invoked from the first line in 
  # combo_task and some_task2 as a dependency of dependent_task
  prerequisites.some_task1
  dependent_task
end

I think that just about covers it. Look at the RSpec examples if you want more info.

As always, I'd love your feedback.

Tag-based Deployment with Capistrano 1

Posted by ryan Mon, 31 Jul 2006 13:16:00 GMT

I've discovered that Capistrano is a useful deployment utility out-of-the-box if your needs are relatively simple. By simple, I mean that there is a single SCM that is accessible by all machines that you might need to do deployment to. This isn't always the case, especially when developing applications for large organizations. In such situations, you won't always get the access that you need to make deployment a straight-forward process. We've recently completed the deployment process for the project that I am currently working on, and it was one of those not-so-straight-forward scenarios.

Our source code is stored in an SVN repos that is seperate from the client's repos, and only used by the dev team (for CI and such). However, the client servers do not connect to our dev repos, and they want deployment to be possible from their own repos. To do this, we've developed a deployment policy, along with supporting logic in the deployment process, around making periodic deployments to the client SCM.

Basically, we tag the revision that we want to deploy on the dev repos, then do an export from the dev repos and import it to the client repos into a subdirectory that has the same name as the tagged version on the dev repos. We then communicate to the client that " ver X.Y.Z is now available for deployment". After that, either they or we can log into a machine on their network (which has access to their own SCM), do a checkout of the tagged deployment, and run the deployment process. The rest is your usual Capistrano stuff.

The first step is to add a Rake task to support the tag/build part of the process:

task :tag_build do
  local_repository = "svn+ssh://local/repos"
  remote_repository = "svn+ssh://remote/repos"
    
  build = TagBuild.new(local_repository, remote_repository)
  build.tag(ENV['tag'], ENV['rev'])    
end
This allows the user to tag/build from the command line like this:
rake tag_build tag=x.y.z rev=123
In the above example, the "tag" argument is the tag name to apply to both the local and remote repositories. The "rev" argument is the revision number that you want to take from the local repository. The TagBuild class contains the logic to tag the local repository and then check that tagged version into the remote repository. I leave that code as an exercise for the reader.

The next step in this process is to add tag-based deployment capabilities to your Capistrano script. This first requires a modification of the "remote:deploy" Capistrano Rake task that passes a given tag to Capistrano. Here is an example of that modification (from lib/tasks/capistrano.rake):

task(:deploy) { cap :deploy, "-Stag=#{ENV['tag']}" }
This allows the user to invoke the deployment from the command-line like:
rake deploy tag=X.Y.Z

And last but not least, a modification to the deploy.rb file must be made to consider the new "tag" value.

  if symbol_defined? "tag"
    set :repository, "#{base_repository}/tags/#{tag}"
  else
    set :repository, "#{base_repository}/trunk"
  end