ricodigo-capistrano-recipes 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +76 -0
- data/LICENSE +20 -0
- data/README.rdoc +88 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/generators/app.bluepill.erb +35 -0
- data/generators/nginx.conf.erb +185 -0
- data/generators/unicorn.rb.erb +65 -0
- data/lib/helpers.rb +88 -0
- data/lib/recipes/application.rb +68 -0
- data/lib/recipes/bluepill.rb +66 -0
- data/lib/recipes/bundler.rb +14 -0
- data/lib/recipes/db.rb +97 -0
- data/lib/recipes/deploy.rb +65 -0
- data/lib/recipes/hooks.rb +14 -0
- data/lib/recipes/log.rb +42 -0
- data/lib/recipes/nginx.rb +55 -0
- data/lib/recipes/symlinks.rb +27 -0
- data/lib/recipes/unicorn.rb +80 -0
- data/lib/ricodigo_capistrano_recipes.rb +6 -0
- data/ricodigo-capistrano-recipes.gemspec +87 -0
- metadata +212 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
rails_root = '<%= "#{deploy_to}/current" %>'
|
2
|
+
rails_env = '<%= environment %>'
|
3
|
+
pid_file = '<%= unicorn_pid %>'
|
4
|
+
socket_file= '<%= unicorn_socket %>'
|
5
|
+
log_file = '<%= deploy_to %>/current/log/unicorn.log'
|
6
|
+
username = '<%= unicorn_user %>'
|
7
|
+
group = '<%= unicorn_group %>'
|
8
|
+
old_pid = pid_file + '.oldbin'
|
9
|
+
|
10
|
+
|
11
|
+
working_directory rails_root
|
12
|
+
|
13
|
+
timeout <%= unicorn_workers_timeout %>
|
14
|
+
|
15
|
+
worker_processes <%= unicorn_workers %>
|
16
|
+
|
17
|
+
# Listen on a Unix data socket
|
18
|
+
listen socket_file, :backlog => 1024
|
19
|
+
pid pid_file
|
20
|
+
|
21
|
+
stderr_path log_file
|
22
|
+
stdout_path log_file
|
23
|
+
|
24
|
+
preload_app true
|
25
|
+
|
26
|
+
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
|
27
|
+
|
28
|
+
before_fork do |server, worker|
|
29
|
+
if File.exists?(old_pid) && server.pid != old_pid
|
30
|
+
pid = File.read(old_pid).to_i
|
31
|
+
begin
|
32
|
+
Process.kill("QUIT", pid)
|
33
|
+
Process.kill(0, pid)
|
34
|
+
Process.wait
|
35
|
+
rescue Errno::ECHILD, Errno::ESRCH => e
|
36
|
+
$stderr.puts ">> Process #{pid} has stopped"
|
37
|
+
rescue Errno::ENOENT => e
|
38
|
+
$stderr.puts ">> Error killing previous instance. #{e.message}"
|
39
|
+
# someone else did our job for us
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
after_fork do |server, worker|
|
46
|
+
begin
|
47
|
+
uid, gid = Process.euid, Process.egid
|
48
|
+
|
49
|
+
target_uid = File.stat(Rails.root).uid
|
50
|
+
user = Etc.getpwuid(target_uid).name
|
51
|
+
|
52
|
+
target_gid = File.stat(Rails.root).gid
|
53
|
+
group = Etc.getgrgid(target_gid).name
|
54
|
+
|
55
|
+
worker.tmp.chown(target_uid, target_gid)
|
56
|
+
if uid != target_uid || gid != target_gid
|
57
|
+
Process.initgroups(user, target_gid)
|
58
|
+
Process::GID.change_privilege(target_gid)
|
59
|
+
Process::UID.change_privilege(target_uid)
|
60
|
+
end
|
61
|
+
rescue => e
|
62
|
+
STDERR.puts "cannot change privileges on #{Rails.env} environment"
|
63
|
+
STDERR.puts " #{e}"
|
64
|
+
end
|
65
|
+
end
|
data/lib/helpers.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# =========================================================================
|
2
|
+
# These are helper methods that will be available to your recipes.
|
3
|
+
# =========================================================================
|
4
|
+
|
5
|
+
# automatically sets the environment based on presence of
|
6
|
+
# :stage (multistage gem), :rails_env, or RAILS_ENV variable; otherwise defaults to 'production'
|
7
|
+
def environment
|
8
|
+
if exists?(:stage)
|
9
|
+
stage
|
10
|
+
elsif exists?(:rails_env)
|
11
|
+
rails_env
|
12
|
+
elsif(ENV['RAILS_ENV'])
|
13
|
+
ENV['RAILS_ENV']
|
14
|
+
else
|
15
|
+
"production"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_using_nginx
|
20
|
+
is_using('nginx',:web_server)
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_using_passenger
|
24
|
+
is_using('passenger',:app_server)
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_using_unicorn
|
28
|
+
is_using('unicorn',:app_server)
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_app_monitored?
|
32
|
+
is_using('bluepill', :monitorer)
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_using(something, with_some_var)
|
36
|
+
exists?(with_some_var.to_sym) && fetch(with_some_var.to_sym).to_s.downcase == something
|
37
|
+
end
|
38
|
+
|
39
|
+
# Path to where the generators live
|
40
|
+
def templates_path
|
41
|
+
expanded_path_for('../generators')
|
42
|
+
end
|
43
|
+
|
44
|
+
def docs_path
|
45
|
+
expanded_path_for('../doc')
|
46
|
+
end
|
47
|
+
|
48
|
+
def expanded_path_for(path)
|
49
|
+
e = File.join(File.dirname(__FILE__),path)
|
50
|
+
File.expand_path(e)
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_config(file)
|
54
|
+
require 'erb' #render not available in Capistrano 2
|
55
|
+
template=File.read(file) # read it
|
56
|
+
return ERB.new(template).result(binding) # parse it
|
57
|
+
end
|
58
|
+
|
59
|
+
# =========================================================================
|
60
|
+
# Prompts the user for a message to agree/decline
|
61
|
+
# =========================================================================
|
62
|
+
def ask(message, default=true)
|
63
|
+
Capistrano::CLI.ui.agree(message)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Generates a configuration file parsing through ERB
|
67
|
+
# Fetches local file and uploads it to remote_file
|
68
|
+
# Make sure your user has the right permissions.
|
69
|
+
def generate_config(local_file,remote_file)
|
70
|
+
temp_file = '/tmp/' + File.basename(local_file)
|
71
|
+
buffer = parse_config(local_file)
|
72
|
+
File.open(temp_file, 'w+') { |f| f << buffer }
|
73
|
+
upload temp_file, remote_file, :via => :scp
|
74
|
+
`rm #{temp_file}`
|
75
|
+
end
|
76
|
+
|
77
|
+
# =========================================================================
|
78
|
+
# Executes a basic rake task.
|
79
|
+
# Example: run_rake log:clear
|
80
|
+
# =========================================================================
|
81
|
+
def run_rake(task)
|
82
|
+
run "cd #{current_path} && rake #{task} RAILS_ENV=#{environment}"
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def rvmsudo(task)
|
87
|
+
run "cd #{current_path} && rvmsudo #{task}"
|
88
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
Capistrano::Configuration.instance.load do
|
2
|
+
# User settings
|
3
|
+
set :user, 'app' unless exists?(:user)
|
4
|
+
set :group,'app' unless exists?(:group)
|
5
|
+
|
6
|
+
# Server settings
|
7
|
+
set :app_server, :unicorn unless exists?(:app_server)
|
8
|
+
set :web_server, :nginx unless exists?(:web_server)
|
9
|
+
set :runner, user unless exists?(:runner)
|
10
|
+
set :application_port, 80 unless exists?(:application_port)
|
11
|
+
|
12
|
+
set :application_uses_ssl, true unless exists?(:application_uses_ssl)
|
13
|
+
set :application_port_ssl, 443 unless exists?(:application_port_ssl)
|
14
|
+
|
15
|
+
# Database settings
|
16
|
+
set :database, :mongodb unless exists?(:database)
|
17
|
+
|
18
|
+
# SCM settings
|
19
|
+
set :scm, :git
|
20
|
+
set :branch, 'master' unless exists?(:branch)
|
21
|
+
set :deploy_to, "/home/#{user}/rails/#{application}"
|
22
|
+
set :deploy_via, :checkout
|
23
|
+
set :keep_releases, 3
|
24
|
+
set :run_method, :run
|
25
|
+
set :git_enable_submodules, true
|
26
|
+
set :git_shallow_clone, 1
|
27
|
+
set :rails_env, 'production' unless exists?(:rails_env)
|
28
|
+
|
29
|
+
# Git settings for capistrano
|
30
|
+
default_run_options[:pty] = true
|
31
|
+
ssh_options[:forward_agent] = true
|
32
|
+
|
33
|
+
# RVM settings
|
34
|
+
set :using_rvm, true unless exists?(:using_rvm)
|
35
|
+
|
36
|
+
if using_rvm
|
37
|
+
$:.unshift(File.expand_path('./lib', ENV['rvm_path'])) # Add RVM's lib directory to the load path.
|
38
|
+
require "rvm/capistrano" # Load RVM's capistrano plugin.
|
39
|
+
|
40
|
+
# Sets the rvm to a specific version (or whatever env you want it to run in)
|
41
|
+
set :rvm_ruby_string, '1.9.2' unless exists?(:rvm_ruby_string)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Daemons settings
|
45
|
+
# The unix socket that unicorn will be attached to.
|
46
|
+
# Also, nginx will upstream to this guy.
|
47
|
+
# The *nix place for socks is /var/run, so we should probably put it there
|
48
|
+
# Make sure the runner can access this though.
|
49
|
+
set :sockets_path, "/var/run/#{application}" unless exists?(:sockets_path)
|
50
|
+
|
51
|
+
# Just to be safe, put the pid somewhere that survives deploys. shared/pids is
|
52
|
+
# a good choice as any.
|
53
|
+
set(:pids_path) { File.join(shared_path, "pids") } unless exists?(:pids_path)
|
54
|
+
|
55
|
+
set :monitorer, 'bluepill' unless exists?(:monitorer)
|
56
|
+
|
57
|
+
# Application settings
|
58
|
+
set :shared_dirs, %w(config config/pills uploads backup bundle tmp sockets pids log system) unless exists?(:shared_dirs)
|
59
|
+
|
60
|
+
namespace :app do
|
61
|
+
task :setup, :roles => :app do
|
62
|
+
commands = shared_dirs.map do |path|
|
63
|
+
"if [ ! -d '#{path}' ]; then mkdir -p #{path}; fi;"
|
64
|
+
end
|
65
|
+
run "cd #{shared_path}; #{commands.join(' ')}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
Capistrano::Configuration.instance.load do
|
2
|
+
set(:bluepill_local_config) { "#{templates_path}/app.bluepill.erb" } unless exists?(:nginx_local_config)
|
3
|
+
set(:bluepill_remote_config) { "#{shared_path}/config/pills/#{application}.pill" } unless exists?(:nginx_remote_config)
|
4
|
+
|
5
|
+
namespace :bluepill do
|
6
|
+
desc "|capistrano-recipes| Parses and uploads nginx configuration for this app."
|
7
|
+
task :setup, :roles => :app , :except => { :no_release => true } do
|
8
|
+
generate_config(bluepill_local_config, bluepill_remote_config)
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "|capistrano-recipes| Install the bluepill monitoring tool"
|
12
|
+
task :install, :roles => [:app] do
|
13
|
+
sudo "gem install bluepill"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "|capistrano-recipes| Stop processes that bluepill is monitoring and quit bluepill"
|
17
|
+
task :quit, :roles => [:app] do
|
18
|
+
args = exists?(:options) ? options : ''
|
19
|
+
begin
|
20
|
+
rvmsudo "bluepill stop #{args}"
|
21
|
+
rescue
|
22
|
+
puts "Bluepill was unable to finish gracefully all the process"
|
23
|
+
ensure
|
24
|
+
rvmsudo "bluepill quit"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "|capistrano-recipes| Load the pill from {your-app}/config/pills/{app-name}.pill"
|
29
|
+
task :init, :roles =>[:app] do
|
30
|
+
rvmsudo "bluepill load #{shared_path}/config/pills/#{application}.pill"
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "|capistrano-recipes| Starts your previous stopped pill"
|
34
|
+
task :start, :roles =>[:app] do
|
35
|
+
args = exists?(:options) ? options : ''
|
36
|
+
app = exists?(:app) ? app : application
|
37
|
+
rvmsudo "bluepill #{app} start #{args}"
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "|capistrano-recipes| Stops some bluepill monitored process"
|
41
|
+
task :stop, :roles =>[:app] do
|
42
|
+
args = exists?(:options) ? options : ''
|
43
|
+
app = exists?(:app) ? app : application
|
44
|
+
rvmsudo "bluepill #{app} stop #{args}"
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "|capistrano-recipes| Restarts the pill from {your-app}/config/pills/{app-name}.pill"
|
48
|
+
task :restart, :roles =>[:app] do
|
49
|
+
args = exists?(:options) ? options : ''
|
50
|
+
app = exists?(:app) ? app : application
|
51
|
+
rvmsudo "bluepill #{app} restart #{args}"
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "|capistrano-recipes| Prints bluepills monitored processes statuses"
|
55
|
+
task :status, :roles => [:app] do
|
56
|
+
args = exists?(:options) ? options : ''
|
57
|
+
app = exists?(:app) ? app : application
|
58
|
+
rvmsudo "bluepill #{app} status #{args}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
after 'deploy:setup' do
|
63
|
+
bluepill.install if Capistrano::CLI.ui.agree("Do you want to install the bluepill monitor? [Yn]")
|
64
|
+
bluepill.setup if Capistrano::CLI.ui.agree("Create bluepill configuration file? [Yn]")
|
65
|
+
end if is_using('bluepill', :monitorer)
|
66
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Capistrano::Configuration.instance.load do
|
2
|
+
namespace :bundler do
|
3
|
+
desc "|capistrano-recipes| Installs bundler gem to your server"
|
4
|
+
task :setup, :roles => :app do
|
5
|
+
run "if ! gem list | grep --silent -e 'bundler'; then #{try_sudo} gem uninstall bundler; #{try_sudo} gem install --no-rdoc --no-ri bundler; fi"
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "|capistrano-recipes| Runs bundle install on the app server (internal task)"
|
9
|
+
task :install, :roles => :app, :except => { :no_release => true } do
|
10
|
+
run "cd #{current_path} && bundle install --deployment --without=development test"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
data/lib/recipes/db.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance.load do
|
4
|
+
namespace :db do
|
5
|
+
namespace :mongodb do
|
6
|
+
desc <<-EOF
|
7
|
+
|capistrano-recipes| Performs a compressed database dump. \
|
8
|
+
WARNING: This locks your tables for the duration of the mongodump.
|
9
|
+
Don't run it madly!
|
10
|
+
EOF
|
11
|
+
task :dump, :roles => :db, :only => { :primary => true } do
|
12
|
+
prepare_from_yaml
|
13
|
+
run "mongodump -u #{db_user} -p #{db_pass} -h #{db_host} --port #{db_port} -d #{db_name} #{db_backup_path}" do |ch, stream, out|
|
14
|
+
puts out
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "|capistrano-recipes| Restores the database from the latest compressed dump"
|
19
|
+
task :restore, :roles => :db, :only => { :primary => true } do
|
20
|
+
prepare_from_yaml
|
21
|
+
run "mongorestore --drop -d #{db_name} #{db_backup_path}/#{db_name}" do |ch, stream, out|
|
22
|
+
puts out
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "|capistrano-recipes| Downloads the compressed database dump to this machine"
|
27
|
+
task :fetch_dump, :roles => :db, :only => { :primary => true } do
|
28
|
+
prepare_from_yaml
|
29
|
+
download db_remote_file, db_local_file, :via => :scp
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sets database variables from remote database.yaml
|
33
|
+
def prepare_from_yaml
|
34
|
+
set(:db_backup_path) { "#{shared_path}/backup/" }
|
35
|
+
set(:db_local_file) { "tmp/#{db_file}" }
|
36
|
+
set(:db_user) { db_config[rails_env]["username"] }
|
37
|
+
set(:db_pass) { db_config[rails_env]["password"] }
|
38
|
+
set(:db_host) { db_config[rails_env]["host"] }
|
39
|
+
set(:db_name) { db_config[rails_env]["database"] }
|
40
|
+
end
|
41
|
+
|
42
|
+
def db_config
|
43
|
+
@db_config ||= fetch_db_config
|
44
|
+
end
|
45
|
+
|
46
|
+
def fetch_db_config
|
47
|
+
require 'yaml'
|
48
|
+
file = capture "cat #{shared_path}/config/mongoid.yml"
|
49
|
+
db_config = YAML.load(file)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "|capistrano-recipes| Create mongoid.yml in shared path with settings for current stage and test env"
|
54
|
+
task :setup do
|
55
|
+
set(:db_host) { Capistrano::CLI.ui.ask("Enter #{environment} database host:") {|q|q.default = "localhost"} }
|
56
|
+
set(:db_port) { Capistrano::CLI.ui.ask("Enter #{environment} database port:", Integer){|q| q.default = 27017 } }
|
57
|
+
set(:db_user) { Capistrano::CLI.ui.ask "Enter #{environment} database username:" }
|
58
|
+
set(:db_pass) { Capistrano::CLI.password_prompt "Enter #{environment} database password:" }
|
59
|
+
set(:db_safe_mode) { Capistrano::CLI.ui.agree "Enable safe mode on #{environment} database? [Yn]:" }
|
60
|
+
|
61
|
+
db_config = ERB.new <<-EOF
|
62
|
+
defaults: &defaults
|
63
|
+
host: #{db_host}
|
64
|
+
port: #{db_port}
|
65
|
+
<% if db_user && !db_user.empty? %>
|
66
|
+
username: #{db_user}
|
67
|
+
password: #{db_pass}
|
68
|
+
<% end %>
|
69
|
+
autocreate_indexes: false
|
70
|
+
allow_dynamic_fields: true
|
71
|
+
include_root_in_json: false
|
72
|
+
parameterize_keys: true
|
73
|
+
persist_in_safe_mode: #{db_safe_mode}
|
74
|
+
raise_not_found_error: true
|
75
|
+
reconnect_time: 3
|
76
|
+
|
77
|
+
development:
|
78
|
+
<<: *defaults
|
79
|
+
database: #{application}-development
|
80
|
+
|
81
|
+
test:
|
82
|
+
<<: *defaults
|
83
|
+
database: #{application}-test
|
84
|
+
|
85
|
+
production:
|
86
|
+
<<: *defaults
|
87
|
+
database: #{application}-production
|
88
|
+
EOF
|
89
|
+
|
90
|
+
put db_config.result(binding), "#{shared_path}/config/mongoid.yml"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
after "deploy:setup" do
|
95
|
+
db.setup if Capistrano::CLI.ui.agree("Create mongoid.yml in app's shared path? [Yn]")
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
Capistrano::Configuration.instance.load do
|
2
|
+
set :shared_children, %w(system log pids config)
|
3
|
+
|
4
|
+
namespace :deploy do
|
5
|
+
desc "|capistrano-recipes| Deploy it, github-style."
|
6
|
+
task :default, :roles => :app, :except => { :no_release => true } do
|
7
|
+
update
|
8
|
+
restart
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "|capistrano-recipes| Destroys everything"
|
12
|
+
task :seppuku, :roles => :app, :except => { :no_release => true } do
|
13
|
+
run "rm -rf #{current_path}; rm -rf #{shared_path}"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "|capistrano-recipes| Create shared dirs"
|
17
|
+
task :setup_dirs, :roles => :app, :except => { :no_release => true } do
|
18
|
+
commands = shared_dirs.map do |path|
|
19
|
+
"mkdir -p #{shared_path}/#{path}"
|
20
|
+
end
|
21
|
+
run commands.join(" && ")
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "|capistrano-recipes| Uploads your local config.yml to the server"
|
25
|
+
task :configure, :roles => :app, :except => { :no_release => true } do
|
26
|
+
generate_config('config/config.yml', "#{shared_path}/config/config.yml")
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "|capistrano-recipes| Setup a GitHub-style deployment."
|
30
|
+
task :setup, :roles => :app, :except => { :no_release => true } do
|
31
|
+
run "rm -rf #{current_path}"
|
32
|
+
setup_dirs
|
33
|
+
run "git clone #{repository} #{current_path}"
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "|capistrano-recipes| Update the deployed code."
|
37
|
+
task :update_code, :roles => :app, :except => { :no_release => true } do
|
38
|
+
run "cd #{current_path}; git fetch origin; git reset --hard #{branch}"
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "|capistrano-recipes| Alias for symlinks:make"
|
42
|
+
task :symlink, :roles => :app, :except => { :no_release => true } do
|
43
|
+
symlinks.make
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "|capistrano-recipes| Remote run for rake db:seed"
|
47
|
+
task :migrate, :roles => :app, :except => { :no_release => true } do
|
48
|
+
run "cd #{current_path}; bundle exec rake RAILS_ENV=#{rails_env} db:seed"
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "|capistrano-recipes| [Obsolete] Nothing to cleanup when using reset --hard on git"
|
52
|
+
task :cleanup, :roles => :app, :except => { :no_release => true } do
|
53
|
+
#nothing to cleanup, we're not working with 'releases'
|
54
|
+
puts "Nothing to cleanup, yay!"
|
55
|
+
end
|
56
|
+
|
57
|
+
namespace :rollback do
|
58
|
+
desc "|capistrano-recipes| Rollback , :except => { :no_release => true }a single commit."
|
59
|
+
task :default, :roles => :app, :except => { :no_release => true } do
|
60
|
+
set :branch, "HEAD^"
|
61
|
+
deploy.default
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|