Wednesday, December 12, 2012

Rails 3.1 Asset Syncing to S3 on Heroku

Rails 3.1's Asset Pipeline is a wonder in management of static assets in modern web development. It streamlines the process of gzipping, minifying, and fingerprinting asset files as well as setting HTTP cache headers and integrating into a reverse-proxy cache server such as Rack-Cache (comes with Rails) or Varnish.

On Heroku's new Cedar stack however, one cannot use a high-performance static asset server like Nginx or even a high-performance reverse-proxy server like Varnish. So, the next high-performance option, which is arguably higher-performance is remote asset hosting on Amazon S3 or a CDN (Content Distribution Network) like Amazon Cloudfront.

Fortunately, one gem - asset_sync - comes to the rescue. It automates syncing of static assets from your app to S3 or a CDN upon asset compilation.

I used it to serve static content from S3 for a website I am architecting, so I would like to share these tips with anyone who has attempted to follow Heroku's asset syncing guide:
https://devcenter.heroku.com/articles/cdn-asset-host-rails31

If you try to have it sync automatically on deploy to Heroku, it fails miserably due to Heroku not loading its ENV variables in production mode. We missed that when we deployed to staging because asset syncing was working there during the Heroku asset compilation step.

To avoid falling for that trap, you need to compile assets locally, commit, and push. Unfortunately, this can get painful with setting all the ENV vars for asset_sync or passing it options that are supposed to be secure passwords you do not want to put in the git repo.

I wrote a rake task (inspired by a colleague's rake file) to automate the process of grabbing the ENV vars from Heroku instead, compiling, committing, pushing, and then deploying to heroku all in one command.

Code:
https://gist.github.com/4273735

Usage:
rake deploy app=heroku_app_name branch=branch_to_deploy

Alternate syntax:
rake deploy[heroku_app_name,branch_to_deploy]

Default branch as master:
rake deploy[heroku_app_name]