k3_capistrano 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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in k3_capistrano.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,86 @@
1
+ k3_capistrano
2
+ =============
3
+
4
+ The k3_capistrano gem sets some reasonable defaults so that your app's config/deploy* files can be
5
+ very minimal and ideally only need to include any app-specific configuration.
6
+
7
+ It also adds some additional deploy tasks such as deploy:test_request (see below for a list).
8
+
9
+ Getting started
10
+ ===============
11
+
12
+ Add k3_capistrano to your Gemfile and add something like this to your config/deploy.rb:
13
+
14
+ set :application, 'app_name'
15
+ set :repository, "user@server:#{application}.git"
16
+
17
+ require 'k3_capistrano/capistrano'
18
+
19
+ Add your staging/production -server-specific config in config/deploy/staging.rb /
20
+ config/deploy/production.rb, respectively. For example:
21
+
22
+ set :domain, 'my.domain.org'
23
+
24
+ To deploy, you just do the usual:
25
+
26
+ cap deploy # defaults to staging
27
+
28
+ or to deploy to production:
29
+
30
+ cap production deploy
31
+
32
+ If your app needs to, it can override any of the defaults, for example these:
33
+
34
+ set :deploy_to, "/apps/#{application}"
35
+ set :branch, 'master' # The git branch to deploy. Could also be set in config/deploy/#{stage}.rb
36
+
37
+ Integrating with Chef
38
+ =====================
39
+
40
+ If you are using Chef and would like capistrano to simply use the configuration that you've already
41
+ defined for this app using Chef, you can follow this alternate instructions to avoid duplicating
42
+ that configuration data in two places...
43
+
44
+ Set the chef_home environment variable to point to your copy of K3's deployment/kitchen/.
45
+
46
+ Just add a line like this to your .bashrc and source your .bashrc:
47
+
48
+ export chef_home=/path/to/deployment/kitchen/
49
+
50
+ Add k3_capistrano to your Gemfile and add something like this to your config/deploy.rb:
51
+
52
+ set :application, 'app_name'
53
+ require 'k3_capistrano/chef'
54
+ require 'k3_capistrano/capistrano'
55
+
56
+
57
+ Tasks provided by k3_capistrano
58
+ ===============================
59
+
60
+ cap backup:create # Back up the database
61
+ cap backup:download # Download the latest backup
62
+ cap backup:locate_last # Locate the latest backup
63
+ cap backup:restore # Restore a database backup file.
64
+ cap backup:upload_backup # Upload a database backup file to the ...
65
+ cap db:database_yml:setup # Creates the database.yml file under t...
66
+ cap db:pull # Pull data from remote database to you...
67
+ cap db:pull_to_staging # Pull data from production database to...
68
+ cap db:setup # Run the db:setup rake task.
69
+ cap db:taps_server # This uses your database config from y...
70
+ cap deploy:test_request # Does a test request to the web site s...
71
+ cap git:check_if_needs_push # Checks to see if your branch is ahead...
72
+ cap git:push # Pushes your local commits to the git ...
73
+ cap logs:tail # tail all 4 app log files to help you ...
74
+ cap logs:watch # tail -f the production.log file
75
+
76
+ Skipping steps
77
+ ==============
78
+
79
+ This deploy script (and its before/after hooks) will take care of doing everything, including
80
+ running db:migrate and precompiling your assets.
81
+
82
+ Since some of those steps aren't necessary for every signle deploy and can make your deploy take
83
+ longer than it needs to, you can skip some of those steps by setting the skip environment variable
84
+ when you run cap deploy. For example, to skip the migrate and assets steps, just run this:
85
+
86
+ skip=assets,migrate cap production deploy
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "k3_capistrano/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "k3_capistrano"
7
+ s.version = K3Capistrano.version
8
+ s.authors = ["Tyler Rick"]
9
+ s.email = ["tyler@k3integrations.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Common Capistrano tasks and defaults used by K3 Integrations}
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "k3_capistrano"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency 'parseconfig'
22
+ s.add_runtime_dependency 'capistrano-ext'
23
+ s.add_runtime_dependency 'capistrano_colors'
24
+ s.add_runtime_dependency 'facets'
25
+ s.add_runtime_dependency 'rvm-capistrano'
26
+ s.add_runtime_dependency 'colored'
27
+
28
+ #s.add_runtime_dependency 'tylerrick-chef'
29
+ #s.add_runtime_dependency 'capistrano-chef'
30
+ end
@@ -0,0 +1,142 @@
1
+ #
2
+ # = Capistrano database.yml task
3
+ #
4
+ # Provides a couple of tasks for creating the database.yml
5
+ # configuration file dynamically when deploy:setup is run.
6
+ #
7
+ # Category:: Capistrano
8
+ # Package:: Database
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # Copyright:: 2007-2010 The Authors
11
+ # License:: MIT License
12
+ # Link:: http://www.simonecarletti.com/
13
+ # Source:: http://gist.github.com/2769
14
+ #
15
+ #
16
+ # == Requirements
17
+ #
18
+ # This extension requires the original <tt>config/database.yml</tt> to be excluded
19
+ # from source code checkout. You can easily accomplish this by renaming
20
+ # the file (for example to database.example.yml) and appending <tt>database.yml</tt>
21
+ # value to your SCM ignore list.
22
+ #
23
+ # # Example for Subversion
24
+ #
25
+ # $ svn mv config/database.yml config/database.example.yml
26
+ # $ svn propset svn:ignore 'database.yml' config
27
+ #
28
+ # # Example for Git
29
+ #
30
+ # $ git mv config/database.yml config/database.example.yml
31
+ # $ echo 'config/database.yml' >> .gitignore
32
+ #
33
+ #
34
+ # == Usage
35
+ #
36
+ # Include this file in your <tt>deploy.rb</tt> configuration file.
37
+ # Assuming you saved this recipe as capistrano_database_yml.rb:
38
+ #
39
+ # require "capistrano_database_yml"
40
+ #
41
+ # Now, when <tt>deploy:setup</tt> is called, this script will automatically
42
+ # create the <tt>database.yml</tt> file in the shared folder.
43
+ # Each time you run a deploy, this script will also create a symlink
44
+ # from your application <tt>config/database.yml</tt> pointing to the shared configuration file.
45
+ #
46
+ # == Custom template
47
+ #
48
+ # By default, this script creates an exact copy of the default
49
+ # <tt>database.yml</tt> file shipped with a new Rails 2.x application.
50
+ # If you want to overwrite the default template, simply create a custom Erb template
51
+ # called <tt>database.yml.erb</tt> and save it into <tt>config/deploy</tt> folder.
52
+ #
53
+ # Although the name of the file can't be changed, you can customize the directory
54
+ # where it is stored defining a variable called <tt>:template_dir</tt>.
55
+ #
56
+ # # store your custom template at foo/bar/database.yml.erb
57
+ # set :template_dir, "foo/bar"
58
+ #
59
+ # # example of database template
60
+ #
61
+ # base: &base
62
+ # adapter: sqlite3
63
+ # timeout: 5000
64
+ # development:
65
+ # database: #{shared_path}/db/development.sqlite3
66
+ # <<: *base
67
+ # test:
68
+ # database: #{shared_path}/db/test.sqlite3
69
+ # <<: *base
70
+ # production:
71
+ # adapter: mysql
72
+ # database: #{application}_production
73
+ # username: #{user}
74
+ # password: #{Capistrano::CLI.ui.ask("Enter MySQL database password: ")}
75
+ # encoding: utf8
76
+ # timeout: 5000
77
+ #
78
+ # Because this is an Erb template, you can place variables and Ruby scripts
79
+ # within the file.
80
+ # For instance, the template above takes advantage of Capistrano CLI
81
+ # to ask for a MySQL database password instead of hard coding it into the template.
82
+ #
83
+ # === Password prompt
84
+ #
85
+ # For security reasons, in the example below the password is not
86
+ # hard coded (or stored in a variable) but asked on setup.
87
+ # I don't like to store passwords in files under version control
88
+ # because they will live forever in your history.
89
+ # This is why I use the Capistrano::CLI utility.
90
+ #
91
+
92
+ unless Capistrano::Configuration.respond_to?(:instance)
93
+ abort "This extension requires Capistrano 2"
94
+ end
95
+
96
+ require 'pathname'
97
+
98
+ Capistrano::Configuration.instance.load do
99
+
100
+ namespace :db do
101
+ namespace :database_yml do
102
+
103
+ desc <<-DESC
104
+ Creates the database.yml file under the shared dir on the server.
105
+
106
+ During a deploy, the db:database_yml:symlink task will symlink from config/database.yml to
107
+ the shared file.
108
+
109
+ By default, this task uses the default template found in lib/templates/database.yml.erb of
110
+ the k3_capistrano gem unless a template called database.yml.erb is found either in your
111
+ app's config/deploy directory.
112
+ DESC
113
+ task :setup, :except => { :no_release => true } do
114
+ upload_to = "#{shared_path}/config/database.yml"
115
+
116
+ default_template = Pathname.new(__FILE__).dirname + 'templates/database.yml.erb'
117
+
118
+ override_template = Pathname.new(fetch(:template_dir, "config/deploy")) + 'database.yml.erb'
119
+ template = override_template.file? ? override_template.read : default_template.read
120
+
121
+ config = ERB.new(template).result(binding)
122
+ puts "Installing this database.yml to #{upload_to}:\n#{config}"
123
+
124
+ run "mkdir -p #{shared_path}/db"
125
+ run "mkdir -p #{shared_path}/config"
126
+ put config, upload_to
127
+ end
128
+
129
+ desc <<-DESC
130
+ [internal] Updates the symlink for database.yml file to the just deployed release.
131
+ DESC
132
+ task :symlink, :except => { :no_release => true } do
133
+ run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
134
+ end
135
+
136
+ end
137
+ end
138
+
139
+ before 'db:database_yml:setup', 'db:read_remote_my_cnf' # lib/templates/database.yml.erb gets password from my_cnf
140
+ after "deploy:setup", "db:database_yml:setup" unless fetch(:skip_db_setup, false)
141
+ after "deploy:finalize_update", "db:database_yml:symlink"
142
+ end
@@ -0,0 +1,2 @@
1
+ # This file is here for backwards compatibility. You should just require the following file instead:
2
+ require 'k3_capistrano/capistrano'
@@ -0,0 +1,5 @@
1
+ # This file will be automatically required if the k3_capistrano gem is in your
2
+ # Gemfile, so we don't want to put anything Capistrano-dependent here in case
3
+ # they aren't loading/running cap.
4
+ #
5
+ # To actually load this into your config/deploy.rb, you need to explicitly require 'k3_capistrano/capistrano' instead.
@@ -0,0 +1,316 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+
3
+ # From Rails 3.1 and later, we should assume they want to use the asset pipeline. Can be disabled by setting to false.
4
+ _cset :asset_pipeline, true
5
+
6
+ if asset_pipeline
7
+ #===================================================================================================
8
+ # Standard recipe
9
+
10
+ load 'deploy/assets'
11
+ # The assets recipes that are included with capistrano work like this:
12
+ # 1. deploy:assets:symlink creates a symlink from #{latest_release}/public/assets to #{shared_path}/assets
13
+ # 2. Runs rake assets:precompile on the server
14
+ #
15
+ # We will make use of #1, but override #2 as needed to also allow assets to precompiled locally on
16
+ # the developer's localhost.
17
+
18
+ #===================================================================================================
19
+ # More libraries and requires
20
+
21
+ require 'pp'
22
+ require 'facets/class/to_proc'
23
+ require 'facets/string/cleanlines'
24
+ require 'colored'
25
+
26
+ # We can just get the digests from the manifest.yml instead of recomputing them!
27
+ # Also, when I had it recomputing the digest of every file, it would give false positives for a
28
+ # bunch of .gz files. Apparently, gzipping the same file twice can produce a file with a different
29
+ # digest each time? Anyway, if the source file that the .gz file was produced to hasn't changed, we
30
+ # don't care if the .gz file itself is not identical.
31
+ #Pathname.class_eval do
32
+ # def digest
33
+ # Digest::MD5.file(@path).hexdigest
34
+ # end
35
+ #end
36
+ class K3Capistrano::AssetManifest
37
+ attr_reader :digests
38
+ def initialize(manifest_file)
39
+ @manifest_file = manifest_file
40
+ @digests = YAML.load_file(manifest_file)
41
+ end
42
+
43
+ # Compare to digest_for actionpack-3.2.11/lib/sprockets/helpers/rails_helper.rb
44
+ # We could just reuse that, if we were okay with it loading the Rails application (in order to get
45
+ # Rails.application.config.assets)
46
+ def digest_for!(logical_path)
47
+ digest_for(logical_path) or (raise "#{logical_path} not found in #{@manifest_file}" if logical_path =~ /\.js$|\.css$/)
48
+ end
49
+
50
+ def digest_for(logical_path)
51
+ #puts %(@digests[#{logical_path}]=#{(@digests[logical_path]).inspect})
52
+ @digests[logical_path]
53
+ end
54
+
55
+ def to_s
56
+ @manifest_file.to_s
57
+ end
58
+ end
59
+
60
+ #===================================================================================================
61
+ # Variables
62
+
63
+ _cset :precompile_assets_locally, false
64
+ # The following options only apply if precompile_assets_locally is true:
65
+ (
66
+ # Set skip to include 'local_precompile' to skip running assets:precompile locally during this deploy.
67
+ # You may want to do this to speed up the deploy if you know that your local assets directory is
68
+ # already current -- for example, if you just deployed to staging (and did the precompile during
69
+ # that deploy) and now you are ready to deploy the exact same code to production.
70
+
71
+ # Can also set to :previous_local
72
+ #_cset :compare_to_manifest_from, :remote
73
+
74
+ # If true, keeps a local public/assets; if false, renames it immediately after running precompile
75
+ # It is recommended to set this to false, because if a public/assets dir exists in development mode,
76
+ # changes to your assets files will *not* take effect until you run precompile, whereas if there is
77
+ # *no* public/assets dir, changes will be compiled automatically as needed and take immediately
78
+ # after changing them.
79
+ set :keep_public_assets_dir, false
80
+
81
+ # TODO: How many backups of public/assets to keep
82
+ set :local_asset_dirs_to_keep, 5
83
+
84
+ # Set to false if you don't want it to show the list of changed assets during a deploy.
85
+ set :list_changed_assets_during_deploy, true
86
+ )
87
+
88
+ #===================================================================================================
89
+ namespace :deploy do
90
+ namespace :assets do
91
+
92
+ # If you already know that no changes have been made to your assets since your last deploy, you
93
+ # can speed up the deploy by several minutes by skipping the precompilation step and just reusing
94
+ # the assets in shared/assets that were used by the previous deploy.
95
+ #
96
+ # Usage: skip=assets cap deploy
97
+ #
98
+ if skip.include?('assets')
99
+ task :precompile, :roles => :app, :except => { :no_release => false } do
100
+ # Do nothing.
101
+ puts "Warning: Skipping the 'deploy:assets:precompile' task. Make sure the assets in shared/assets are current."
102
+ end
103
+
104
+ else
105
+ if precompile_assets_locally
106
+ # This strategy precompiles your assets locally and then copies the files to the remote server.
107
+ # This way you can deploy to multiple servers in a row without having to recompile the assets each time,
108
+ # and you can redeploy to the same server without recompiling as long as no assets have changed.
109
+ # You must set :precompile_assets_locally, true if you want to opt in to using this.
110
+ #
111
+ # Overview:
112
+ # * Downloads current manifest from shared/assets/manifest.yml on server.
113
+ # * For each file in local public/assets dir (or most recent timestamped dir):
114
+ # * If local file not present in currently deployed manifest, upload it
115
+ # * If local file has different digest, upload it
116
+ # Do incremental update instead of rsyncing all
117
+
118
+ # List all asset dirs (dirs matching public/assets.*), sorted by filename, except always returning
119
+ # public/assets last if it exists.
120
+ def local_asset_dirs
121
+ (
122
+ Dir["public/assets.*"].map(&Pathname).sort +
123
+ [Pathname.new('public/assets')]
124
+ ).select(&:directory?).select(&:exist?).to_a
125
+ end
126
+
127
+ task :list_local_asset_dirs do
128
+ puts local_asset_dirs
129
+ end
130
+
131
+ def changed_from(previous_file_list, previous_manifest, verbose = false)
132
+ current_dir = local_asset_dirs.last or raise "no local public/assets* dirs exist"
133
+ current_manifest = K3Capistrano::AssetManifest.new(current_dir + 'manifest.yml')
134
+
135
+ if verbose
136
+ puts "Listing changes between '#{previous_manifest}' (previous) and '#{current_manifest}' (latest local precompile, at '#{current_dir}')..."
137
+ puts "Legend: " + "doesn't exist (in dir of '#{previous_manifest}')".cyan + ' | ' + "exists but has changed since '#{previous_manifest}'".yellow
138
+ end
139
+
140
+ descendants = []; current_dir.find {|_| descendants << _ if _.file? }
141
+ descendants.select do |file_in_current|
142
+ base_name = file_in_current.relative_path_from(current_dir).to_s.gsub(%r<^../>, '')
143
+ logical_path = base_name.gsub(/-\w{32}/, '').gsub(/\.gz$/, '')
144
+ #file_in_previous = previous_dir + base_name if previous_dir
145
+
146
+ # Only if the file *exists* in and is in the manifest can we skip it.
147
+ # This is a safeguard against the case where the manifest.yml file gets uploaded but the
148
+ # files themselves haven't yet.
149
+
150
+ file_in_previous = previous_file_list.include?(base_name) && previous_manifest.digest_for(logical_path)
151
+ changed =
152
+ if file_in_previous #&& file_in_previous.exist?
153
+ if previous_manifest.digest_for!(logical_path) == current_manifest.digest_for!(logical_path)
154
+ color = :green
155
+ false
156
+ else
157
+ color = :yellow
158
+ true
159
+ end
160
+ else
161
+ color = :cyan
162
+ true
163
+ end
164
+ puts file_in_current.to_s.send(color) if verbose && changed
165
+ changed
166
+ end.map do |_|
167
+ _.relative_path_from(current_dir).to_s.gsub(%r<^../>, '')
168
+ end
169
+ end
170
+
171
+ def changed_since_previous_local_precompile(verbose = false)
172
+ # TODO: It is also possible for there to be no previous assets dir, in which case it should
173
+ # list *all* files in latest_dir as being new files.
174
+ previous_dir = local_asset_dirs[-2]
175
+ previous_file_list = []; previous_dir.find {|_| previous_file_list << _ if _.file? }
176
+ latest_dir = local_asset_dirs.last
177
+ puts "Listing changes between '#{previous_dir}' and latest local precompile, '#{latest_dir}'..." if verbose
178
+ previous_manifest = K3Capistrano::AssetManifest.new(previous_dir + 'manifest.yml')
179
+ changed_from(previous_file_list, previous_manifest, verbose)
180
+ end
181
+
182
+ def remote_file_list
183
+ capture("cd #{shared_path}/assets && find . -type f").
184
+ cleanlines.map {|_| _.gsub(%r<^\./>, '') }
185
+ end
186
+
187
+ def changed_since_last_deploy(verbose = false)
188
+ remote_manifest = 'tmp/manifest_from_last_deploy.yml'
189
+ get "#{shared_path}/assets/manifest.yml", remote_manifest, via: :scp
190
+ remote_manifest = K3Capistrano::AssetManifest.new(remote_manifest)
191
+ remote_file_list = remote_file_list()
192
+ Pathname('tmp/asset_file_from_last_deploy.txt').tap do |file|
193
+ puts %(Saving list of files from server at: #{file})
194
+ file.open('w') {|_| _.puts remote_file_list.join("\n" )}
195
+ end
196
+ puts "Listing changes between latest deploy and latest local precompile..." if verbose
197
+ changed_from(remote_file_list, remote_manifest, verbose)
198
+ end
199
+
200
+ def changed_assets(verbose = false)
201
+ #case compare_to_manifest_from
202
+ #when :remote
203
+ changed_since_last_deploy(verbose)
204
+ #when :previous_local
205
+ # changed_since_previous_local_precompile(verbose)
206
+ #else
207
+ # raise "unrecognized value for compare_to_manifest_from: #{compare_to_manifest_from.inspect}"
208
+ #end
209
+ end
210
+
211
+ task :list_changed_since_previous_local_precompile do
212
+ puts changed_since_previous_local_precompile(verbose = true)
213
+ end
214
+
215
+ task :list_changed_assets do
216
+ changed_assets(verbose = true)
217
+ end
218
+
219
+ task :list_changed_since_last_deploy do
220
+ changed_since_last_deploy(verbose = true)
221
+ end
222
+
223
+ task :push_changed_assets do
224
+ current_dir = local_asset_dirs.last or raise "no local public/assets* dirs exist"
225
+ changed_files = changed_assets(verbose = list_changed_assets_during_deploy)
226
+
227
+ #remote_host_and_path = "#{user}@#{domain}:#{release_or_current_path.gsub('~/', './')}/public/assets/"
228
+ remote_host_and_path = "#{user}@#{domain}:#{shared_path}/assets/"
229
+
230
+ changed_files_in_root, changed_files_in_subdir = changed_files.partition {|_|
231
+ Pathname.new(_).dirname.to_s == '.'
232
+ }
233
+ if changed_files_in_root.any?
234
+ # TODO: shell escape arguments
235
+ command = "time scp " +
236
+ "#{changed_files_in_root.map {|_| "#{current_dir}/#{_}" }.join(' ')} " +
237
+ "#{remote_host_and_path}"
238
+ #command = "rsync -a --itemize-changes --recursive --compress --times #{changed_files} #{remote_host_and_path}"
239
+ run_locally command
240
+
241
+ # See also this version from https://groups.google.com/forum/#!topic/capistrano/cuOeI-aNLfo
242
+ # servers = find_servers_for_task(current_task)
243
+ # port_option = port ? " -e 'ssh -p #{port}' " : ''
244
+ # servers.each do |server|
245
+ # run_locally("rsync --recursive --times --rsh=ssh --compress --human-readable #{port_option} --progress public/assets #{user} @#{server}:#{shared_path}")
246
+ # end
247
+ end
248
+
249
+ remote_dir_list = remote_file_list().map {|_| Pathname(_).dirname }.uniq
250
+ changed_files_in_subdir.each do |_|
251
+ # mkdir subdir if it doesn't exist on server. (Check in remote_file_list so we don't
252
+ # have to hit the server to check each file individually.)
253
+ dir = Pathname(_).dirname
254
+ unless remote_dir_list.include? dir
255
+ remote_dir_list << dir
256
+ run "mkdir -p #{shared_path}/assets/#{dir}"
257
+ end
258
+
259
+ #put "#{current_dir}/#{_}", "#{shared_path}/assets/#{_}", via: :scp
260
+ command = "scp #{"#{current_dir}/#{_}"} #{remote_host_and_path}#{_}"
261
+ run_locally command
262
+ end
263
+ end
264
+
265
+ def skip_local_precompile?
266
+ skip.include?('local_precompile')
267
+ end
268
+
269
+ def local_asset_current?
270
+ # TODO: Compare time of last commit to app/assets or vendor/assets with time of last local
271
+ # precompile. Return true if there have been any changes since the last precompile that
272
+ # wouldn't be included in the last precompile.
273
+ #
274
+ #local_asset_dirs.any?
275
+ skip_local_precompile?
276
+ end
277
+
278
+ task :precompile_locally do
279
+ run_locally "time bundle exec rake assets:precompile RAILS_ENV=production RAILS_GROUPS=assets"
280
+ after_local_precompile
281
+ end
282
+
283
+ task :after_local_precompile do
284
+ unless keep_public_assets_dir
285
+ latest_dir = "public/assets.#{Time.now.strftime("%Y-%m-%dT%H.%M.%S")}"
286
+ # Move them out of the way locally, so that in development it won't try to use (possibly stale) assets from public/assets instead of dynamically generating them
287
+ run_locally "mv public/assets #{latest_dir}"
288
+ end
289
+ end
290
+
291
+ task :precompile, :roles => :app, :except => { :no_release => false } do
292
+ if local_asset_current?
293
+ # We'll just reuse the assets that we've already previously precompiled locally.
294
+ #latest_dir = local_asset_dirs.last or raise "local_asset_dirs empty"
295
+ puts "Using existing local asset dir: #{local_asset_dirs.last}"
296
+ else
297
+ precompile_locally
298
+ end
299
+ #Pathname(latest_dir).exist? or raise "local assets dir #{latest_dir.inspect} does not exist"
300
+
301
+ push_changed_assets
302
+
303
+ # This is so that the deploy will be aborted if the previous steps failed to produce
304
+ # asset files in the target directory on the server.
305
+ run "ls #{release_or_current_path}/public/assets/*.js"
306
+ end
307
+
308
+ else
309
+ # Just use the standard recipe supplied with capistrano in 'deploy/assets'
310
+ end
311
+ end
312
+
313
+ end
314
+ end
315
+ end # if asset_pipeline
316
+ end