Simple S3 deployment

Oct 17, 2016

I set up this blog with Jekyll on S3. It was partially an experiment to set up a Jekyll site and to try to set up a static site on S3. I was expecting there to be more debugging and troubleshooting like I’ve experienced with hosting in the past, but the process was entirely painless.

Amazon has a great guide for this setup. You’ll additionally need to register a domain and configure Route53, to handle DNS. In following this guide, I was up and running within 30 minutes.

There’s no great built-in tools with Jekyll for deployment, so this turned into a little project for me. Amazon has a well-supported and well-documented Ruby client library for S3. This was an obvious choice. I wanted to avoid uploading my entire _site/ directory that’s generated by Jekyll every time I made a change or new post, so I found a library that wraps git to check for file diffs.

This was the script that I came up with! Do what you will with it, I thought it’d be nice to share for anyone else looking for a simple S3 Jekyll uploader.

require 'aws'
require 'git'

class Deployment
  JEKYLL_SITE_BASE = '_site'
  attr_reader :diff_commit,
              :base_dir

  class << self
    def send(diff_commit='HEAD^')
      state = new(diff_commit)
      if state.changed?
        state.upload
      else
        puts "No new files to upload!"
      end
    end
  end

  def initialize(diff_commit, base_dir=JEKYLL_SITE_BASE)
    @diff_commit = diff_commit
    @base_dir = base_dir
  end

  def changed?
    diffed_files.any?
  end

  def upload
    diffed_files.each do |filename|
      relative_filename = filename.gsub(base_dir, '').slice(1..-1)
      puts "Uploading: '#{relative_filename}'"
      s3_objects.create(relative_filename, File.open(filename, 'rb'))
    end
  end

  private

  def diffed_files
    Git.open(Dir.pwd).
        diff(diff_commit).
        path(base_dir).
        stats.
        fetch(:files, {}).
        keys
  end

  def s3_objects
    @s3_bucket ||= s3.buckets[ENV['S3_BUCKET']].objects
  end

  def s3
    @s3 ||= AWS::S3.new(
      access_key_id: ENV['S3_ACCESS_KEY'],
      secret_access_key: ENV['S3_SECRET_KEY'],
      region: 'us-west-2'
    )
  end
end