ricodigo-capistrano-recipes 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|