Create Capistrano Recipes for NodeJS Applications

This, and the following entry, are the main focus of this tutorial. Here you will learn how to create a Capistrano project, recipe – for deploying your application code, run tests, stop and restart the NodeJS server; and maintain roles. The assumptions are that git, npm and NodeJS are already installed on target servers, and your code is hosted in a git repository.

This entry is structured around a set of basic questions, followed by answered and code snippets. Snippets below are based on default code generated by an install command.

How to create a Capistrano project?

A Capistrano project can be created, issuing the following command, in a project’s folder:

cap install

It is a good idea to store Capistrano projects in a separate repository, to be able to control access and separate concerns. This dedicated repository could follow a folder structure with a directory per project.

What are Capistrano recipes and tasks?

Capistrano recipes are a set of instructions, that run either local or remote shell commands. Commands can be grouped by tasks – defined by a set of Ruby instructions. The main Capistrano recipe is “deploy”, with some of it’s tasks below:

publishing - used for deploying code

restart - used for restarting your application related services

rollback - used for rolling back a release

A wide range of other tasks are available, and documented here:

http://capistranorb.com/documentation/getting-started/flow/

Custom tasks can be added, to extend the default deploy workflow. In this article, we will create two additional deployment tasks:

test - used for running tests

npminstall - used for installing nodejs dependencies, via npm

A default Capistrano project consists of two deployment scenarios, one for a production server, and one for a staging server; with a common deployment script. These are stored in the following files:

config/deploy.rb - main deployment script

config/staging.rb - staging deployment configuration

config/production.rb - production deployment configuration

What are Capistrano roles?

Capistrano roles define the function of each server, within your cluster or cloud. Three major roles are defined by default:

app - application servers

web - web servers

db - database servers

This tutorial focuses on the app role; with instructions on how to configure provided further down the document.

How does a Capistrano deployment work?

Essentially, Capistrano executes a set of shell scripts on a given set of servers. This process starts by uploading a shell script for each task to a remote server, executing it and capturing it’s output. A failed command, results in a failed task and recipe. By default, the first executed script, clones a repository – configurable through the recipe file. 

Below are configuration samples for your git repository, using HTTP authentication, stored in deploy.rb:

1. Defines the VCS:

set :scm, :git

2. Defines the application name:

set :application, ‘ApplicationName’

3. Defines the repository URL:

set :repo_url, 'http://hostname/repository.git'

4. Defined git repository authentication:

set :git_http_username, ‘GitUsername’

set :git_http_password, 'GitPassword'

5. Output formatting, and log level:

# Output format and log level:

set :format, :pretty

set :log_level, :info

Followed by directory structure settings:

set :deploy_to, '/path/to/application/directory/'

Directory structure notes:

Capistrano creates a couple of subdirectories, within the target directory, such as:

current - current release files, with an extra REVISION file holding the current revision number

repo - git repository content

releases - previous release directories, with subdirectories named after previous release year, month, day and time

revisions.log - revision log

shared - shared files

*.tar.gz - compressed archives of each release

As such, your most recent application resides in current/. Should you decide to rollback a release, Capistrano would fall back to one of the previous releases, already found in this directory.

Once Capistrano is aware of where to fetch the from, and which directory to deploy to, it must be made aware of which servers to perform this tasks on:

Production server configuration (config/production.rb):

1. Define the app role, with a main build server:

role :app, %w{deployment@hostname.com}

2. Define servers for this role:

server 'hostname.com’, user: ‘deployment’, roles: %w{app}

Staging server configuration (config/staging.rb), is similar to a production configuration; but pointing to different servers:

role :app, %w{deployment@staging.hostname.com}

server 'staging.hostname.com', user: 'deployment', roles: %w{app}

Having configured what and where to deploy, tasks can be created for each type of action to take.

1. Define the restart task, part of the deploy recipe (ruby namespace):

namespace :deploy do desc 'Restart application' task :restart do on roles(:app), in: :sequence, wait: 5 do within release_path do  execute "npm", "stop", "; true"  execute "npm", "start"

      end

    end

  end

2. Called after the publishing task:

 after :publishing, :restart

3. Define an npminstall task, required for installing npm modules:

 task :npminstall do  on roles(:app) do  within release_path do  execute "npm", "install"  end  end

  end

4. Called before a restart task:

 before :restart, :npminstall

5. Close namespace definition:

end

Tasks above ensure proper actions for a production server. On the staging server however, we want to run tests, prior to making a server restart:

1. Extend the deploy recipe defined above, and add the following:

namespace :deploy do


  desc 'Run tests, prior to an application restart.'

  task :test do

    on roles(:app), in: :sequence, wait: 5 do

      within release_path do

        execute "npm", "test"

      end

    end

  end


  after :npminstall, :test

end

Calling it, after the npminstall task, and prior to the restart task.

How does Capistrano authenticate to remote servers?

Capistrano supports various SSH authentications models. However, to ensure that unwanted eyes do not view ssh passwords for live servers, capistrano shell users should have their public ssh keys added to remote servers. 

Resulting code available here – with inline comments added:

1. config/staging.db

# Application role.

role :app, %w{deployment@staging.hostname.com}

# Target server(s).

server 'staging.hostname.com', user: 'deployment', roles: %w{app}

# Extend the deploy recipe.

namespace :deploy do

  desc 'Run tests, prior to an application restart.'

  task :test do

    on roles(:app), in: :sequence, wait: 5 do

      within release_path do

        execute "npm", "test"

      end

    end

  end

  after :npminstall, :test

end

2. config/production.rb

# Application role.

role :app, %w{deployment@hostname.com}

# Target server(s).

server 'hostname.com', user: 'root', roles: %w{app}

3. deploy.rb

# config valid only for Capistrano 3.1 lock '3.2.1' # Application, and repository settings. set :application, ‘ApplicationName’ set :repo_url, 'http://hostname.com/repository.git' set :git_http_username, 'GitUsername' set :git_http_password, 'GitPassword' # Deploy path. set :deploy_to, '/path/to/application/directory/' # SCM settings. set :scm, :git # Output format and log level: set :format, :pretty set :log_level, :info # Common restart and npminstall tasks. namespace :deploy do desc 'Restart application' task :restart do on roles(:app), in: :sequence, wait: 5 do within release_path do  execute "npm", "stop", "; true"  execute "npm", "start" end end end after :publishing, :restart task :npminstall do  on roles(:app) do  within release_path do  execute "npm", "install"  end  end

  end

  before :restart, :npminstall

end

How to run these tasks, without Jenkins?

Issue the following commands:

1. Full production deployment:

cap production deploy

2. Production rollback:

cap production deploy:rollback

3. Restart your server(s):

cap production deploy:restart

Same tasks can be executed against the staging server, by replacing production with staging. Tests can be run individually using:

cap staging deploy:test

Results of tests can be viewed by changing the log level to debug.