capistrano_recipes 1.0.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.
@@ -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