capistrano_recipes 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,139 @@
1
+ <% if using_recipe?(:unicorn) %>
2
+ upstream unicorn_<%= application %> {
3
+ server unix:<%= unicorn_socket %> fail_timeout=0;
4
+ }
5
+
6
+ <% end %>
7
+ <% if exists?(:domain_alias) %>
8
+ server {
9
+ server_name <%= domain_alias %>;
10
+ rewrite ^(.*) http://<%= domain %>$1 permanent;
11
+ }
12
+
13
+ <% end %>
14
+ server {
15
+ listen <%= nginx_port %>;
16
+ server_name <%= domain %>;
17
+
18
+ root <%= current_path %>/public;
19
+ access_log <%= shared_path %>/log/access.log main;
20
+ error_log <%= shared_path %>/log/error.log info;
21
+
22
+ # Rewrite all the requests to the maintenance.html
23
+ # page if it exists in the doc root. This is for
24
+ # capistrano's disable web task
25
+ if (-f $document_root/system/maintenance.html) {
26
+ rewrite ^(.*)$ /system/maintenance.html last;
27
+ break;
28
+ }
29
+
30
+ location / {
31
+ # If the file exists as a static file, serve it directly
32
+ # without running all the other rewrite tests on it
33
+ if (-f $request_filename) {
34
+ break;
35
+ }
36
+
37
+ # Check for index.html
38
+ if (-f $request_filename/index.html) {
39
+ rewrite (.*) $1/index.html break;
40
+ }
41
+
42
+ # Check if .html file exists for page caching
43
+ if (-f $request_filename.html) {
44
+ rewrite (.*) $1.html break;
45
+ }
46
+ }
47
+
48
+ <% if using_recipe?(:rails_assets) %>
49
+ # Asset pipeline
50
+ location ^~ /assets/ {
51
+ gzip_static on;
52
+ expires max;
53
+ add_header Cache-Control public;
54
+ }
55
+
56
+ <% end %>
57
+ <% if using_recipe?(:unicorn) %>
58
+ try_files $uri/index.html $uri @unicorn;
59
+ location @unicorn {
60
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
61
+ proxy_set_header Host $http_host;
62
+ proxy_redirect off;
63
+ proxy_pass http://unicorn_<%= application %>;
64
+ }
65
+
66
+ <% end %>
67
+ error_page 500 502 503 504 /500.html;
68
+ location = /500.html {
69
+ root <%= current_path %>/public;
70
+ }
71
+ }
72
+ <% if using_ssl? %>
73
+
74
+ server {
75
+ listen <%= nginx_ssl_port %>;
76
+ server_name <%= domain %>;
77
+
78
+ root <%= current_path %>/public;
79
+ access_log <%= shared_path %>/log/ssl-access.log main;
80
+ error_log <%= shared_path %>/log/ssl-error.log info;
81
+
82
+ ssl on;
83
+ ssl_certificate <%= File.join nginx_ssl_certs_path, nginx_ssl_cert %>;
84
+ ssl_certificate_key <%= File.join nginx_ssl_private_path, nginx_ssl_key %>;
85
+ ssl_session_timeout 5m;
86
+
87
+
88
+ # Rewrite all the requests to the maintenance.html
89
+ # page if it exists in the doc root. This is for
90
+ # capistrano's disable web task
91
+ if (-f $document_root/system/maintenance.html) {
92
+ rewrite ^(.*)$ /system/maintenance.html last;
93
+ break;
94
+ }
95
+
96
+ location / {
97
+ # If the file exists as a static file, serve it directly
98
+ # without running all the other rewrite tests on it
99
+ if (-f $request_filename) {
100
+ break;
101
+ }
102
+
103
+ # Check for index.html
104
+ if (-f $request_filename/index.html) {
105
+ rewrite (.*) $1/index.html break;
106
+ }
107
+
108
+ # Check if .html file exists for page caching
109
+ if (-f $request_filename.html) {
110
+ rewrite (.*) $1.html break;
111
+ }
112
+ }
113
+
114
+ <% if using_recipe?(:rails_assets) %>
115
+ # Asset pipeline
116
+ location ^~ /assets/ {
117
+ gzip_static on;
118
+ expires max;
119
+ add_header Cache-Control public;
120
+ }
121
+
122
+ <% end %>
123
+ <% if using_recipe?(:unicorn) %>
124
+ try_files $uri/index.html $uri @unicorn;
125
+ location @unicorn {
126
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
127
+ proxy_set_header X-Forwarded-Proto https;
128
+ proxy_set_header Host $http_host;
129
+ proxy_redirect off;
130
+ proxy_pass http://unicorn_<%= application %>;
131
+ }
132
+
133
+ <% end %>
134
+ error_page 500 502 503 504 /500.html;
135
+ location = /500.html {
136
+ root <%= current_path %>/public;
137
+ }
138
+ }
139
+ <% end %>
@@ -0,0 +1,47 @@
1
+ working_directory "<%= current_path %>"
2
+ pid "<%= unicorn_pid_file %>"
3
+ stderr_path "<%= unicorn_log_file %>"
4
+ stdout_path "<%= unicorn_log_file %>"
5
+
6
+ # Listen on a Unix domain socket
7
+ # Use shorter backlog for quicker failover when busy
8
+ listen "<%= unicorn_socket_file %>", backlog: 64
9
+
10
+ # Preload our app for more speed
11
+ preload_app true
12
+
13
+ # See unicorn docs for more configuration details
14
+ worker_processes <%= unicorn_workers %>
15
+
16
+ # Kill workers after <%= unicorn_workers_timeout %> seconds, instead of the default 60
17
+ timeout <%= unicorn_workers_timeout %>
18
+
19
+ before_fork do |server, worker|
20
+ # the following is recommended for Rails + "preload_app true"
21
+ # as there is no need for the master process to hold a connection
22
+ if defined?(ActiveRecord::Base)
23
+ ActiveRecord::Base.connection.disconnect!
24
+ end
25
+
26
+ # Before forking, kill the master process that belongs to the .oldbin PID.
27
+ # This enables 0 downtime deploys.
28
+ old_pid = "<%= unicorn_pid_file %>.oldbin"
29
+
30
+ if File.exists?(old_pid) && server.pid != old_pid
31
+ begin
32
+ Process.kill("QUIT", File.read(old_pid).to_i)
33
+ rescue Errno::ENOENT, Errno::ESRCH
34
+ # someone else killed it
35
+ end
36
+ end
37
+ end
38
+
39
+ after_fork do |server, worker|
40
+ # the following is required for Rails + "preload_app true"
41
+ if defined?(ActiveRecord::Base)
42
+ ActiveRecord::Base.establish_connection
43
+ end
44
+
45
+ # if preload_app is true, you may also want to check and
46
+ # restart any other shared sockets such as Memcached
47
+ end
@@ -0,0 +1,84 @@
1
+ #!/bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: unicorn
4
+ # Required-Start: $remote_fs $syslog
5
+ # Required-Stop: $remote_fs $syslog
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: 0 1 6
8
+ # Short-Description: Manage unicorn server
9
+ # Description: Start, stop, restart unicorn server for a specific application.
10
+ ### END INIT INFO
11
+ set -e
12
+
13
+ # Feel free to change any of the following variables for your app:
14
+ TIMEOUT=${TIMEOUT-60}
15
+ APP_ROOT=<%= current_path %>
16
+ PID=<%= unicorn_pid_file %>
17
+ CMD="cd <%= current_path %>; <%= unicorn_command %> -D -c <%= unicorn_config_file %> -E <%= rails_env %>"
18
+ AS_USER=<%= unicorn_user %>
19
+ set -u
20
+
21
+ OLD_PID="$PID.oldbin"
22
+
23
+ sig () {
24
+ test -s "$PID" && kill -$1 `cat $PID`
25
+ }
26
+
27
+ oldsig () {
28
+ test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
29
+ }
30
+
31
+ run () {
32
+ if [ "$(id -un)" = "$AS_USER" ]; then
33
+ eval $1
34
+ else
35
+ su -c "$1" - $AS_USER
36
+ fi
37
+ }
38
+
39
+ case "$1" in
40
+ start)
41
+ sig 0 && echo >&2 "Already running" && exit 0
42
+ run "$CMD"
43
+ ;;
44
+ stop)
45
+ kill -s QUIT `cat $PID` && exit 0
46
+ echo >&2 "Not running"
47
+ ;;
48
+ force-stop)
49
+ sig TERM && exit 0
50
+ echo >&2 "Not running"
51
+ ;;
52
+ restart|reload)
53
+ kill -s USR2 `cat $PID` && echo reloaded OK && exit 0
54
+ echo >&2 "Couldn't reload, starting '$CMD' instead"
55
+ run "$CMD"
56
+ ;;
57
+ upgrade)
58
+ if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
59
+ then
60
+ n=$TIMEOUT
61
+ while test -s $OLD_PIN && test $n -ge 0
62
+ do
63
+ printf '.' && sleep 1 && n=$(( $n - 1 ))
64
+ done
65
+ echo
66
+
67
+ if test $n -lt 0 && test -s $OLD_PIN
68
+ then
69
+ echo >&2 "$OLD_PID still exists after $TIMEOUT seconds"
70
+ exit 1
71
+ fi
72
+ exit 0
73
+ fi
74
+ echo >&2 "Couldn't upgrade, starting '$CMD' instead"
75
+ run "$CMD"
76
+ ;;
77
+ reopen-logs)
78
+ sig USR1
79
+ ;;
80
+ *)
81
+ echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
82
+ exit 1
83
+ ;;
84
+ esac
@@ -0,0 +1,71 @@
1
+ module CapistranoRecipes
2
+ module Unicorn
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ # Start workers with this user
6
+ _cset :unicorn_user, lambda { user }
7
+
8
+ # Number of unicorn workers
9
+ _cset :unicorn_workers, lambda { 2 }
10
+
11
+ # Unicorn workers timeout in seconds
12
+ _cset :unicorn_workers_timeout, lambda { 15 }
13
+
14
+ # Unicorn config template
15
+ _cset :unicorn_config_template, lambda { File.join templates_path, 'unicorn.rb.erb' }
16
+
17
+ # Unicorn config file
18
+ _cset :unicorn_config_file, lambda { File.join config_path, 'unicorn.rb' }
19
+
20
+ # Whether or not to use unicorn init to start on reboot
21
+ _cset :use_unicorn_init, lambda { true }
22
+
23
+ # Unicorn init template
24
+ _cset :unicorn_init_template, lambda { File.join templates_path, 'unicorn_init.erb' }
25
+
26
+ # Unicorn init file
27
+ _cset :unicorn_init_file, lambda { "/etc/init.d/unicorn-#{application}" }
28
+
29
+ # Unicorn pid file
30
+ _cset :unicorn_pid_file, lambda { File.join pids_path, 'unicorn.pid' }
31
+
32
+ # Unicorn log file
33
+ _cset :unicorn_log_file, lambda { File.join log_path, 'unicorn.log' }
34
+
35
+ # Unicorn socket file
36
+ _cset :unicorn_socket_file, lambda { File.join sockets_path, 'unicorn.sock' }
37
+
38
+ # Unicofn command
39
+ _cset :unicorn_command, lambda { using_recipe?(:bundle) ? 'bundle exec unicorn' : 'unicorn' }
40
+
41
+ def using_unicorn_init?
42
+ fetch(:use_unicorn_init)
43
+ end
44
+
45
+ namespace :unicorn do
46
+ desc 'Setup unicorn'
47
+ task :setup, :roles => :app, :except => { :no_release => true } do
48
+ run "rm -f #{unicorn_socket_file}"
49
+ upload_template unicorn_config_template, unicorn_config_file
50
+
51
+ if using_unicorn_init?
52
+ upload_template unicorn_init_template, unicorn_init_file, '+x'
53
+ run "#{sudo} chkconfig --levels 235 unicorn-#{application} on"
54
+ end
55
+ end
56
+ after 'deploy:setup' do
57
+ unicorn.setup if agree? "Create and upload unicorn config for #{application}?"
58
+ end
59
+
60
+ %w[start stop restart].each do |command|
61
+ desc "#{command.capitalize} unicorn"
62
+ task command, :roles => :app, :except => { :no_release => true } do
63
+ run "#{unicorn_init_file} #{command}"
64
+ end
65
+ after "deploy:#{command}", "unicorn:#{command}"
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1 @@
1
+ require 'capistrano_recipes'
@@ -0,0 +1,129 @@
1
+ require 'capistrano'
2
+
3
+ module CapistranoRecipes
4
+ def self.load_into(configuration)
5
+ configuration.load('deploy')
6
+
7
+ configuration.load do
8
+ _cset :shared_children, %w(public/system log tmp/pids tmp/sockets)
9
+ _cset(:domain) { abort "Please specify domain, set :domain, 'domain.com'"}
10
+ _cset :config_dir, 'config'
11
+ _cset :config_path, lambda { File.join shared_path, config_dir }
12
+ _cset :backup_dir, 'backup'
13
+ _cset :backup_path, lambda { File.join shared_path, backup_dir }
14
+ _cset :log_path, lambda { File.join shared_path, 'log' }
15
+ _cset :pids_path, lambda { File.join shared_path, 'pids' }
16
+ _cset :sockets_path, lambda { File.join shared_path, 'sockets' }
17
+
18
+ set :user, 'deployer'
19
+
20
+ @used_recipes = []
21
+
22
+ class << self
23
+ attr_reader :used_recipes
24
+ end
25
+
26
+ def use_recipe(recipe_name)
27
+ return if @used_recipes.include?(recipe_name.to_sym)
28
+
29
+ begin
30
+ require "capistrano/recipes/#{recipe_name}"
31
+
32
+ recipe = CapistranoRecipes.const_get(recipe_name.to_s.capitalize.gsub(/_(\w)/) { $1.upcase })
33
+ recipe.load_into(self)
34
+ @used_recipes << recipe.to_s.split('::').last.downcase.to_sym
35
+ rescue LoadError
36
+ abort "Did you misspell `#{recipe_name}` recipe name?"
37
+ end
38
+ end
39
+
40
+ def use_recipes(*recipes)
41
+ recipes.each do |recipe|
42
+ use_recipe(recipe)
43
+ end
44
+ end
45
+
46
+ def using_recipe?(recipe)
47
+ used_recipes.include?(recipe.to_sym)
48
+ end
49
+
50
+ def upload_template(local_file, remote_file, permissions = nil)
51
+ temp_file = "/tmp/#{File.basename(remote_file)}"
52
+ template = parse_template(local_file)
53
+ put template, temp_file
54
+ run "chmod #{permissions} #{temp_file}" unless permissions.nil?
55
+ run "#{sudo} mv #{temp_file} #{remote_file}"
56
+ end
57
+
58
+ def templates_path
59
+ expanded_path_for('capistrano/recipes/templates')
60
+ end
61
+
62
+ def expanded_path_for(path)
63
+ e = File.join(File.dirname(__FILE__), path)
64
+ File.expand_path(e)
65
+ end
66
+
67
+ def parse_template(file)
68
+ require 'erb'
69
+ template = File.read(file)
70
+ return ERB.new(template, nil, '<>').result(binding)
71
+ end
72
+
73
+ def ask(question)
74
+ q = "\n#{question} : "
75
+ Capistrano::CLI.ui.ask(q)
76
+ end
77
+
78
+ def agree?(question)
79
+ q = "\n#{question} : "
80
+ Capistrano::CLI.ui.agree(q)
81
+ end
82
+
83
+ def password_prompt(prompt)
84
+ p = "\n#{prompt} : "
85
+ Capistrano::CLI.password_prompt(p)
86
+ end
87
+
88
+ def say(message)
89
+ m = "\n#{message}"
90
+ Capistrano::CLI.ui.say(m)
91
+ end
92
+
93
+ namespace :deploy do
94
+ desc 'Deploy application'
95
+ task :default do
96
+ update
97
+ restart
98
+ end
99
+
100
+ desc 'Setup servers for deployment'
101
+ task :setup, :except => { :no_release => true } do
102
+ dirs = [deploy_to, releases_path, shared_path, config_path, backup_path]
103
+ dirs += shared_children.map { |d| File.join(shared_path, d.split('/').last) }
104
+ run "#{try_sudo} mkdir -p #{dirs.join(' ')}"
105
+ run "#{try_sudo} chmod g+w #{dirs.join(' ')}" if fetch(:group_writable, true)
106
+ end
107
+
108
+ task :restart do
109
+ # nothing
110
+ # if task is not being used, it will not appear in `cap -T`
111
+ end
112
+
113
+ task :start do
114
+ # nothing
115
+ # if task is not being used, it will not appear in `cap -T`
116
+ end
117
+
118
+ task :stop do
119
+ # nothing
120
+ # if task is not being used, it will not appear in `cap -T`
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ if Capistrano::Configuration.instance
128
+ CapistranoRecipes.load_into(Capistrano::Configuration.instance)
129
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'bundle' do
4
+
5
+ before do
6
+ mock_config do
7
+ use_recipes :bundle
8
+ set :deploy_to, '/foo/bar'
9
+ end
10
+ end
11
+
12
+ it 'returns used recipe' do
13
+ config.used_recipes.should == [:bundle]
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'bundler' do
4
+ before do
5
+ mock_config do
6
+ use_recipes :bundler
7
+ set :deploy_to, '/foo/bar'
8
+ end
9
+ end
10
+
11
+ it 'uses bundler alias for bundle recipe' do
12
+ config.used_recipes.should == [:bundle]
13
+ end
14
+ end
data/spec/git_spec.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'git' do
4
+ before do
5
+ mock_config do
6
+ use_recipe :git
7
+ set :deploy_to, '/foo/bar'
8
+ def finalize_update; return; end;
9
+ end
10
+ end
11
+
12
+ it 'has branch' do
13
+ config.branch.should == 'master'
14
+ end
15
+
16
+ context 'with repository' do
17
+ before do
18
+ mock_config { set :repository, 'git@example.com/test-app.git' }
19
+ end
20
+
21
+ describe 'deploy:setup' do
22
+ before do
23
+ mock_config do
24
+ set :shared_path, "#{deploy_to}/shared"
25
+ set :shared_children, %w(public/system log tmp/pids)
26
+ end
27
+ end
28
+
29
+ it 'clones repository' do
30
+ cli_execute 'deploy:setup'
31
+ config.should have_run('git clone --no-checkout git@example.com/test-app.git /foo/bar/current')
32
+ end
33
+ end
34
+
35
+ describe 'deploy:update' do
36
+ it 'updates' do
37
+ cli_execute 'deploy:update'
38
+ config.should have_run('cd /foo/bar && git fetch origin && git reset --hard origin/master')
39
+ end
40
+ end
41
+ end
42
+
43
+ it 'has current revision' do
44
+ config.should_receive(:capture).with('cd /foo/bar && git rev-parse --short HEAD') { "baz\n" }
45
+ config.current_revision.should == 'baz'
46
+ end
47
+
48
+ it 'shows pending' do
49
+ config.should_receive(:current_revision) { 'baz' }
50
+ config.namespaces[:deploy].namespaces[:pending].should_receive(:system).with('git log --pretty=medium --stat baz..origin/master')
51
+ cli_execute 'deploy:pending'
52
+ end
53
+
54
+ it 'sets forward agent' do
55
+ config.ssh_options[:forward_agent].should == true
56
+ end
57
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'multistage' do
4
+ before do
5
+ mock_config do
6
+ use_recipes :multistage
7
+
8
+ set :default_stage, :development
9
+ stage(:development, :branch => 'develop') { set :foo, 'bar' }
10
+ stage(:production, :branch => 'master') { set :foo, 'baz' }
11
+ stage :another_stage, :foo => 'bar'
12
+
13
+ task(:example) {}
14
+ end
15
+ end
16
+
17
+ it 'uses default stage' do
18
+ cli_execute 'example'
19
+ config.current_stage.should == 'development'
20
+ config.foo.should == 'bar'
21
+ end
22
+
23
+ it 'aborts when no stage selected' do
24
+ with_stderr do |output|
25
+ config.unset :default_stage
26
+ expect { cli_execute 'example' }.to raise_error(SystemExit)
27
+ output.should include('No stage specified. Please specify one of: development, production')
28
+ end
29
+ end
30
+
31
+ it 'uses specified stage' do
32
+ cli_execute %w[production example]
33
+ config.current_stage.should == 'production'
34
+ config.foo.should == 'baz'
35
+ end
36
+
37
+ it 'sets variables from options' do
38
+ cli_execute 'another_stage'
39
+ config.foo.should == 'bar'
40
+ end
41
+
42
+ it 'accepts default option' do
43
+ mock_config { stage :to_be_default, :default => true }
44
+ config.default_stage.should == :to_be_default
45
+ end
46
+
47
+ context 'with git' do
48
+ before do
49
+ mock_config { use_recipe :git }
50
+ end
51
+
52
+ it 'infers stage using local branch' do
53
+ config.stub(:local_branch) { 'master' }
54
+ cli_execute 'example'
55
+ config.current_stage.should == 'production'
56
+ config.branch.should == 'master'
57
+ end
58
+
59
+ it 'uses default state when local branch not matches' do
60
+ config.stub(:local_branch) { 'foo' }
61
+ cli_execute 'example'
62
+ config.current_stage.should == 'development'
63
+ config.branch.should == 'develop'
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'mysql' do
4
+
5
+ before do
6
+ mock_config do
7
+ use_recipe :mysql
8
+ end
9
+ end
10
+
11
+ it 'returns used recipe' do
12
+ config.used_recipes.should == [:mysql]
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'nginx' do
4
+
5
+ before do
6
+ mock_config do
7
+ use_recipe :nginx
8
+ set :application, 'foo'
9
+ set :deploy_to, '/foo/bar'
10
+ end
11
+ end
12
+
13
+ it 'returns used recipe' do
14
+ config.used_recipes.should == [:nginx]
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'rails assets' do
4
+ before do
5
+ mock_config do
6
+ use_recipe :rails_assets
7
+ set :application, 'foo'
8
+ set :deploy_to, '/foo/bar'
9
+ set :latest_release, deploy_to
10
+ set :use_sudo, false
11
+ end
12
+ end
13
+
14
+ describe 'deploy:assets:precompile' do
15
+ it 'runs precompile' do
16
+ cli_execute 'deploy:assets:precompile'
17
+ config.should have_run('[ -e /foo/bar/shared/assets/manifest* ] && cat /foo/bar/shared/assets/manifest* || echo')
18
+ config.should have_run(' cd -- /foo/bar && rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile ')
19
+ config.should have_run('ls -1 /foo/bar/shared/assets/manifest* | wc -l')
20
+ config.should have_run('ls /foo/bar/shared/assets/manifest*')
21
+ config.should have_run(" cp -- '' ''/assets_manifest ")
22
+ end
23
+
24
+ it 'uses bundle command' do
25
+ mock_config { use_recipe :bundle }
26
+ cli_execute 'deploy:assets:precompile'
27
+ config.should have_run(' cd -- /foo/bar && bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile ')
28
+ end
29
+ end
30
+
31
+ describe 'deploy:assets:clean' do
32
+ it 'runs clean' do
33
+ cli_execute 'deploy:assets:clean'
34
+ config.should have_run('cd /foo/bar && rake RAILS_ENV=production RAILS_GROUPS=assets assets:clean')
35
+ end
36
+ end
37
+ end