-
-
Save albrow/4385707 to your computer and use it in GitHub Desktop.
# ... | |
desc "Deploy website to s3/cloudfront via aws-sdk" | |
task :s3_cloudfront => [:generate, :minify, :gzip, :compress_images] do | |
puts "==================================================" | |
puts " Deploying to Amazon S3 & CloudFront" | |
puts "==================================================" | |
# setup the aws_deploy_tools object | |
config = YAML::load( File.open("_config.yml")) | |
aws_deploy = AWSDeployTools.new(config) | |
# get all files in the public directory | |
all_files = Dir.glob("#{$public_dir}/**/*.*") | |
# we want the gzipped version of the files, not the regular (non-gzipped) version | |
# excluded files contains all the regular versions, which will not be deployed | |
excluded_files = [] | |
$gzip_exts.collect do |ext| | |
excluded_files += Dir.glob("#{$public_dir}/**/*.#{ext}") | |
end | |
# we do gzipped files seperately since they have different metadata (:content_encoding => gzip) | |
puts "--> syncing gzipped files...".yellow | |
gzipped_files = Dir.glob("#{$public_dir}/**/*.gz") | |
gzipped_keys = gzipped_files.collect {|f| (f.split("#{$public_dir}/")[1]).sub(".gz", "")} | |
aws_deploy.sync(gzipped_keys, gzipped_files, | |
:reduced_redundancy => true, | |
:cache_control => "max_age=86400", #24 hours | |
:content_encoding => 'gzip', | |
:acl => config['acl'] | |
) | |
puts "--> syncing all other files...".yellow | |
non_gzipped_files = all_files - gzipped_files - excluded_files | |
non_gzipped_keys = non_gzipped_files.collect {|f| f.split("#{$public_dir}/")[1]} | |
aws_deploy.sync(non_gzipped_keys, non_gzipped_files, | |
:reduced_redundancy => true, | |
:cache_control => "max_age=86400", #24 hours | |
:acl => config['acl'] | |
) | |
# invalidate all the files we just pushed | |
aws_deploy.invalidate_dirty_keys | |
puts "DONE." | |
end | |
desc "Compress all applicable content in public/ using gzip" | |
task :gzip do | |
unless which('gzip') | |
puts "WARNING: gzip is not installed on your system. Skipping gzip..." | |
return | |
end | |
@compressor ||= RedDragonfly.new | |
$gzip_exts.each do |ext| | |
puts "--> gzipping all #{ext}...".yellow | |
files = Dir.glob("#{$gzip_dir}/**/*.#{ext}") | |
files.each do |f| | |
@compressor.gzip(f) | |
end | |
end | |
puts "DONE." | |
end | |
desc "Minify all applicable files in public/ using jitify" | |
task :minify do | |
unless which('jitify') | |
puts "WARNING: jitify is not installed on your system. Skipping minification..." | |
return | |
end | |
@compressor ||= RedDragonfly.new | |
$minify_exts.each do |ext| | |
puts "--> minifying all #{ext}...".yellow | |
files = Dir.glob("#{$minify_dir}/**/*.#{ext}") | |
files.each do |f| | |
@compressor.minify(f) | |
end | |
end | |
puts "DONE." | |
end | |
desc "Compress all images in public/ using ImageMagick" | |
task :compress_images do | |
unless which('convert') | |
puts "WARNING: ImageMagick is not installed on your system. Skipping image compression..." | |
return | |
end | |
@compressor ||= RedDragonfly.new | |
$compress_img_exts.each do |ext| | |
puts "--> compressing all #{ext}...".yellow | |
files = Dir.glob("#{$compress_img_dir}/**/*.#{ext}") | |
files.each do |f| | |
@compressor.compress_img(f) | |
end | |
end | |
puts "DONE." | |
end | |
# ... |
# A set of tools for minifying and compressing content | |
# | |
# Currently supports: | |
# - miniifying js, css, and html | |
# - gzipping any content | |
# - compressing and shrinking images | |
# | |
# Currently depends on 3 system command line tools: | |
# - ImageMagick (http://www.imagemagick.org/script/index.php) | |
# - gzip (http://www.gzip.org/) | |
# - jitify (http://www.jitify.com/) | |
# | |
# These may be swapped out for gem versions in the future, | |
# but for now you have to manually install command line tools | |
# above on your system if you don't already have them. | |
# | |
# Author: Alex Browne | |
class RedDragonfly | |
def initialize | |
# ~~ gzip ~~ | |
$gzip_options = { | |
:output_ext => "gz" | |
} | |
# ~~ minify (jitify) ~~ | |
# $minify_options = { | |
# | |
# } | |
# ~~ images (ImageMagick) ~~ | |
# exts : files extensions which sould be minified during batch operations | |
# output_ext : file extension of the output file ("" means keep the same extension) | |
# max_width : max width for compressed images | |
# max_height : max height for compressed images | |
# quality : image compression quality (1-100, higher is better quality/bigger files) | |
# compress_type : type of compression to be used (http://www.imagemagick.org/script/command-line-options.php#compress) | |
$img_options = { | |
:output_ext => "jpg", | |
:max_width => "600", | |
:max_height => "1200", | |
:quality => "65", | |
:compress_type => "JPEG" | |
} | |
end | |
# accepts a single file or an array of files | |
# accepts a file object or the path to a file (a string) | |
# perserves the original file | |
# the output is (e.g.) .html.gz | |
def gzip (files = []) | |
unless which('gzip') | |
puts "WARNING: gzip is not installed on your system. Skipping gzip..." | |
return | |
end | |
unless files.is_a? Array | |
files = [files] | |
end | |
files.each do |file| | |
fname = get_filename(file) | |
# invoke system gzip | |
system("gzip -cn9 #{fname} > #{fname + '.' + $gzip_options[:output_ext]}") | |
end | |
end | |
# accepts a single file or an array of files | |
# accepts a file object or the path to a file | |
# overwrites the original file with the minified version | |
# html, css, and js supported only | |
def minify (files = []) | |
unless which('jitify') | |
puts "WARNING: jitify is not installed on your system. Skipping minification..." | |
return | |
end | |
unless files.is_a? Array | |
files = [files] | |
end | |
files.each do |file| | |
fname = get_filename(file) | |
# invoke system jitify | |
system("jitify --minify #{fname} > #{fname + '.min'}") | |
# remove the .min extension | |
system("mv #{fname + '.min'} #{fname}") | |
end | |
end | |
# compresses an image file using the options | |
# specified at the top | |
# accepts either a single file or an array of files | |
# accepts either a file object or a path to a file | |
def compress_img (files = []) | |
unless which('convert') | |
puts "WARNING: ImageMagick is not installed on your system. Skipping image compression..." | |
return | |
end | |
unless files.is_a? Array | |
files = [files] | |
end | |
files.each do |file| | |
fname = get_filename(file) | |
compress_cmd = "convert #{fname} -resize #{$img_options[:max_width]}x#{$img_options[:max_height]}\\>" + | |
" -compress #{$img_options[:compress_type]} -quality #{$img_options[:quality]}" + | |
" #{get_raw_filename(fname) + '.' + $img_options[:output_ext]}" | |
# invoke system ImageMagick | |
system(compress_cmd) | |
# remove the old file (if applicable) | |
if (get_ext(fname) != ("." + $img_options[:output_ext])) | |
system("rm #{fname}") | |
end | |
end | |
end | |
# returns the filename (including path and ext) if the input is a file | |
# if the input is a string, returns the same string | |
def get_filename (file) | |
if file.is_a? File | |
file = file.path | |
end | |
return file | |
end | |
# returns the extension of a file | |
# accepts either a file object or a string path | |
def get_ext (file) | |
if file.is_a? String | |
return File.extname(file) | |
elsif file.is_a? File | |
return File.extname(file.path) | |
end | |
end | |
# returns the raw filename (minus extension) of a file | |
# accepts either a file object or a string path | |
def get_raw_filename (file) | |
# convert to string | |
file = get_filename(file) | |
# remove extension | |
file.sub(get_ext(file), "") | |
end | |
## | |
# invoke system which to check if a command is supported | |
# from http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby | |
# which('ruby') #=> /usr/bin/ruby | |
def which(cmd) | |
system("which #{ cmd} > /dev/null 2>&1") | |
end | |
end |
Good catch. Turns out S3 is setting the content-type for everything to be "image/jpeg." It doesn't seem to break anything, but I'll probably still fix it at some point for the sanity's sake.
Turns out the content type problem was causing problems on certain versions of IE, plus I can imagine that specifying an incorrect content type might have a performance penalty. I added a few lines to aws_deploy_tools.rb to automatically detect the content type based on the file extension. It uses part of Actionpack, which means you have to add rails to your gemfile.
I've also fixed a problem with cloudfront invalidations. It turns out that they work with paths instead of files. So, e.g., it's necessary to post invalidation requests for both "/index.html" and "/"
By the way– Anyone interested in seeing some of this converted to a gem?
This would make a useful gem.
I'll note that I had to replace "$public_dir" in the Rakefile excerpt with "public_dir"; otherwise the Dir.glob line tried to scan my entire hard drive since $public_dir is unset.
I would really like this into a gem! :)
I think there could be an issue with
def compress_img (files = [])
in that $img_options[:output_ext] is being forced to jpg by
$img_options = {
:output_ext => "jpg",
:max_width => "600",
:max_height => "1200",
:quality => "65",
:compress_type => "JPEG"
}
This means png files are being converted to jpg and deleted.
Thanks for this, but I wanted to point out an issue I came across:
If you don't specify a :content_type option in aws_deploy.sync, then you could end up with a Content-Type of "image/jpeg" for html files. This means that the sync method needs to have logic that detects what kind of file is being uploaded and add the appropriate Content-Type.