Deploying — the smell of coffee, the satisfaction of shipping, the excitement of lovingly crafted code going out into the world. It’s hard not to love releasing code, but when it comes to actually writing deployment scripts, it’s one part of programming that most people don’t write home about.
In the Ruby world, Capistrano has been the de-facto standard for deploying applications since it’s release in 2006, but the familiar syntax and bundled deploy tasks have remained largely constant since Jamis Buck’s departure in 2009. This summer however, it’s time to get excited about deployment again — Capistrano’s core team have been hard at work and version three is on the edge of a beta release. All in all, this feels like the perfect time to share some of the features about which I’m most excited.
All New, All the Time
Make no mistake, this is a ground-up rewrite. Capistrano is now a lean, focused and lightweight deployment management tool, cloc’ing in at just 700 lines of code. Capistrano’s Rake-like DSL has been replaced with Rake itself, while behind the scenes SSHKit — a new gem which fell naturally out of the development process — provides the heavy lifting. This combination gives us a new, yet familiar, powerful and expressive DSL:
desc 'Example task'
task :migrate do
on primary :db do
within release_path do
with rails_env: fetch(:stage) do
execute :rake, 'db:migrate'
end
end
end
end
Along with the core of Capistrano, the bundled deployment tasks have been re-created with a more contemporary toolchain in mind. Framework specific tasks have been broken out into separate gems, whilst the new `before` and `after` syntax makes it simple for gems to hook into any task:
after :finishing, :notify
This includes `cap install`:
after :install, :create_a_directory do
mkdir_p 'my_custom_directory'
end
And also the stage configuration tasks, for example `cap production`:
before :production, :warn do
info 'Deploying to production, hold tight!'
end
Multistage by default
From the initial install, Capistrano encourages thinking in terms of ‘stages’, the environments to which we will deploy our applications.
We can optionally specify the required stages as part of the installation task.
$ cap install STAGES=sandbox,qa,production
This creates all that is needed to get started. These files include minimal configuration and are full of comments and examples.
├── Capfile
├── config
│ ├── deploy
│ │ ├── production.rb
│ │ ├── qa.rb
│ │ └── sandbox.rb
│ └── deploy.rb
└── lib
└── capistrano
└── tasks
The generated ‘Capfile’ takes care of loading the bundled deploy tasks as well as any custom tasks from `lib/capistrano/tasks`
# Load DSL and stages
require 'capistrano/setup'
# Include default deployment tasks
require 'capistrano/deploy'
# Loads custom tasks from `lib/capistrano/tasks’
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
And for those who dislike the naming convention, it’s worth noting that both `capfile` and `capfile.rb` will also work.
Configuration that applies to all stages can be set in `config/deploy.rb`:
set :application, 'my app name'
set :repo_url, 'git@example.com:me/my_repo.git'
Stage specific configuration is set in the corresponding stage file:
set :stage, :production
role :app, %w{deploy@example.com}
role :web, %w{deploy@example.com}
role :db, %w{deploy@example.com}
The `server` syntax is also available where a single server has multiple roles:
set :stage, :sandbox
server 'example.com', user: 'deploy', roles: %w{web app db}
A list of default tasks is available by running `cap -vT`
Hot is the New Cold
The core Capistrano deploy tasks are idempotent, so whilst the setup tasks can be run individually, there’s really no need. `cap deploy:cold` is no more. Capistrano will setup the folder structure, including any specific directories, as part of the pre-deployment checks when running `cap production deploy`
.
├── current -> /var/www/cap-test/releases/20130807201118
├── releases
│ ├── 20130807200904
│ ├── 20130807201054
│ └── 20130807201118
├── repo
│ ├── FETCH_HEAD
│ ├── HEAD
│ ├── branches
│ ├── config
│ ├── description
│ ├── hooks
│ ├── info
│ ├── objects
│ ├── packed-refs
│ └── refs
├── revisions.log
└── shared
├── bin
├── bundle
├── config
├── log
├── public
├── tmp
└── vendor
If upgrading an existing deploy, an early version of the upgrade guide is available.
Dry Runnings
Visually confirm every command to be run without ever touching a server:
$ cap production deploy —dry-run
Rolling Restarts
Commands can be executed in parallel, sequence or in groups.
on roles :app, in: :sequence, wait: 2 do
execute :touch, 'tmp/restart.txt'
end
Log Levels and Formatters
If one word could describe Capistrano, that word could very well be “verbose”. With this release, we can finally get unwieldy logs under control with both log level and formatting options.
set :format, :pretty
set :log_level, :info
Formatters are very simple to implement, personally I look forward to my first Nyan Cat deploy.
Ask — don’t tell
Keep variables variable by asking for values at run-time:
ask :branch, 'my_default_branch'
When the variable `:branch` is first requested, we will see a Highline-inspired prompt requesting the value. Just hitting return will select the default value shown between pipes:
Please enter branch: |my_default_branch|
The default can also be a little more practical:
ask :branch, proc { `git rev-parse —abbrev-ref HEAD`.chomp }
Please enter branch: |feature/cap-test|
Excited yet? The beta release is imminent, for an excellent place to get started right now try the ‘Getting Started’ walk-through. And if you’ve enjoyed this, feel free to check out my more usual blogging home, or find me on Twitter.
Update — 3.0.0 has now been officially released.