require File.dirname(__FILE__) + “/../morning_glory”
namespace :morning_glory do
namespace :cloudfront do @@prev_cdn_revision = nil @@scm_commit_required = false begin
# MORNING_GLORY_CONFIG = YAML.load_file(“#{Rails.root}/config/morning_glory.yml”) if !defined? MORNING_GLORY_CONFIG
MORNING_GLORY_CONFIG = YAML.load_file("config/morning_glory.yml") if !defined? MORNING_GLORY_CONFIG rescue end def check_config if !defined? MORNING_GLORY_CONFIG[Rails.env] || MORNING_GLORY_CONFIG[Rails.env]['enabled'] != true raise "Deployment appears to be disabled for this environment (#{Rails.env}) within config/morning_glory.yml. Specify an alternative environment with RAILS_ENV={environment name}." end if (!ENV['S3_KEY'] && !ENV['S3_SECRET']) if !defined? S3_CONFIG[Rails.env] raise "You seem to be lacking your Amazon S3 configuration file, config/s3.yml" end end end def get_revision rev = nil # SVN begin svn_rev = `svnversion .`.chomp.gsub(':','_') puts svn_rev if svn_rev != 'exported' && svn_rev != '' && svn_rev != nil rev = Digest::MD5.hexdigest( svn_rev ).to_s puts '* Using SVN revision' end rescue # Ignore end # GIT begin git_rev = `git show --pretty=format:"%H|%ci" --quiet`.split('|')[0] if !git_rev.nil? rev = git_rev.to_s puts '* Using Git revision' end rescue # Ignore end if rev.nil? rev = Time.new.strftime("%Y%m%d%H%M%S") puts '* Using timestamp revision' @@scm_commit_required = true end return rev end def update_revision prev = MORNING_GLORY_CONFIG[Rails.env]['revision'].to_s rev = get_revision MORNING_GLORY_CONFIG[Rails.env]['revision'] = rev ENV['RAILS_ASSET_ID'] = CLOUDFRONT_REVISION_PREFIX + rev # Store the previous revision so we can delete the bucket from S3 later after deploy @@prev_cdn_revision = CLOUDFRONT_REVISION_PREFIX + prev File.open("#{path}/config/morning_glory.yml", 'w') { |f| YAML.dump(MORNING_GLORY_CONFIG, f) } puts "* CDN revision updated for '#{Rails.env}' environment to #{ENV['RAILS_ASSET_ID']}" end def compile_sass_if_available if defined? Sass puts "* Compiling Sass stylesheets" Sass::Plugin.update_stylesheets end end desc "Bump the revision, compile any Sass stylesheets, and deploy assets to S3 and Cloudfront" task :deploy => [:environment] do |t, args| require 'aws/s3' require 'fileutils' puts 'MorningGlory: Starting deployment to the Cloudfront CDN...' check_config update_revision compile_sass_if_available # Constants SYNC_DIRECTORY = File.join(Rails.root, 'public') TEMP_DIRECTORY = File.join(Rails.root, 'tmp', 'morning_glory', 'cloudfront', Rails.env, ENV['RAILS_ASSET_ID']); # Configuration constants BUCKET = MORNING_GLORY_CONFIG[Rails.env]['bucket'] || Rails.env DIRECTORIES = MORNING_GLORY_CONFIG[Rails.env]['asset_directories'] || %w(images javascripts stylesheets) CONTENT_TYPES = MORNING_GLORY_CONFIG[Rails.env]['content_types'] || { :jpg => 'image/jpeg', :png => 'image/png', :gif => 'image/gif', :css => 'text/css', :js => 'text/javascript' } S3_LOGGING_ENABLED = MORNING_GLORY_CONFIG[Rails.env]['s3_logging_enabled'] || false DELETE_PREV_REVISION = MORNING_GLORY_CONFIG[Rails.env]['delete_prev_rev'] || false REGEX_ROOT_RELATIVE_CSS_URL = /url\((\'|\")?(\/+.*(#{CONTENT_TYPES.keys.map { |k| '\.' + k.to_s }.join('|')}))\1?\)/ # Copy all the assets into the temp directory for processing FileUtils.makedirs TEMP_DIRECTORY if !FileTest::directory?(TEMP_DIRECTORY) puts "* Copying files to working directory for cache-busting-renaming" DIRECTORIES.each do |directory| Dir[File.join(SYNC_DIRECTORY, directory, '**', "*.{#{CONTENT_TYPES.keys.join(',')}}")].each do |file| file_path = file.gsub(/.*public\//, "") temp_file_path = File.join(TEMP_DIRECTORY, file_path) FileUtils.makedirs(File.dirname(temp_file_path)) if !FileTest::directory?(File.dirname(temp_file_path)) puts " ** Copied to #{temp_file_path}" FileUtils.copy file, temp_file_path end end puts "* Replacing image references within CSS files" DIRECTORIES.each do |directory| Dir[File.join(TEMP_DIRECTORY, directory, '**', "*.{css}")].each do |file| puts " ** Renaming image references within #{file}" buffer = File.new(file,'r').read.gsub(REGEX_ROOT_RELATIVE_CSS_URL) { |m| m.insert m.index('(') + ($1 ? 2 : 1), '/'+ENV['RAILS_ASSET_ID'] } File.open(file,'w') {|fw| fw.write(buffer)} end end # TODO: Update references within JS files AWS::S3::Base.establish_connection!( :access_key_id => S3_CONFIG['access_key_id'] || ENV['S3_KEY'], :secret_access_key => S3_CONFIG['secret_access_key'] || ENV['S3_SECRET'] ) begin puts "* Attempting to create S3 Bucket '#{BUCKET}'" AWS::S3::Bucket.create(BUCKET) AWS::S3::Bucket.enable_logging_for(BUCKET) if S3_LOGGING_ENABLED puts "* Uploading files to S3 Bucket '#{BUCKET}'" DIRECTORIES.each do |directory| Dir[File.join(TEMP_DIRECTORY, directory, '**', "*.{#{CONTENT_TYPES.keys.join(',')}}")].each do |file| file_path = file.gsub(/.*#{TEMP_DIRECTORY}\//, "") file_path = File.join(ENV['RAILS_ASSET_ID'], file_path) file_ext = file.split(/\./)[-1].to_sym puts " ** Uploading #{BUCKET}/#{file_path}" AWS::S3::S3Object.store(file_path, open(file), BUCKET, :access => :public_read, :content_type => CONTENT_TYPES[file_ext]) end end # If the configured to delete the prev revision, and the prev revision value was in the YAML (not the blank concat of CLOUDFRONT_REVISION_PREFIX + revision number) if DELETE_PREV_REVISION && @@prev_cdn_revision != CLOUDFRONT_REVISION_PREFIX # TODO: Figure out how to delete from the S3 bucket properly puts "* Deleting previous CDN revision #{BUCKET}/#{@@prev_cdn_revision}" AWS::S3::Bucket.find(BUCKET).objects(:prefix => @@prev_cdn_revision).each do |object| puts " ** Deleting #{BUCKET}/#{object.key}" object.delete end end rescue raise ensure puts "* Deleting temp cache files in #{TEMP_DIRECTORY}" FileUtils.rm_r TEMP_DIRECTORY end puts "MorningGlory: DONE! Your assets have been deployed to the Cloudfront CDN." if @@scm_commit_required == true puts '='*80 puts "NB: You will need to commit the /config/morning_glory.yml file and update it on your servers." puts '='*80 end end end
end