Developing and testing system automation with capistrano-cowboy



Written By : Josh Nichols


December 16, 2010

I’m not sure if you’ve heard, but we love us some system automation.

I think most of us can agree system automation (chef, puppet, moonshine, etc) is a good thing. As much as we love it, things can get tedious when it comes to actually developing and testing the automation. The process usually degrades into something like:

git commit -m "Update configuration to install widgets"
cap staging deploy # => argh, fail
git commit -m "Update configuration install widgets for real."
cap staging deploy # => argh, fail!
git commit -m "Blarg, really install widgets for real."
cap staging deploy # => argh, fail!!!
git commit -m "widget install thing go please go"
cap staging deploy # => argh, FAIL!!!

Not only are you about to pull your hair out, but you also have just littered your commit history with noise, making it harder to see what changes went in when you’re done. Basically, we want to reduce the feedback cycle between making changes, seeing them work, and being able to commit and move on to the next task. Here, the version control just gets in the way.

Capistrano, as it happens, does supports deploying from the current working directory, avoiding having to commit locally and checkout remotely. You can manage this with a few lines to your Capfile or config/deploy.rb:

set :repository, "."
set :deploy_via, :copy
set :scm, :none

Now the workflow looks more like:

cap staging deploy # => argh, fail
cap staging deploy # => argh, fail!
cap staging deploy # => argh, fail!!!
cap staging deploy # => argh, FAIL!!!
cap staging deploy # => oh... SUCCESS!
git commit -m "Update configuration to install widgets"

This is much better, particularly the absence of committing in anger. But now, you’re always deploying from the current directory. It should be a special case, not the default behavior. The next thing we can do is this into a task, so you can opt in:

task :cowboy do
  set :repository, "."
  set :deploy_via, :copy
  set :scm, :none
end

Here’s the new workflow:

cap staging cowboy deploy # => argh, fail
cap staging cowboy deploy # => argh, fail!
cap staging cowboy deploy # => argh, fail!!!
cap staging cowboy deploy # => argh, FAIL!!!
cap staging cowboy deploy # => wait... SUCCESS!
git commit -m "Update configuration to install widgets"
git push
cap staging deploy # =>; still success, ship it!

We found ourselves doing this on multiple projects, and really, we just want it everywhere. Capistrano will load a ~/.caprc if it exists, and so our little cowboy ended there. He’s grown up a bit since then, and he eventually made his way to a gist

Personally, I draw a line at sharing gists. It was time to become a gem. Behold! Install the usual way:

gem install capistrano-cowboy

Next, require it in your ~/.caprc:

require 'capistrano-cowboy'

And now you are ready to deploy from the hip in any capistrano-enabled project on your system:

cap staging cowboy deploy

Before you get any crazy ideas, please be extremely careful, or you might just shoot yourself in the foot. I’ll leave you with imaginary scenario that definitely has never happened:

git checkout crazy-awesome-but-experimental-feature
cap production cowboyw deploy # => what... nyoooo!
cap seppuku # => http://www.realultimatepower.net/ninja/seppuku.htm