Matthew Lindfield Seager

Matthew Lindfield Seager

Capistrano Branches

Capistrano (as of version 3.19.1) still defaults to deploying the master branch, even though GitHub, GitLab and BitBucket have all changed their default branch name to main.

The simplest fix for this is to add a line to config/deploy.rb which sets the branch to main:

# config/deploy.rb
set :branch, "main"

You can easily override that branch setting on a per-environment basis. For example, if you want to always deploy the currently checked-out (HEAD) branch when deploying to staging, override the :branch setting in config/deploy/staging.rb. The current branch can be obtained with git rev-parse --abbrev-ref HEAD so the command becomes:

# config/deploy/staging.rb
set :branch, `git rev-parse --abbrev-ref HEAD`.chomp

Getting more granular

Setting a global default and then overriding it per-environment is probably sufficient 95+% of the time, but what if you ever want to do something different?

If it’s a one-off thing, you could edit the local copy of config/deploy.rb or config/deploy/<env>.rb and deploy. As long as you don’t commit and push the changes to Git, Capistrano on your machine will use the branch you set, and everyone else will happily keep using the original setting from Git.

However, I found a better way (on StackOverflow) that doesn’t rely on you having to remember not to commit your changes. We will set up a workflow that gives us:

  • a sane default for all environments
  • the ability to set a new default for a specific environment
  • the ability to override the default for any given deploy (for some or all environments)

First the code, then the explanation (lightly modified from my project’s README).

# config/deploy.rb
# config valid for current version and patch releases of Capistrano
lock "~> 3.19.1"

def branch_name(default_branch)
  branch = ENV.fetch('BRANCH', default_branch)

  if branch == '.'
    `git rev-parse --abbrev-ref HEAD`.chomp
  else
    branch
  end
end

# Uncomment one of these to set an app-wide default for all environments
# set :branch, "main"
# set :branch, branch_name("main")

set :application, "my_app"
# etc, etc

 

# config/deploy/production.rb
server "<hostname>", user: "deploy", roles: %w[app db web]

set :branch, branch_name("main")

 

# config/deploy/staging.rb
server "<hostname>", user: "deploy", roles: %w[app db web]

set :branch, branch_name(".")

And here’s the relevant section from my project’s README.md:

Deployment

Capistrano is used for deployment. By default the main branch will be deployed in production (more info in next section), so make sure all code has been committed (and pushed), then run:

> cap production deploy

A typical deployment takes less than 30 seconds and will finish with:

00:19 deploy:log_revision
  01 echo "Branch main (at a087b4c40d8ef0031a0b90773c8511d8e873fa59) deployed as release 20240921040843 b…
✔ 01 deploy@<hostname> 0.067s

The last five releases are kept on the server (in theory allowing for easy rollback) but in practice it’s generally safer to revert the changes and deploy a new release (reference).

Deploying a different branch

The default branch has been set to “main” in config/deploy/production.rb (using set :branch, branch_name("main")), but you can override it by setting the BRANCH environment variable when running Capistrano:

> BRANCH=my-new-feature cap production deploy

Set the variable to . to deploy the current branch (a bit like the current working directory on *nix systems):

> BRANCH=. cap production deploy

The . shortcut also works in deploy files. We default to using the current branch in staging using:

# config/deploy/staging.rb
set :branch, branch_name(".")

P.S. On a bigger team you might not want to make it so easy to deploy a different branch to production. In that case, don’t include the banch_name method in production.rb:

# config/deploy/production.rb
server "<hostname>", user: "deploy", roles: %w[app db web]

set :branch, "main"

P.P.S. I deliberately commented out set :branch from config/deploy.rb. That way if I forget to set the branch for a new environment, Capistrano will attempt to use master and the deploy will fail. If you want new environments to default to something else, uncomment one of the set :branch lines in config/deploy.rb