Deploying static websites to Amazon S3
Amazon S3 is a great choice for hosting static content (like blogs), but the process of deploying can leave a little to be desired — unless you leverage a build tool like Rake.
I recently switched hosting for this site — from Heroku to Amazon S3. I’ve always used Jekyll to build it, but had been using Heroku for the convenience of push-to-deploy (I use custom plugins as well as SASS, so I’m unable to use GitHub Pages without mucking up my site’s git history).
My hesitation to use Amazon S3 has always been deploying — I wanted something as simple as Heroku’s push-to-deploy. After a bit of struggling, I ended up writing a Rake task to do it all for me. To see the complete source (including the build task), check out this site on GitHub.
Rake task to deploy the _site/
directory to an S3 bucket:
desc "Deploy site to Amazon S3"
task :deploy => :build do
require "s3"
id = ENV["WWWDUNKMANME_ID"]
key = ENV["WWWDUNKMANME_KEY"]
if id.nil? || key.nil?
puts "S3 credentials not found in environment."
exit 1
end
service = S3::Service.new(:access_key_id => id, :secret_access_key => key)
bucket = service.buckets.find("www.dunkman.me")
local_files = Dir["_site/**/*"].map { |file| file.sub(/_site\//, "") }
remote_files = bucket.objects
puts "Checking for remote files to delete..."
changes = false
remote_files.each do |remote_file|
unless File.exists? "_site/#{remote_file.key}"
remote_file.destroy
puts " | #{remote_file.key} has been removed."
changes = true
end
end
unless changes
puts " (no files to delete)"
end
puts
puts "Checking for local files to upload..."
changes = false
local_files.each do |local_file|
next if File.directory? "_site/#{local_file}"
files = remote_files.select { |remote_file| remote_file.key == local_file }
remote_file = files.first
if remote_file
local_md5 = Digest::MD5.file("_site/#{local_file}").hexdigest
remote_md5 = remote_file.etag
unless local_md5 == remote_md5
remote_file.content = open("_site/#{local_file}")
remote_file.content_type = "" # Use default
remote_file.save
puts " | #{local_file} has changed."
changes = true
end
else
remote_file = bucket.objects.build(local_file)
remote_file.content = open("_site/#{local_file}")
remote_file.content_type = "" # Use default
remote_file.save
puts " | #{local_file} is new."
changes = true
end
end
unless changes
puts " (no files to upload)"
end
puts
end