130

I am using capistrano to deploy a RoR application. The codebase is in a git repository, and branching is widely used in development. Capistrano uses deploy.rb file for it's settings, one of them being the branch to deploy from.

My problem is this: let's say I create a new branch A from master. The deploy file will reference master branch. I edit that, so A can be deployed to test environment. I finish working on the feature, and merge branch A into master. Since the deploy.rb file from A is fresher, it gets merged in and now the deploy.rb in master branch references A. Time to edit again.

That's a lot of seemingly unnecessary manual editing - the parameter should always match current branch name. On top of that, it is easy to forget to edit the settings each and every time.

What would be the best way to automate this process?

Edit: Turns out someone already had done exactly what I needed:

This morning I had occasion to deploy a branch of a git repository to a staging server but hadn’t the foggiest idea how. A quick search through the capistrano source code revealed that I could use set :branch "branch_name" in my deploy script. I tried it and it worked. I then figured I would need to make a similar change across all my branches. Of course, I’m a lazy sod and wondered if there wasn’t a better way.

If you’re not familiar with git, the output of the git branch command is a list of branches with an asterisk marking the one currently checked out on your local machine. For example:

> git branch
* drupal_authentication
fragment_caching
master

So, I figured, what if I just parsed the output and searched for the branch marked as current:

set :branch, $1 if `git branch` =~ /\* (\S+)\s/m

Now I’m able to deploy whatever branch is current on my local machine from a single, shared, deploy script.

7ochem
  • 2,035
  • 1
  • 29
  • 37
Toms Mikoss
  • 8,091
  • 10
  • 26
  • 39
  • This is the updated link: [Deploying branches with Capistrano](http://www.harukizaemon.com/blog/2008/05/30/deploying-branches-with-capistrano/) – wacko Jul 18 '14 at 20:01

13 Answers13

166

This works with Capistrano >= 3.1:

add this line to config/deploy.rb:

set :branch, ENV['BRANCH'] if ENV['BRANCH']

and then call capistrano with:

cap production deploy BRANCH=master

This solution works with Capistrano < 3.1:

# call with cap -s env="<env>" branch="<branchname>" deploy

set :branch, fetch(:branch, "master")
set :env, fetch(:env, "production")
wintersolutions
  • 4,855
  • 5
  • 28
  • 48
  • 4
    If using mustistage extension, no need to set `env`, but this worked for me just using a branch – Tom Harrison Aug 23 '12 at 21:59
  • as stated by [@lulalala](http://stackoverflow.com/users/474597/lulalala) I need to use lowercase -s in order for it to fetch the specified branch. – Jahan Dec 06 '13 at 23:35
  • @Jani: Thanks, seems they changed that in newer capistrano releases, i edited my answer accordingly. – wintersolutions Dec 07 '13 at 14:25
  • I had the exact opposite problem than @Jani: I had to upper-case -S, orelse the argument would not pass through to cap, when using fetch(:var_name, 'default') to get it. – Frederik Struck-Schøning Jan 23 '14 at 10:14
  • 1
    option '-s' (--set) stands for 'Set a variable after the recipes are loaded.' and option 'S' (--set-before) stands for 'Set a variable before the recipes are loaded.' – Ramon Caldeira Jul 30 '14 at 16:05
34

Using Capistrano 3.1.0+, none of these were working for me anymore. Instead, according to their commented instructions:

   ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }

But, you don't want to use ask or it will prompt you. Instead you should use set. HEAD is the top most branch; 'edge' as it's called. If you want a different branch, replace HEAD with your branch name, eg: master, staging, etc.

To conclude with examples, in /config/deploy/production.rb, you might include this line:

   set :branch, proc { `git rev-parse --abbrev-ref master`.chomp }

...or

   set :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }

btw, HEAD is the default setting, so no need to really state that in file. Might be used better in a /config/deploy/edge.rb.

In /config/deploy/staging.rb, you might include this line:

   set :branch, proc { `git rev-parse --abbrev-ref staging`.chomp }

...or

   set :branch, proc { `git rev-parse --abbrev-ref test`.chomp }

You get the idea!

I hope these examples help future users of capistrano (^_^)

Eric Wanchic
  • 1,626
  • 19
  • 22
  • 7
    `git rev-parse --abbrev-ref HEAD` is used to find out which branch HEAD is on. running `git rev-parse --abbrev-ref staging` will (almost) always output `staging`. You can just use `set :branch, 'staging'`. – MiniGod Dec 05 '14 at 14:48
31

I can confirm that the below still works in Cap 3.11.0 13/10/18 as well as Cap 2:

In deploy.rb / stage.rb:

set :branch, ENV['BRANCH'] || 'develop'

On the command line:

cap deploy BRANCH=featurex

This gives you a default branch (which could be different for different environments), and the ability to change branches when you want.

Paul Odeon
  • 3,574
  • 1
  • 31
  • 31
28

With multistage, it's actually now:

cap production deploy -s branch=my-branch

The previous post syntax does not work in my environment

Giacomo1968
  • 23,903
  • 10
  • 59
  • 92
David Hersey
  • 911
  • 10
  • 12
  • 1
    `-s branch=foo` sets the capistrano variable branch to `foo` after the recipes are loaded – alvin Apr 23 '13 at 17:56
15

Alternatively you could structure it from the command line where you have a default branch and environment and also you are able to pass parameters to the cap call which could include the environment and the branch to use. This could be a branch that is explicitly passed or you could have a parameter which would indicate current branch as described in the link you listed.

#call with cap -S env="<env>" branch="<branchname>" deploy
...

# Prevents error if not parameter passed, assumes that default 'cap deploy' command
# and should deploy the master branch to the production server
set(:env, ‘production’) unless exists?(:env)
set(:branch, ‘master’) unless exists?(:branch)

if !env.nil? && env == "production"
   role :web, "production_ip_address"
else   # add more as needed 
   role :web, "development_ip_address"
end

if !branch.nil? && branch == "current"
   set :branch, $1 if `git branch` =~ /\* (\S+)\s/m
elsif !branch.nil?
   set :branch, branch
else   # add more as needed 
   set :branch, "master"
end
...

Code example borrowed heavily from here

naven87
  • 936
  • 1
  • 7
  • 24
10

If you're using capistrano-multistage, you only need to run

cap -s branch=$MY_BRANCH deploy

or

cap -s branch=$MY_BRANCH production deploy

without any further edit to your deploy.rb.

asymmetric
  • 3,516
  • 1
  • 32
  • 50
9

This command won't work anymore:

cap deploy -s branch=your_branch

Support for -sS flags was removed in capistrano v3+.
Here you can read more about it: link
It was mentioned in couple of answers, but currently is not correct.

What works for me:
in deploy.rb file add

set :branch, ENV['BRANCH'] || :master

then run:

BRANCH=your_branch cap deploy

Also please notice that, in order to successfully run this command, you need to be on master branch.

SakyHank
  • 91
  • 1
  • 3
3

This solution should work with all versions of Capistrano.

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

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

set :branch, branch_name('master')

Usage:

BRANCH=. cap [staging] deploy
# => deploy current branch

BRANCH=master cap [staging] deploy
# => deploy master branch

cap [staging] deploy
# => deploy default branch
Pablo Cantero
  • 5,902
  • 4
  • 30
  • 44
3

Im using version 3.3.5 and i have this working:

set :branch, 'develop'
David Rosa
  • 59
  • 2
1

General answer:

If you have a setting file with a content modified from environment to environment, you should make that line as a "template" (with a string representing variable name like @BRANCH_NAME@ or @ENV_NAME@).

Then you would have a (versioned) script able to read your config file, and replace the "@BRANCH_NAME@" variable by the appropriate value needed by your deployment process.

VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
  • See also on the same kind of issue: http://stackoverflow.com/questions/1429256/how-to-track-config-files-of-submodules-in-git/1429288#1429288, http://stackoverflow.com/questions/487753/how-to-realise-a-deployment-branch-in-git/1214509#1214509, http://stackoverflow.com/questions/6009/how-do-you-deal-with-configuration-files-in-source-control – VonC Oct 06 '09 at 09:10
1

For capistrano 3 users:

desc "prompt for branch or tag"
task :git_branch_or_tag do
  on roles(:all) do |host|
    run_locally do
      execute :git, 'tag'
      tag_prompt = "Enter a branch or tag name to deploy"
      ask(:branch_or_tag, tag_prompt)
      tag_branch_target = fetch(:branch_or_tag, 'master')
      set(:branch, tag_branch_target)
    end
  end
end

before 'deploy:updated',  :git_branch_or_tag
lfender6445
  • 25,940
  • 9
  • 95
  • 82
1

Method 1: Set stage specific branch (e.g. test, production) for deployment

Put branch configuration in stage files instead of 'deploy.rb' and set the target branch for that stage to deploy from.

For a two stage app with associated branch name test and production, the configuration will look like this,

# app_root/config/deploy/test.rb
...
set :branch, "test"
...

# app_root/config/deploy/production.rb
...
set :branch, "production"
...

This method enables to deploy from stage specific branches. So, only additional step that'll be required is to merge or rebase latest code from base branch.

Method 2: Deploy directly from any branch (using tag)

Another approach is to deploy using tag. In order to deploy using tag, set the branch config. in 'deploy.rb' as follows,

set :branch, `git describe --tags $(git rev-list --tags --max-count=1)`.chomp

And, configure the CI to conditionally deploy to different stages if the associated tag pattern matches (e.g. /.*-test$/).

Now, a deploy can be made from any branch,

  • First, create a tag from any branch,

    git tag -a v0.1.0-test -m "Version 0.1.0-test"

  • And, push

    git push origin v0.1.0-test

Note: The above methods are based on Capistrano 3.

Shakil
  • 867
  • 10
  • 16
0
git rev-parse --abbrev-ref HEAD

will return the current branch you are exactly in.

I always set the gpsh instead of git push -u origin branch_name

$ which gpsh
gpsh: aliased to git push -u origin `git rev-parse --abbrev-ref HEAD`
TorvaldsDB
  • 415
  • 6
  • 3