Example of a remote application deployment coded in php using Capistrano and Git.

January 31, 2016 21 egor

Capistrano is an instrument with an open source code able to run commands on a remote server via ssh access. Capistrano is written in Ruby, and it is mostly applied to deploy Ruby applications, though it can also be used to deploy applications in other programming languages. In this very recipe, PHP programming language will be used. So, off we go.

It is a completely working recipe tested on Windows 7 using denwer. The recipe assumes you have basic skills of working with Git. github.com is used as a basic repository in the recipe.

Attention! While the article was being written, Capistrano 3 had been released, but Capistrano 2 will be used in the recipe.

Installation

Install Ruby latest version. You can download Ruby by the link: http://rubyinstaller.org/downloads/. Having installed it, make sure Ruby path got into Path (Properties "My Computer"-> System Advanced Settings ->Environment Settings -> System Settings -> C:\Ruby200-x64\bin;...).

Install RubyGems. You can download it by the link: https://rubygems.org/pages/download. Extract archive with RubyGems into the place suitable for you. Open command line and go to directory where RubyGems is extracted. For example, we have extracted RubyGems into folder: C:/rubygems-2.4.5.

cd c:/
cd rubygems-2.4.5

And run an installation command:

ruby setup.rb install
gem update --system

Now, let's pass to installing Capistrano:

gem install capistrano --version 2.15.5

Now, it is turn for Capifony to be installed:

gem install capifony

Let's install capistrano_rsync_with_remote_cache to work comfy with scm (subversion, git):

gem install capistrano_rsync_with_remote_cache

Capistrano assumes we have already got folder config in the project root, as configuration files are placed in this very folder in Ruby, but we do not have that one. We need to create it. For example, our application is placed in folder: d:/web/home/deploy.test.local/www.

d:
cd /web/home/deploy.test.local/www
mkdir config

We can create folder config with the help of Windows Explorer without using console.

If the folder where catalogue config was created in is a root folder for the server, then it is necessary to create file .htaccess for Apache therein that will disable viewing catalogue contents. An example of file .htaccess:

Options All -Indexes

Capification

Having installed Capistrano, the first thing we need to do is to execute command capify . for our application. The command will settle Capistrano configurations for application to further deploy them on the server. Prior running the command, it is necessary to ensure you are in the root folder of your application and then start the command in console:

capify .

Command capify . will create two files:

Capfile is a master file needed to Capistrano. It is very Capfile. that Capistrano searches and downloads by default. In its unmodified view, generated Capfile is very flat: it downloads file config/deploy.rb. We do not need to know anything else about that. At this stage, let’s leave it and pass to the second file.

config/deploy.rb is a deploy configuration file of our application.

In its original state, file deploy.rb will look like that:

set :application, "set your application name here"
set :repository,  "set your repository location here"
set :scm, :subversion
# Or: `accurev`, `bzr`, `vcs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`
role :web, "your web-server here"                          # Your HTTP server, Apache/etc
role :app, "your app-server here"                          # This may be the same as your `Web` server
role :db,  "your primary db-server here", :primary => true # This is where Rails migrations will run
role :db,  "your slave db-server here"
# If you are using Passenger mod_rails uncomment this:
# if you're still using the script/reapear helper you will need
# these http://github.com/rails/irs_process_scripts
# namespace :deploy do
#   task :start do ; end
#   task :stop do ; end
#   task :restart, :roles => :app, :except => { :no_release => true } do
#     run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
#   end
# end

Let’s recompile this configuration file suitably for our needs.

Configuration

The first thing we have got to do is to name out application, e.g. "my php app":

set :application, "my php app"

Then, we need to specify repository where our application code is located. You need to have an access to this repository from your local PC and hosting where you are going to deploy your project. Now, let’s specify repository:

set :repository,  "https://userName:userPassword@github.com/userName/repoName.git"

Here is an example when repository is connected with transferring username and password. It is not safe, thus, it is better not to do like that, though it suites quite well as an example.

If url for access to repository from local PC or server differ (e.g. connection via ssh is located on the other port), then it is necessary to specify both addresses. Example:

set :repository, "ssh://git@example.com:22100/repoName.git"
set :local_repository, "ssh://git@example:repoName.git"

As we use Git (by default, it is Subversion), then it is necessary to add the following line:

set :scm, :git

Now, let's specify a folder for Capistrano on the server where we need to deploy our application in. Let's consider structure of files and folders used by Capistrano to post application in order to understand those better.

An application successfully deployed via Capistrano will have the following structure (where deployTo is a catalogue where we are going to deploy application into):

deployTo
deployTo/releases
deployTo/releases/12345678901234
deployTo/releases/...
deployTo/shared
deployTo/shared/log
deployTo/shared/pids
deployTo/shared/system
deployTo/current -> [deploy_to]/releases/12345678901234

Every time when deploying the project, a new directory with application last version will be created in folder releases. After that, a symbolic link current will only point to a newly created directory with a current application version. If architecture of your application is the same as the architecture of applications in Ruby on Rails where web directory and root folder of the project are different, then it is necessary to make sure that the server is set just for this directory (it is deployTo/current/public in Ruby on Rails).

Let’s get back to our configuration file. Specify the folder in which our application is posted on the server. By default, it is folder /u/apps/#{application} (where #{application} is a name of our application specified in variable: application). Our directory differs from the directory specified by default, therefore, we need to clearly specify it. For example, let’s deploy the application into folder /var/www/userSrvName/data/deployTest:

set :deploy_to, "/var/www/userSrvName/data/deployTest"

We specify username for ssh or ftp access.

set :user, "userName"

We specify number of stored releases on the server (number of stored copies in folder releases):

set :keep_releases, 3

We specify username to get access to repository. Some vcs do not support this parameter. If version control system you use does not support this parameter than you need to specify username in parameter :repository as shown above.

set :scm_username, "userName"

Basically, option below saves clone of your application on the server and then simply draws up changes.

set :repository_cache, "git_cache"

If you need to let Capistrano use sudo access to perform operations, then specify true:

set :use_sudo, false

We specify data copy protocol:

set :via, "scp"

We specify project branch where application deploy code will be taken from.

set :branch, "master"

We specify that it is necessary to save last cash version on the server and when performing a new deploy, one needs to download only updates:

set :deploy_via, :remote_cache

We disable copying specified files or catalogues:

set :copy_exclude, [".git"]

Now, we specify where our servers are located. By default, Capistrano uses three roles to post applications: web, app and db. As we use only one server and role functionality is unnecessary for us, then let’s use the following syntax:

role :web, "example.com"

ssh-settings. You can set up ssh-connection by yourself but we are going to turn it off in this recipe:

set :ssh_options, {
    config: false
    #Other options...
}

Let's try that

Let's try to interconnect Capistrano with our server. As a first step, we create basic folder structure by executing command (we have to stay in project root, if we did not change anything, we stay just right there):

cap deploy:setup

When executing cap deploy:setup command, Capistrano connects to our server and consecutively performs a number of commands mkdir to create basic structure (make sure beforehand that you have access rights to the directory you are going to deploy your application into).

We pass to dependency check. Now, when we have created basic directories and files, we request Capistrano whether everything is ready to go on with deploying application:

cap deploy:check

Command cap deploy:check checks readiness of local and remote servers and outputs a corresponding message to us. If something goes wrong, then you will get an error message (for example, you do not have rights to make entries into a folder or etc.).

We can try to send code to the server (The code should already be in the repository specified above and the branch specified above, therefore, the branch should be created). It will not be a major deploy but simply a test of downloading code to the server. Let’s make sure everything is OK:

cap deploy:update

Command cap deploy:update downloads code from repository to the server and installs a symbolic link current to a new folder.

Application Deploy

At last, we have come to a real application deploy. Deploy command is only an envelope over a sequence of other commands.

As deploy:update and deploy:finalize_update commands are specific for Ruby on Rails applications, we need to redefine those. Except for these two commands, it is better to redefine commands deploy:start and deploy:stop, as they are coded for Ruby on Rails, and, if started, they will most likely lead to an error (there are other specific commands but we are not going to redefine them as we are going to cover only the basic ones):

namespace :deploy do
    task :start do
    end
    task :stop do
    end
    task :restart do
    end
    task :finalize_update do
    end
end

Now, let’s automate our application deploy:

after "deploy:update", "deploy:moveToSrv"
namespace :deploy do
    task :moveToSrv do 
        run "cp -r /var/www/userSrvName/data/deployTest/current/* /var/www/userSrvName/data/deployTest/current/.[a-zA-Z0-9]* /var/www/userSrvName/data/www/example.com/";
    end
    task :start do ; end
    task :stop do ; end      
    task :restart do ; end      
end

We have stated here that after command update, we need to start command moveToSrv that copies files from the current release version directly into the web-site folder. You can add needed commands by yourself based on this example.

Thus, the final file looks like this:

set :application, "my php app"
set :repository,  "https://userName:userPassword@github.com/userName/repoName.git"
set :scm, :git
set :deploy_to, "/var/www/userSrvName/data/deployTest"
set :user, "userName"
set :keep_releases, 3
set :scm_username, "userName"
set :repository_cache, "git_cache"
set :use_sudo, false
set :via, "scp"
set :branch, "master"
set :deploy_via, :remote_cache
#set :copy_exclude, [".git", ".gitignore", "log", "public", "REVISION"]
set :copy_exclude, [".git"]
role :web, "example.com"                          # Your HTTP server, Apache/etc
after "deploy:update", "deploy:moveToSrv"
namespace :deploy do
    task :moveToSrv do 
        run "cp -r /var/www/userSrvName/data/deployTest/current/* /var/www/userSrvName/data/deployTest/current/.[a-zA-Z0-9]* /var/www/userSrvName/data/www/example.com/";
    end
    task :start do ; end
    task :stop do ; end      
    task :restart do ; end   
end
set :ssh_options, {
    config: false
}

Now, deployer is ready for work. We execute command from application root.

cap deploy:update

or

cap deploy

Done, our application from the defined repository and defined branch have been moved into our site (example.com), now we do not need to copy files via ftp any more. There is one problem about this recipe: it does not deploy changes in Data Base structure, though if you do wish to do that, the issue can be solved.

Application Deploy into test server/domain (multistage)

Quite often when developing an application, it is necessary to test it on a test server or sub-domain before deploying it. Extension capistrano-ext is used for this purpose, it allows using individual configuration files and deploying for the needed server from a definite branch.

In order to install an extension, it is needed to input a command in console:

gem install capistrano-ext

Then, we create a new catalogue /path/deploy/from/config/<code> in folder <code>/path/deploy/from/config/:

mkdir deploy

We can create folder deploy out of Windows Explorer not using console.

Now, we place out configuration files into folder deploy, for example production.rb (downloading code from a wizard branch to a real server) and develop.rb (downloading code from branch develop to a test server), and set up those according to the guidelines above. Then we compile the following two lines into file /path/deploy/from/config/deploy.rb:

set :stages, %w(develop production)
require 'capistrano/ext/multistage'

What is left is to create a basic structure for new configuration files:

cap production deploy:setup

and

cap develop deploy:setup

Now we can deploy application using commands cap production deploy (to a real server) and cap develop deploy (to a test server). If you need to use one more configuration, then simply create it in catalogue deploy, set it up and add its name into variable :stages:

set :stages, %w(test production develop)

Now, when executing command cap deploy, Capistrano will inform that you need to specify a recipe with the help of which application deployment will be done, and then it will stop performance. To avoid such a situation, we can specify deploy recipe by default. We need to specify variable default_stage for that. For example, application will be deployed by default from branch develop into the test server:

set :stages, %w(test production develop)
set :default_stage, "develop"
require 'capistrano/ext/multistage'

After these changes, command cap deploy will be equal to command cap develop deploy.

Additionally

Capistrano commands. You may need some of the following commands:

Show list of possible options:

cap -h

Show list of all options and detailed description to each of them.

cap -H

Show list of all tasks and a short description to each of them.

cap -T

Show detailed information on the task set.

cap -e deploy:moveToSrv

Possible errors and ways to solve them

When calling command gem install capistrano-ext or gem install capistrano – version 2.15.5, an error appears:

ERROR:  Could not find a valid gem 'capistrano-ext' (>= 0), here is why:
Unable to download data from https://rubygems.org/ - SSL_connect retur
ned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://rubygems.org/latest_specs.4.8.gz)

Run the command in console:

gem sources --add http://rubygems.org/