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]
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module RailsApplication | |
module Deploy | |
class << self | |
def run(*cmd) | |
system(*cmd) | |
raise "Command #{cmd.inspect} failed!" unless $?.success? | |
end | |
def deploy(app, branch = "master") | |
branch ||= "master" #in case it was passed as nil | |
puts "-----> Compiling Assets..." | |
heroku_env_vars = heroku_app_env_vars(app) | |
heroku_env_vars.each {|k, v| ENV[k] = v unless ENV[k].present?} | |
run "git checkout #{branch}" | |
Rake::Task["assets:precompile"].execute | |
run "git add public/assets" | |
run "git commit -m'deploy compiled assets'" | |
run "git push origin #{branch}" | |
puts "-----> Pushing..." | |
#run "git push git@github.com:RailsApplication/#{app}.git HEAD:master -f" | |
run "git push --force git@heroku.com:#{app}.git #{branch}:master" | |
puts "-----> Migrating..." | |
run "heroku run rake db:migrate --app #{app}" | |
puts "-----> Seeding..." | |
run "heroku run rake db:seed --app #{app}" | |
puts "-----> Restarting..." | |
run "heroku restart --app #{app}" | |
end | |
def heroku_app_env_vars(app) | |
heroku_env_vars_output = `heroku config -a #{app}` | |
heroku_env_vars_output_split = heroku_env_vars_output.split("\n") | |
heroku_env_var_lines = heroku_env_vars_output_split[1, heroku_env_vars_output_split.size] | |
heroku_env_vars = heroku_env_var_lines.inject({}) do |output, line| | |
match_data = line.match('([^:]+):(.*)') | |
output.merge match_data[1].strip => match_data[2].strip | |
end | |
end | |
end | |
end | |
end | |
desc "Deploy heroku app from branch (or master by default)" | |
task :deploy, [:app, :branch] => [:environment] do |t, args| | |
RailsApplication::Deploy.deploy(args[:app], args[:branch]) | |
end |
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]
2 comments:
There is a Heroku labs feature that will include the environment variables for you, that allows you to simply deploy as usual and have the asset pipeline work its magic.
And the link: https://devcenter.heroku.com/articles/labs-user-env-compile
Post a Comment