Permalink
Browse files

Merge pull request #281 from ndbroadbent/assets_rollback_and_expiry

Added support for rolling back assets, and removing expired assets
  • Loading branch information...
2 parents 9a0303e + 23452d6 commit 5793c33e39e6f05f424796ecc3fe37b126ff332d @carsomyr carsomyr committed Nov 8, 2012
Showing with 102 additions and 5 deletions.
  1. +1 −1 lib/capistrano/configuration/actions/invocation.rb
  2. +101 −4 lib/capistrano/recipes/deploy/assets.rb
@@ -160,7 +160,7 @@ def run(cmd, options={}, &block)
# or #invoke_command.
def run_tree(tree, options={}) #:nodoc:
if tree.branches.empty? && tree.fallback
- logger.debug "executing #{tree.fallback}"
+ logger.debug "executing #{tree.fallback}" unless options[:silent]
elsif tree.branches.any?
logger.debug "executing multiple commands in parallel"
tree.each do |branch|
@@ -3,11 +3,15 @@
_cset :asset_env, "RAILS_GROUPS=assets"
_cset :assets_prefix, "assets"
_cset :assets_role, [:web]
+_cset :expire_assets_after, (3600 * 24 * 7)
_cset :normalize_asset_timestamps, false
-before 'deploy:finalize_update', 'deploy:assets:symlink'
-after 'deploy:update_code', 'deploy:assets:precompile'
+before 'deploy:finalize_update', 'deploy:assets:symlink'
+after 'deploy:update_code', 'deploy:assets:precompile'
+before 'deploy:assets:precompile', 'deploy:assets:update_asset_mtimes'
+after 'deploy:cleanup', 'deploy:assets:clean_expired'
+after 'deploy:rollback:revision', 'deploy:assets:rollback'
namespace :deploy do
namespace :assets do
@@ -20,7 +24,7 @@
:assets_prefix variable to match.
DESC
task :symlink, :roles => assets_role, :except => { :no_release => true } do
- run <<-CMD
+ run <<-CMD.compact
rm -rf #{latest_release}/public/#{assets_prefix} &&
mkdir -p #{latest_release}/public &&
mkdir -p #{shared_path}/assets &&
@@ -39,7 +43,29 @@
set :asset_env, "RAILS_GROUPS=assets"
DESC
task :precompile, :roles => assets_role, :except => { :no_release => true } do
- run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile"
+ run <<-CMD.compact
+ cd -- #{latest_release.shellescape} &&
+ #{rake} RAILS_ENV=#{rails_env.shellescape} #{asset_env.shellescape} assets:precompile &&
+ cp -- #{shared_path.shellescape}/assets/manifest.yml #{current_release.shellescape}/assets_manifest.yml
+ CMD
+ end
+
+ desc <<-DESC
+ [internal] Updates the mtimes for assets that are required by the current release.
+ This task runs before assets:precompile.
+ DESC
+ task :update_asset_mtimes, :roles => assets_role, :except => { :no_release => true } do
+ # Fetch assets/manifest.yml contents.
+ manifest_path = "#{shared_path}/assets/manifest.yml"
+ manifest_yml = capture("[ -e #{manifest_path.shellescape} ] && cat #{manifest_path.shellescape} || echo").strip
+
+ if manifest_yml != ""
+ manifest = YAML.load(manifest_yml)
+ current_assets = manifest.to_a.flatten.map {|a| [a, "#{a}.gz"] }.flatten
+ logger.info "Updating mtimes for ~#{current_assets.count} assets..."
+ command = "touch -cm -- " << current_assets.map {|a| "#{shared_path}/assets/#{a}".shellescape }.join(' ')
+ run command, :silent => true
+ end
end
desc <<-DESC
@@ -56,5 +82,76 @@
task :clean, :roles => assets_role, :except => { :no_release => true } do
run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:clean"
end
+
+ desc <<-DESC
+ Clean up any assets that haven't been deployed for more than :expire_assets_after seconds.
+ Default time to keep old assets is one week. Set the :expire_assets_after variable
+ to change the assets expiry time. Assets will only be deleted if they are not required by
+ an existing release.
+ DESC
+ task :clean_expired, :roles => assets_role, :except => { :no_release => true } do
+ # Fetch all assets_manifest.yml contents.
+ manifests_output = capture <<-CMD.compact
+ for manifest in #{releases_path.shellescape}/*/assets_manifest.yml; do
+ cat -- "$manifest" 2> /dev/null && printf ':::' || true;
+ done
+ CMD
+ manifests = manifests_output.split(':::')
+
+ if manifests.empty?
+ logger.info "No manifests in #{releases_path}/*/assets_manifest.yml"
+ else
+ logger.info "Fetched #{manifests.count} manifests from #{releases_path}/*/assets_manifest.yml"
+ current_assets = Set.new
+ manifests.each do |yaml|
+ manifest = YAML.load(yaml)
+ current_assets += manifest.to_a.flatten.flat_map do |file|
+ [file, "#{file}.gz"]
+ end
+ end
+ current_assets += %w(manifest.yml sources_manifest.yml)
+
+ # Write the list of required assets to server.
+ # Files must be sorted in dictionary order using Linux sort
+ logger.info "Writing required assets to #{deploy_to}/REQUIRED_ASSETS..."
+ escaped_assets = current_assets.to_a.join("\\n").gsub("\"", "\\\"")
+ run "printf -- \"#{escaped_assets}\" | sort > #{deploy_to.shellescape}/REQUIRED_ASSETS", :silent => true
+
+ # Finds all files older than X minutes, then removes them if they are not referenced
+ # in REQUIRED_ASSETS.
+ expire_after_mins = (expire_assets_after.to_f / 60.0).to_i
+ logger.info "Removing assets that haven't been deployed for #{expire_after_mins} minutes..."
+ run <<-CMD.compact
+ cd -- #{shared_path.shellescape}/assets/ &&
+ for f in $(
+ find * -mmin +#{expire_after_mins.to_s.shellescape} -type f | sort |
+ comm -23 -- - #{deploy_to.shellescape}/REQUIRED_ASSETS
+ ); do
+ echo "Removing unneeded asset: $f";
+ rm -f -- "$f";
+ done;
+ rm -f -- #{deploy_to.shellescape}/REQUIRED_ASSETS
+ CMD
+ end
+ end
+
+ desc <<-DESC
+ Rolls back assets to the previous release by symlinking the release's manifest
+ to shared/assets/manifest.yml, and finally recompiling or regenerating nondigest assets.
+ DESC
+ task :rollback, :roles => assets_role, :except => { :no_release => true } do
+ previous_manifest = "#{previous_release}/assets_manifest.yml"
+ if capture("[ -e #{previous_manifest.shellescape} ] && echo true || echo false").strip != 'true'
+ puts "#{previous_manifest} is missing! Cannot roll back assets. " <<
+ "Please run deploy:assets:precompile to update your assets when the rollback is finished."
+ return false
+ else
+ run <<-CMD.compact
+ cd -- #{previous_release.shellescape} &&
+ cp -f -- #{previous_manifest.shellescape} #{shared_path.shellescape}/assets/manifest.yml &&
+ #{rake} RAILS_ENV=#{rails_env.shellescape} #{asset_env.shellescape} assets:precompile:nondigest
+ CMD
+ end
+ end
end
end

0 comments on commit 5793c33

Please sign in to comment.