How does Capistrano work?

How does Capistrano work? There is nothing magic, there is nothing to be scared of

You’ve joined a team or inherited a project that uses Capistrano to do deploys. When you ask how it works you are told to do cap deploy or run cap -T to see all the commands.

That gets you over day one.

Soon you will need to do something that will need you to write your own Capistrano command or edit an existing one.

You breath a sigh of relief when you realise it is all written in Ruby and you know Ruby. The DSL for Capistrano is nothing special so you are able to write solid enough cap commands. All is right with the world.

Then one day the laughter stops.

Something has changed. On the server?, with Capistrano?, somewhere someone has typed the wrong incantation into the wrong terminal.

It would be good to know exactly how Capistrano works.

In this article I will cover what is happening when you run cap deploy, this is a command that 90% of people will have come across.

I am writing this article “live”, which means I haven’t spent a long time with the internals of Capistrano, this post will cover a lot of how I found out what I needed to as well.

The Source

To find out how Capistrano works, we need to go to the source, if you haven’t went spelunking in someone else’s code before it can seem daunting but always remember you can’t break anything by reading it.

The first thing you should do is grab a local copy of Capistrano, it makes it much easier when it comes to searching for things.

git clone git@github.com:capistrano/capistrano.git

If you haven’t looked at a Ruby Gem before don’t worry, we can make this easier by skipping over certain things.

There are four directories, but we only really care about two of them, lib and spec.

The spec folder contains all the tests written for Capistrano, these tell us a lot about the code.

The lib/capistrano folder is were all the action happens. Lets go in there now.

There are lots of files, but considering we want to find out how cap deploy works I think we should look at lib/capistrano/deploy.rb, that feels like it would give us some clues.

require 'capistrano/framework'
load File.expand_path("../tasks/deploy.rake", __FILE__)

Just two lines with code in them. This first line seems to be loading in the entire framework, that sounds like it could be a rabbit hole.

The second line is much more promising, we are loading in a Rake task. We are learning something already, it seems like Capistrano might be a collection of Rake tasks. That would be nice since Rake tasks are widely documented and many of us know them.

With this new information lets just take a quick look at that framework file.

load File.expand_path("../tasks/framework.rake", __FILE__)
require 'capistrano/install'

More rake files! Not as much of a rabbit hole as I expected.

The Rake Task

If you are following along at home I am now in the lib/capistrano/tasks/deploy.rake file, the most substantial file we have looked at so far, fear not though because once we know this file just represents a collection of tasks, many of which are just grouping other tasks together, things get a lot more simple.

The conveniently named :starting task seems like a good place to, well, start.

All this does it call two other tasks, deploy:check, and deploy:set_previous_revision

I won’t go through each of the tasks here, although that might make for run second blog post. What I will do is note that even though we can scan through this deploy Rake task, there isn’t anything set to actually kick off a deploy, just the tasks we need to run for a deploy.

Come back to me, framework

Remember that framework.rake file we seen earlier and I conveniently ignored? Lets open it up.

There are three things to note.

  1. We are using the same namespace as the deploy task
  2. There are a lot of empty tasks
  3. At the bottom of the file we have our default task.

It looks like we are using some sort of Template Method Pattern, which is the base file (framework.rake) sets up empty methods that will be filled in later by the deploy.rake. (Thanks to Humza for putting a name to the pattern!)

Now, this default task is where the magic happens.

desc 'Deploy a new release.'
task :deploy do
  set(:deploying, true)
  %w{ starting started
      updating updated
      publishing published
      finishing finished }.each do |task|
    invoke "deploy:#{task}"
  end
end
task default: :deploy

The first thing we do set is what I imagine is a flag, deploying to true. Without looking into the code I imagine we check this at various points so that Capistrano can see the current state of the application.

Now we loop through each of the tasks we need to perform and perform them with the invoke method.

Today I learned

Today I learned that Capistrano is just a bunch of rake tasks that execute in a specific order.

Each task is well defined and small enough that it is easy to see what it is doing.

There is nothing magic, there is nothing to be scared of.

Recent posts View all

MacProductivity

Some Mac Tips

Some settings or tips I've learned over the years to make using your Mac an even nicer experience

Writing Git

How to speed up Rubocop

A small bit of config that could speed up your Rubocop runs