Welcome to part two in our three part Capistrano 3 tutorial series. If you worked your way through part one then you should be all set to write your first Capistrano deploy script. The deploy script we'll create will be a "bare bones" script which simply moves files from an SVN tag to a folder on your Staging server.

Compared to part one this tutorial instalment is a walk in the park. Enjoy!

Making Capistrano 3 work with non-Rails based projects

Unlike Capistrano 2, Capistrano 3 is not written for default usage with Rails projects. This makes things simpler as we don't have to do anything special for Capistrano to work with our LAMP project.

Let's create a minimal deploy script for our 'Example.com' application. Change into the config folder within the Example project's deploy script files and edit the deploy.rb file:


cd /home/deploy/capistrano/example/config/
vi deploy.rb

You should see something along the lines of:


set :application, 'my_app_name'
set :repo_url, 'git@example.com:me/my_repo.git'

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

# set :deploy_to, '/var/www/my_app'
# set :scm, :git

# set :format, :pretty
# set :log_level, :debug
# set :pty, true

# set :linked_files, %w{config/database.yml}
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

# set :default_env, { path: "/opt/ruby/bin:$PATH" }
# set :keep_releases, 5

namespace :deploy do

  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      # Your restart mechanism here, for example:
      # execute :touch, release_path.join('tmp/restart.txt')
    end
  end

  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do
      # Here we can do anything such as:
      # within release_path do
      #   execute :rake, 'cache:clear'
      # end
    end
  end

  after :finishing, 'deploy:cleanup'

end

We can get rid of a lot of the example and informational lines and fill in some of our known settings:


set :application, 'example'
set :repo_url, "svn+ssh://svn.zodiacmedia.co.uk/example"

set :ssh_options, {
  user: 'deploy'
}

set :scm, :svn

set :format, :pretty
set :log_level, :debug

set :keep_releases, 5

namespace :deploy do
  after :finishing, 'deploy:cleanup'
end

This file contains only the shared settings that are common to all of our deployment environments (Staging and Production). Let's set our Staging specific settings file now.


cd /home/deploy/capistrano/example/config/deploy
vi staging.rb

By default this reads:


set :stage, :staging

# Simple Role Syntax
# ==================
# Supports bulk-adding hosts to roles, the primary
# server in each group is considered to be the first
# unless any hosts have the primary property set.
role :app, %w{deploy@example.com}
role :web, %w{deploy@example.com}
role :db,  %w{deploy@example.com}

# Extended Server Syntax
# ======================
# This can be used to drop a more detailed server
# definition into the server list. The second argument
# something that quacks like a hash can be used to set
# extended properties on the server.
server 'example.com', user: 'deploy', roles: %w{web app}, my_property: :my_value

# you can set custom ssh options
# it's possible to pass any option but you need to keep in mind that net/ssh understand limited list of options
# you can see them in [net/ssh documentation](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start)
# set it globally
#  set :ssh_options, {
#    keys: %w(/home/rlisowski/.ssh/id_rsa),
#    forward_agent: false,
#    auth_methods: %w(password)
#  }
# and/or per server
# server 'example.com',
#   user: 'user_name',
#   roles: %w{web app},
#   ssh_options: {
#     user: 'user_name', # overrides user setting above
#     keys: %w(/home/user_name/.ssh/id_rsa),
#     forward_agent: false,
#     auth_methods: %w(publickey password)
#     # password: 'please use keys'
#   }
# setting per server overrides global ssh_options

# fetch(:default_env).merge!(rails_env: :staging)

Again let's ditch the commented example lines and fill in our specific settings for our Staging deployment server:


set :stage, :staging

server 'staging.zodiacmedia.co.uk', roles: %w{web app db}, port: 22

set :deploy_to, '/var/www/staging.example.com'

Note that you need to change port 22 to the appropriate number if you run SSH sessions on a non-standard port.

We're now in a position to do some testing:


cd /home/deploy/capistrano/example
cap staging svn:check

This should show you the results of an svn info query on your target repository. If you run into issues it'll almost certainly be due to SSH permissions. To debug this you can use the tactic of switching to the deployuser's Linux account and trying to run the svn info command from that Linux account. For example:


sudo –s
su deploy
svn info svn+ssh://svn.zodiacmedia.co.uk/example

If you can run the svn info query ok then continue your debugging by running the same commands Capistrano is trying to run using the same Linux users and relevant servers.

When you run cap staging svn:check you'll be prompted to enter your SSH key's password. This can get quite tiresome and can be avoided by saving your key to ssh-agent for the duration of your SSH session as follows:


ssh-agent bash
ssh-add

Then type in your SSH key's password.

Getting ready to deploy to Staging

If the deploy_to folder doesn't exist on your target server create it using:


sudo mkdir -p /var/www/staging.example.com

Then change the folder ownership and permissions as follows:


sudo chown deploy:www-data /var/www/staging.example.com/

Then we need to run cap staging deploy:check to setup the folder structure needed when deploying. Change directory:


cd /home/deploy/capistrano/example/

And run:


cap staging deploy:check

You should be prompted to enter your SSH key when this command runs unless you've saved in ssh-agent.

This will create the standard folder structure in the /var/www/staging.example.com folder which can be seen as follows:


ls -la /var/www/staging.example.com
total 16
drwxr-xr-x  4 deploy www-data 4096 Nov 29 09:19 .
drwxr-xr-x 12 root   root     4096 Nov 29 09:17 ..
drwxrwxr-x  6 deploy deploy   4096 Nov 29 16:21 releases
drwxrwxr-x  2 deploy deploy   4096 Nov 29 09:19 shared

The releases folder will go on to contain subfolders where the contents of your codebase will be checked out to. When you actually create a successful release then a symlink with the identifier current will be added to the /var/www/staging.example.com folder. This will point to the latest SVN export in the releases folder. The shared folder is for placing assets shared across releases (e.g. user uploaded content etc) which will be symlinked to from your current release.

Before we make our first deploy let's refresh our memory on our deploy strategy.

An SVN tag is a release

Previously we outlined how each SVN tag would be a release candidate. To make this viable the svn.rake file we added to our Capistrano installation earlier prompts us to enter an svn_location when we run a deploy. This defaults to trunk but if we wanted to deploy a specific tag then we could enter tags/TAGNAME where TAGNAME is replaced by an actual tag name e.g. 1.12 or 0.43 etc. We tend to use incremental numbers for tag names as it's easier to keep track of releases that way.

So let's make our first release.


cap staging deploy

Enter the tag name you want to deploy, et voilà, your one line deploy of code from Subversion to Staging is complete!

We're now in a position where we can move on to the more powerful functionality of Capistrano, creating bespoke tasks in the deploy script for a project. In the next installment in this tutorial series we'll expand on the "bare bones" deploy script we've just created and evolve it into a script for deploying a Drupal powered website.

Previous Post Read More Random Post Next Post

Comments

Acheloos-m (not verified) on February 3rd February 2014 - Permalink

Excellent Tutorial! +1 And one of the less for Cap3 - not much around the web for now. Is part 3 still planned - would love to read further! Cheers

Billy Davies on February 3rd February 2014 - Permalink

Yep, part 3 of this series is definitely still in the pipeline. We've been waylaid with client work but we'll look to get this published in the next week or so.

Sami Haahtinen (not verified) on February 20th February 2014 - Permalink

Just a heads up on the server definition in staging.rb, the role attribute takes in the names of roles. There is no special meaning to :all in this attribute. So if you assume that you have roles db, web, and lb and you define a server with :all. Referencing to the roles (say, :web) in deployment rules will not match that server. It works the other way around though. If you define :web in the server and refer to a role :all in the deployment rules the server will match. Your example will still work, but you essentially have a single role (named "all") defined in your configuration.

Billy Davies on March 2nd March 2014 - Permalink

Hi Sami, Thanks very much for picking this up. I've amended the staging.rb server definition of the tutorial to use the standard '%w{web app db}' declaration. Billy

Martin Lauer (not verified) on March 25th March 2014 - Permalink

I'd love to see a third part.

Billy Davies on March 2nd March 2014 - Permalink

Hi Martin, Thanks very much for the encouragement. I'm starting Part 3 today. We've been snowed under with client work for the last couple of months but it would be great to finally finish off this series. We also finally moved to Git for our source control so we plan to create a Git version of this series in the future. Billy

Darren Taylor (not verified) on March 4th March 2014 - Permalink

Great article looking forward to part 3.

Joe (not verified) on March 5th March 2014 - Permalink

Hi Billy, very nice tutorial. Again, we are looking forward to reading your third part :) Thank you for your great work.

Shrek (not verified) on March 31st March 2014 - Permalink

Great article, thank you very much!! Do you have any plan for the third part ? We are all looking forward it :)

Hiveer (not verified) on April 4th April 2014 - Permalink

error when I run cap unicorn:start cap aborted! TypeError: no implicit conversion of Rake::Task into String

Billy Davies on February 14th February 2015 - Permalink

Hi Hiveer, Unfortunately this looks an error which is specific to the project you're working on. I've not used Unicorn before as we're a PHP focused company so Google is your friend on this one. A quick Google for your error message reveals: http://stackoverflow.com/questions/22666681/capistrano-3-1-0-deployment-issue-with-rake-10-2-0 - perhaps that could be the answer. Best of luck, Billy

Victor (not verified) on September 21st September 2014 - Permalink

Still waiting for part 3 ;)

Billy Davies on February 14th February 2015 - Permalink

You'll be pleased to know Part 3 of this series has now been published!