Rails application deployment automation with mina


In this post, I will introduce a really fast deployment tool - Mina, and show you how to deploy a rails application which runs on unicorn with nginx, also I’ll show you how to organize your mina deployment tasks.


All code in this post can be find here.

About mina

Mina is a readlly fast deployer and server automation tool”, this how the team who built it describes it. The concept behind Mina is to connect to a remote server and executes a set of shell command that you define in a local deployment file(config/deploy.rb). One of its outstanding feature is it is really fast because all bash instructions will be performed in one SSH connection.


To use mina automating deployment, you need to get the following things ready.

  1. a remote sever
  2. create a user for deployment (e.g. deployer) and add this user into sudoer list.
  3. generate a ssh key pair, add the generated public key into GitHub.
  4. create a deployment target fold on the remove server(e.g. ‘/var/www/example.com’)

once you got these things done, run mina init in your project directory, this will generate a deployment file - config/deploy.rb, then set the server address, deployment user, deployment target and other settings in deployment file, like the following:

set :user, 'deployer'
set :domain, ENV['on'] == 'prod' ? '<prod ip>' : '<qa ip>'
set :deploy_to, '/var/www/example.com'
set :repository, 'git@github.com:your_company/sample.git'
set :branch, 'master'


Then run mina setup, this will create deployment folders, which looks like this:

/var/www/example.com/     # The deploy_to path
|-  releases/              # Holds releases, one subdir per release
|   |- 1/
|   |- 2/
|   |- 3/
|   '- ...
|-  shared/                # Holds files shared between releases
|   |- logs/               # Log files are usually stored here
|   `- ...
'-  current/               # A symlink to the current release in releases/


It is very common to setup a new server and deploy application on it, it will be good if we can automating this process, here comes the provision task:

    task :provision do
      # add nginx repo
      invoke :'nginx:add_repo'

      queue  "sudo yum install -y git gcc gcc-c++* make openssl-devel mysql-devel curl-devel nginx sendmail-cf ImageMagick"

      #install rbenv
      queue  "source ~/.bash_profile"
      queue  "#{exists('rbenv')} || git clone https://github.com/sstephenson/rbenv.git ~/.rbenv"
      queue  "#{exists('rbenv')} || git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build"
      queue  "#{exists('rbenv')} || echo 'export PATH=\"$HOME/.rbenv/bin:$PATH\"' >> ~/.bash_profile && source ~/.bash_profile"

      #install ruby
      queue  "#{ruby_exists} || RUBY_CONFIGURE_OPTS=--with-openssl-dir=/usr/bin rbenv install #{ruby_version}"

      #install bundle
      queue  "#{ruby_exists} || rbenv local 2.0.0-p247"
      queue  "#{exists('gem')} || gem install bundle --no-ri --no-rdoc"

      #set up deploy to
      queue "sudo mkdir -p #{deploy_to}"
      queue "sudo chown -R #{user} #{deploy_to}"


    #helper method
    def ruby_exists
    "rbenv versions | grep #{ruby_version} >/dev/null 2>&1"

    def exists cmd
      "command -v #{cmd}>/dev/null 2>&1"

to be able run this taks multi times, I create some helper method detecting whether an executable exists or not. e.g. ruby_exists, exits('gem') these helper method will return if the executable exits, otherwise, it will run next command to install the executable.

With this task, you can get a server ready for deployment in seveural minutes.


Once the server is provisioned, you can deploy your application with mina deploy, here is a typical deploy task:

    desc "Deploys the current version to the server."
    task :deploy => :environment do
      deploy do
        invoke :'git:clone'   #clone code from github
        invoke :'deploy:link_shared_paths' #linking shared file with latest file we just cloned
        invoke :'bundle:install' #install bundle
        invoke :'rails:db_migrate' #run database migration
        invoke :'rails:assets_precompile' #compile assets
        invoke :'unicorn_and_nginx' #setup nginx and unicorn config
        to :launch do
          queue '/etc/init.d/unicorn_myapp.sh reload' #reload unicorn after deployment succeed

Unicorn and nginx

to run our application on unicorn and nginx, we need to create our own unicorn and nginx configuration file and start nginx and unicorn with our configuration. here is the task:

    desc "Setup unicorn and nginx config"
    task :unicorn_and_nginx do
      queue! "#{file_exists('/etc/nginx/nginx.conf.save')} || sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.save"

      queue! "#{file_exists('/etc/nginx/nginx.conf')} || sudo ln -nfs #{deploy_to}/current/config/nginx.conf /etc/nginx/nginx.conf"

      queue! "#{file_exists('/etc/init.d/unicorn_avalon.sh')} || sudo ln -nfs #{deploy_to}/current/scripts/unicorn.sh /etc/init.d/unicorn_myapp.sh"


With these tasks: mina init, mina provision, mina deploy, mina helps you deploying easily with less mistake, have fun with mina!

Thoughts on design a RESTful API on RoR stack

Recently I’m working a RoR stack RESTful API project, I was involved in proposing tech stack, architecture, deployment pipeline. There was many thoughts in this progress, so I wrote down our thoughts here, in case it may help you when you met a similar question.

Tech Stack

There’re bunch of api framework out there in ruby stack, such as Grape, rails-api, Sinatra. I’ll share my understanding here:


Sinatra providing a lightweight, simple DSL to creating web application. we can create a web/api application with sinatra within 15 seconds! the downside is, it is not a full stack framework, it requires us to combine sinatra with other frameworks. for example, we have a backend database for storing and retrieving information, you need to interating sinatra with a orm framework(e.g. ActivateRecrod or DataMapper).if we want to render info on a web page, we need to integrate a view template engine.


Grape is a ruby framework, which is designed for creating RESTful api service. it have sevural great features for creating RESTFul api. for example, api verisoning, automatic api doc generation, etc. Similar to sinatra, it is not a full stack framework, it requires some integration work. BTW, Grape can be easily integrated into any Rack application(e.g. sinatra, rails).


Rails::API is a subset of a normal Rails application, created for applications that don’t require all functionality that a complete Rails application provides. It is a bit more lightweight, and consequently a bit faster than a normal Rails application.

In the end, we choose Rails::API as our tech stack for the following reason:

  • it is a fullstack framework, including ORM, db migration, validation, etc all in one place.
  • we can leveage some rails’s feature, e.g. generator, db migration.
  • it is a subset of rails, is designed for creating a API application.
  • rails’s REST conversion.

API Design

Content Type Negotiation

A most important part of designing RESTful API is content-type negotiation. the content type negotiation can be both in request/response header and url suffix:

in request header, Content-Type indicating content type of data in request body, Accept telling server what kind of content type the client expected to accept.

in response header, Content-Type indicating content type of data in the response body.

Also, request to /users/2.json expecting the server return user info in JSON format, but request to /users/2.xml expecting get a XML response.

there’re several kind of standard content type, e.g. application/json, application/xml. People can define their own content type, e.g. application/vnd.google-apps.script+json
My feeling is, if your api is a public endpoint, you’d better define your own content type.

let’s take a example, a authentication api expecting get the following request data:

  email: "sample@gmail.com",
 password: "my_password"

you have two content type choices: application/json and application/vnd.mycompany.credential+json, if this is a public api, I’ll chose the customized content type - application/vnd.mycompany.credential+json, or this is an internal api, I’ll chose the standard content type - application/json. I made this choice by considering the following reasons:

  Pros Cons
Customized content type Could define a json schema, api server and client could use this json schema ensure request is processable. adding complexity
Standard content type Simple and straightforward no validation to request data, any unexpected message could be send to server

Code conversion

I struggled with workflow management when I play the very first story in this project, the problem is, it is very common and business workflow have more then two exit points. e.g. a login workflow, the possiable exit points are:

  1. username password matched, login succeed
  2. username password mimatched, login failed
  3. user is inactived, login failed
  4. user is locked because of too many login failures, login failed.

in a rails project, it is very important to keep your controllers clean, controller’s only responsibility is passing parameters and routating, so it is better is to put these business logic into Model or Service. here comes the problem: how can we let the controller konw the extact reason of failure without put the business logic in controller? return value can not fufill this requirement, so here come out our solution: modeling these exception exit point with ruby exception, handing different exceptions in controller with different exception handler. and we found it makes the controller and model much more cleaner. let’s have a look at this example:

Before, the controller was messy:

      #in controller
      def create
        user = User.authorize(params[:email], params[:password])
        if user.nil?
          render :status => 401, :json => {:error => "Unauthorized"}
        elsif !user.activated?
          render :status => 403, :json => {:error => "user hasn't been activated"}
          response.headers["Token"]= Session.generate_token(user)
          render :status => 201, :nothing => true

After, controller is much cleaner

      #in controller
      rescue_from InactiveUserException, with: :user_is_inactive
      rescue_from InvalidCredentialException, with: :invaild_credential
      rescue_from UserNotFoundException, with: :user_not_found

      def create
        login_session = User.login(params[:email], params[:password])
        response.headers["Token"]= login_session.token
        render :status => :created, :nothing => true

Error code

It is very common to return failure reason when API call failed. Even we can return failure reason in plian english, as an API provider, we shouldn’t assume that API client will use error message we provided, it’s better to return a structured failure reason which can be parsed by API client. let’s take a look at example from Github API:

      "message": "Validation Failed",
        "errors": [
          "resource": "Issue",
          "field": "title",
          "code": "missing_field"

failure reason was returned as an JSON object, resource representing what kind of resource is requested, field indicates which field fails api call, code indicating the exact faliure reason.

Another thing I want to highlight is - do not define numeric error code, it will be a nightmare for you and your client. a better solution is define meanningful error code, like missing_field, too_long, etc.


RESTful api don’t have frontend, so it is very important to make your document friendly. Aslo, it is very common that a developer changing code but forgot to change api doc, so it would be great if we can generating api document automaticly. considering we have a well formed test suite(or spec) for the api, why cann’t we just extract information from these tests/specs and generating document automaticly. Acturally there’re some gems trying to solve this problem: apipie-rails, swagger-ui. we’re using apipie-rails, but we’ve found some missing features in apipie-rails. e.g. it can not record extract request and response headers, while headers play a important rule in a RESTful api.


We have two kind of tests in this project: integration test and unit test.

integration tests test the api from end point, it is a end to end test. we use requesting rspec define this test.

unit tests test different layer. hints: only stub method on the boundary of each layer.

integration test

Make tests less flakey by using contract-based testing instead of hitting live endpoints


we could specify api version in either url or http request header. In theory, verion number in url is not restful, any thoughts, please let me know


We deploy our api on amazon EC2.

  • Provision: creating new node from a customized AMI (with many required dependence installed).

  • Build pipeline:

    the CI will build a rpm package one all test passed, then this package will be pushed to S3, after that, this package will be installed on the provisioned node.

Delimited Continuations(WIP)



Delimited Continuations

Scala 2.8中引入的Delimited Continuation,可以被用来实现一些有趣的流程控制结构。



  • 技术实现
  • Continuation Passing Style
  • 嵌套CPS
  • 全部CPS和有界限的Continuations
  • 如何使用
  • CPS with Return
  • Reset和Shift
  • 注解(WIP)
  • 嵌套shift(WIP)
  • 控制结构的限制(WIP)
  • 建议(WIP)
  • 资源(WIP)


为了使用scala的delimited continuations,你的scala版本必须高于2.8版本(译者注:目前的scala的最新版本是2.10.2),并且你还需要使用scala的continuation编译器插件。你可以通过在命令行制定一个参数来在编译时(compiling)和运行时(runtime)启用此插件:

scalac -P:continuations:enable ${sourcefiles}
scala -P:continuations:enable ${classname} 在代码中,你还需要引入scala中对continuation的支持。

import scala.util.continuations._


<console>:6 error:not found value reset
		^ #### Continuation Passing Style 为了理解scala的delimited continuations,首先你需要理解“continuation passing style”这个术语。


def main{

def sub(){
} `pre`和`post`表示在main方法中在调用sub方法之前、之后的代码,`substuff`表示sub方法中的所有代码。



def main(m: M): Z = {
	val x: X = pre(m)
	val y: Y = sub(m, x)
	val z: Z = post(m, x, y)
	return z

def sub(m: M, x: X): Y {
	val y: Y = substuff(m, x)
	return y


def main(m: M) {
	val x: X = pre(m)
	val z: Z = sub(m, x, {post(m, x, _)})
	return z

def sub(m: M, x: X, subCont: Y => Z) {
	val y: Y = substuff(m, x)
	val z: Z = subCont(y)
	return z
} 当把包含`post`代码段传递给`sub`时,Scala生成一个记录了包含`post`执行上下文(当时的m,x值)的闭包,当这个闭包将来被执行时,可以获得的当时的执行上下文。


val z: Z = sub(m, x, {y: Y => post(m, x, y)}) CPS的要点是“不要用`return`”。不像`direct style`那样调用一个子函数,待子函数执行完毕后返回给主函数,我们传递一个子函数执行结束后要被执行的continuation给子函数。




def prog(m: M) {
	val z: Z = main(m)
} 现在,我们可以对`prog`和`main`做同样的CPS变换:

def prog(m: M) {
	main(m, {z: Z => {

def main(m: M, mainCont: Z => Unit): Unit = {
	val x: X = pre(m)
	val z: Z = sub(m, x, post(m, x, _))
} 我们仍然在`sub`中使用了`return`,并且在`main`中使用`sub`的返回值。为了解决这个问题,我们需要把`mainCont`做为一个`continuation`传递给`sub`,修改后的`main`和`sub`如下:

def main(m: M, mainCont: Z => Unit): Unit = {
	val x: X = pre(m)
	sub(m, x, y: Y => {
		val z: Z = post(m, x, y)

def sub(m:M,x:X, subCont: (Y) => Unit) {
	val y:Y = substuff(m,x)




Full versus Delimited Continuations

在上面的讨论中,我们假设整个程序都被转换成CPS,这就是传统的CPS,我们也称之为Full Continutaitons。然而, 在原生不支持CPS的编程语言(例如scala)中使用CPS可能会使代码变得比较混乱, 因此如果能够把CPS的使用限定在特定的范围内是最好不过了。

这就是delimited continuation被发明出来的原因。仅仅留存部分后续要执行的程序,而不是尝试保存整个后续执行的程序。

我们再来看下上面的例子程序,那个prog方法与其他方法唯一的不同就是我们不能返回一个Direct Style value。如果我们移除掉那个System.exit ,我们可以在一个普通的Direct Style代码中调用prog,同时在prog和其子方法中使用CPS,每个方法以传递一个continuation给下一个方法结尾,直到最后一个continuaion被执行,CPS代码结束,控制返回到调用者prog中。

def prog(m: M) {
	main(m, {z: Z => 



CPS的核心能力是:它能够提供一个显式的对象(continuation)来表示后续要执行的代码(或者,在Delimited continuation的情况下,后续要执行的部分代码)。在上面的例子中,我们在sub方的最后执行那个continuation。但是,如果我们不执行这个continuation,而是把它保存起来,例如一个singleton,会发生什么有意思的事情呢?

object ContinuationSaver{
	var savedContinuation: Option[() => Unit] = None
	def save(saveCont: => Unit) = {savedContinuation = Some(saveCont _)}

def sub(m: M, x: X, subCont: Y => Unit) {
	val y: Y = substuff(m, x)
	ContinuationSaver.save {subCont(y)}
} `sub`保存了continuation后,`sub`就执行完毕,实际上整个delimited continuation已经执行完毕;控制已经返回到调用者`prog`中。但是在`ContinuationSaver`中我们仍然保存着那个记录着后续代码的continuation。实际上,我们已经把这些后续代码放到了挂起状态,并且在以后的任意时间重新执行。


CPS with Return

在纯粹的CPS中是没有return的,但是Scala中有return,甚至在使用CPS的时候。在上一章节中我用了“控制回到了调用者prog”这样的术语。这跟普通的方法调用一样 —— 所有的中间方法都返回到自己的调用者,直到方法调用栈pop到第一个CPS方法调用。我假设所有的CPS方法都不返回值(即返回Unit),但是没有任何规则规定我们不能从CPS方法中返回值。

If we add a return value to the transformed code, this is not something we can get as a result of using the above transformation technique.

上面的例子展示了从一个Direct Style代码到CPS代码的转换过程,上述的转换方式总会生产一个返回值为Unit的代码。如果我们给上面转换后的代码加入返回值,通过上述的转换方式是做不到的。

如果给我们的CPS代码加入返回值,会发生什么样的事情呢?在上面的例子里, 子方法的最后一步总是在执行continuaiton,如果我们把这做为默认行为,当我们给CPS代码增加返回值时,这个返回值会沿着CPS方法调用链一直返回到最顶端的CPS方法,在Direct Style代码中表现为最顶端的CPS方法的返回值。当然,任何一个中间的CPS方法都有可能修改或者替换这个返回值。


object ContinuationSaver {
	var numberOfSavedContinuations = 0
	var savedContinuation: Option[() => Unit] = None
	def save(saveCont: => Unit): Int = {
		savedContinuation = Some(saveCont _)
		numberOfSavedContinuations = numberOfSavedContinuations + 1

def sub(m: M, x: X, subCont: Y => Unit):Int = {
	val y: Y = substuff(m, x)
	ContinuationSaver.save {subCont(y)}


def prog(m: M):Int = {
	main(m, { (z: Z) => 

def main(m: M, mainCont: Z => Unit):Int => {
	val x: X = pre(m)
	sub(m, x, 
	{ (y: Y) => 
			val z: Z = post(m, x, y)
} 如果我们想的话,我们可以在`main`中修改从`sub`中返回的值做为`main`自己的返回值,甚至我们可以从`main`中返回一个与`sub`的返回值没有任何关系的值。


Reset and Shift

终于,我们有了足够的背景知识来理解Scala的 resetshift关键字。

Scala中的delimited continuation是由EPFL的Tiark Rompf创建的。在他和Ingo Maier以及Martin Odersky合作的论文Delimited Continuations in Scala有详细描述。下面的资源部分也有Tiark的关于Delimited Continuations的博客。

reset被用于标识delimited contiuation上界,只有reset内部代码才是CPS代码,reset的返回值不是CPS代码。

shift被用于标识delimited continuation的下界,shift内部的代码不是CPS代码,(but it’s untransformed return value is CPS)但是它的未转换的返回值是CPS。当shift执行时,会被传入一个传入一个从它的调用者开始到一个闭合的reset的continuation做为参数。

所以,resetshift是从Direct Style到CPS,从CPS带Direct Style的转换器。所有在resetshift之间的代码都是CPS。所有包含了shift的方法需要被标识为CPS,所有调用CPS方法的方法也需要被标识为CPS,直到遇到一个闭合的reset



reset {
	shift{ k: (Int => Int) =>  k(7) } + 1



reset {
	var r = shift{k: (Int => Int) => k(7)}
	r + 1


reset {
	var r = shift{k: (Int => Int) => k(7)}
	def f(x: Int) => x + 1
} 这个函数`f`就是我们的continuation,它包含了从`shift`块结束到`reset`块结束的所有代码。


	def f(x: Int) = x + 1



reset {
	shift { k: (Int=>Int) => k(k(k(7)))} + 1
} 会得到

reset {
  def f(x:Int) = x + 1
} 我们可以很容易地看出结果是10。


reset {
  shift { k: (Int=>Int) => k(7) } + 1
} * 2



reset {
  shift { k: (Int=>Int) => k(k(k(7))); "done" } + 1
} 并指出这个代码的返回值是“done”。continuation函数被调用了三次,但是其返回值被抛弃了。对这段代码应用我们的代码转换过程,我们会得到下面的结果:

reset {
  def f(x:Int) = x + 1
  f(f(f(7))); "done"
} 从这个代码中很容易看出来为什么返回是“done”。


当你看到一个shift块,并且它的返回值被应用在一个表达式中时,比如上面的那个“shift + 1”的例子,请记住一点,由于代码的转换,这个从shift块中“return”从来没有真正地return过。实际上,当执行到shift块时,shfit块之后的代码被转换成一个continuation并做为参数传递给这个shift块;如果shift块中的代码执行了这个continuation,这个continuation的执行结果就表现为shfit块的返回值。the value which is passed as an argument to the continuation appears as the value being returned from the shift block.因此,传递给shift块中continuation 函数的参数的类型和代码中shfit块的返回值类型是一致的。 continuation 函数的返回值类型和这个shift块外围的reset块的返回值类型是一致的Thus the type of the argument passed to the shift block’s continuation function is the same as the type of the return value of the shift in the source code, and the type of the return value of that continuation function is the same as the type of the return value of the original last value in the reset block that encloses the shift block.


  • 传递给continuation函数的参数类型,和shfit块的语义返回值类型一致。
  • The type of the argument to pass to the continuation, which is the same as the syntactic return type of the shift in the source code.

  • continuation函数的返回值类型,与从shfit块结束开始的代码的返回值类型一致。(例如,从shift块结束到函数结束或者reset块结束之间的代码的返回值类型)这被叫做untransformaed return type

  • The type of the return from the continuation, which is the same as the return type of all of the code that follows the shift block in the source code (i.e. the type of the last value in the block of code between the shift block and the end of the function or reset block containing the shift block). This is called the untransformed return type.

  • shift块中最后一条语句的值,被做为整个函数或者reset块的返回值的类型。这个被称为transformed return type
  • The type of the last value in the shift block, which becomes the type of the return value of the enclosing function or return block. This is called the transformed return type.


def shift[A, B, C](func: ((A => B) => C)): A @scala.util.continuations.cpsParam[B, C]



def reset[B, C](ctx: => B @scala.util.continuations.cpsParam[B, C]): C


C = reset{…; A = shift{k: A => B => …; C}…;B}

在下面这个例子里,A=Int, B =String, C=Boolean:

def is123(n: Int): Boolean = {
		shift{k: (Int => String) =>
		 (k(n) == "123")

####注解(WIP) ####嵌套shift(WIP) ####控制结构的限制(WIP) ####建议(WIP) ####资源(WIP)



目前spring只支持MRI 1.9.3, MRI 2.0.0, Rails 3.2,没有达到要求的人赶紧升级你们的ruby,rails版本吧



  1. pair写了一个测试
  2. 运行测试
  3. 等待
  4. 该我来编写产品代码
  5. 运行测试
  6. 等待
  7. 代码有bug
  8. 测试失败
  9. 修复测试
  10. 运行测试
  11. 等待
  12. 测试通过,yeah!


  • pair很给力,很快就把一个taks实现成一个测试用例
  • 桌子上的水果也很好吃。
  • 。。。

可是,我总觉得有点不爽快,原来是那么多的等待,每运行一次测试,就需要等待十几秒甚至几十秒,每天我会运行上千次测试,这是多大的浪费?做为一个有追求的程序员,我当然不愿意把宝贵的工作时间浪费在这无谓的等待中去 :-)。


有追求的程序员还是大多数,google之后才发现已经有人尝试解决这个问题,如sporkzeus。他们的原理都是预先把rails环境启动起来,后面在运行测试,执行rake task时从这个启动好的进程fork一个进程,在这个进程中执行操作。然而,spork需要修改spec_helper.rb,并且需要单独启动一个server进程,zeus虽然不需要修改项目代码但仍然需要单独启动一个server进程,用起来还不是很爽快。 spring带来了更加易用的方案。


建议把spring安装到rvm的global gemset中去,这样就可以在多个project使用spring


gem install spring



spring rspec

当第一次使用spring运行测试,rake taks, db migration时,spring会自动在后台load rails 环境,因此执行速度也很慢,但是当再次执行时,spring会从先前的进程中fork出load好的rails环境,执行速度就变得飞快!


require 'rspec/autorun'从spec_helper中删掉,否则,spec会被执行两次,而且第二次会由于找不到url helper method而失败。

Failure/Error: visit posts_path
   undefined local variable or method `posts_path' for #<RSpec::Core::ExampleGroup::Nested_2:0x007fcf650718e0>
 # ./spec/integration/posts/posts_spec.rb:10:in `block (2 levels) in <top (required)>'
 # -e:1:in `<main>'

感谢Stefan Wienert的分享。


spring把对项目代码的影响减少到了没有,并且能够去掉加载rails环境的时间,极大地提升rails开发者的效率,是现有rails开发者必不可少的利器。enjoy coding!!!

Deploying Rails App through Capistrano



  1. 创建一个用于部署的用户(eg. deployer)
  2. 安装本地依赖:
  3. 安装配置nginx: yum install nginx
    • config nginx ln -s /path/to/app/config/nginx.conf /etc/nginx/nginx.conf
    • restart nginx service nginx restart



capify .


我使用capistrano-rbenv gem管理production环境的ruby版本,在Gemfile中增加如下一行:

`gem 'capistrano-rbenv'`


require 'capistrano-rbenv'
set :rbenv_ruby_version, '2.0.0-p0'



server '', :web, :app, :db, :primary => true
set 'application', "my-app"
set :repository, 'deployer@github.com:my-app.git'
set :branch, 'master'
set :scm, :git
set :user_sudo, false
set :user, 'deployer'
set :deploy_to, "/home/#{user}/apps/#{application}"
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
cap deploy