Precompile SASS to CSS for deployment to Heroku

28 September 2010

If you have deployed apps to Heroku you know that you cannot write to the read-only file system that Heroku offers. For file uploads you have to use some storage provider like Amazon S3 or RackSpace CloudFiles.

Now, if your application (I’m assuming you’re already on Rails 3), is using Haml + Sass, you’re in some trouble. Sass is set to generate CSS files on the fly and save them to public/stylesheets so the can be served like static content. On Heroku, that is not possible.

Luckily, there are a few solutions to this problem. I’ll describe one of them. ~

Use the Force (of git)

In my solution I’m using the power of git to generate the necessary CSS files and commit them automatically. Here’s how it works.

In a normal situation the Sass plugin will compile the SASS or SCSS files in public/stylesheets/sass/*.scss and store the generated CSC files in public/stylesheets. What we need to do is generate those CSS files by hand and commit them just like other versioned files in our repository. To do this, you need to write a pre-commit hook for git. This sounds more difficult than it really is.

Here’s the pre-commit hook I’m currently using to take all public/stylesheets/sass/*.scss files and store the resulting CSS files in public/stylesheets.

#!/usr/bin/env ruby

Dir['public/stylesheets/**/*.scss'].each do |sass|
  basename = sass.gsub(/public\/stylesheets\/sass\//, '').gsub(/\.scss$/, '')
  next if basename.match(/^_/)   # skip includes
  css = "public/stylesheets/#{basename}.css"
  puts "Compiling #{sass} -> #{css}"
  system "sass #{sass} #{css}"
  system "git add #{css}"
end

Store the above Ruby script in .git/hooks/pre-commit, then give is execute permissions with chmod 755 .git/hooks/pre-commit.

This script will find all *.scss files and save their *.css equivalents. Then it also stages those files for the commit. Since this is a pre-commit script, the scenario is like this:

  1. You stage your files to commit, like usual and run the git commit command.
  2. Before git makes the actual commit, it runs the pre-commit script, which generates the necessare CSS files. You’ll a message like Compiling public/stylesheets/sass/app.scss -> public/stylesheets/app.css.
  3. With any changes to the CSS stages, your commit is made.

Won’t this spam a lot of CSS commits?

No, it won’t. Git is smart enough to see there are not changes to the content of the CSS file.

Does it work?

Yes, it works. The generated CSS files are deployed to Heroku like normal, static CSS files and will be served as such.

One more thing…

By default, Sass is set to generate CSS files when needed. Since the CSS file is already there Sass probably won’t try to generate it again in production. But, it might try so anyway and cause an exception.

To prevent Sass for generating CSS in production completely, add the following line to your config/environments/production.rb.

Sass::Plugin.options[:never_update] = true

Notes

There is one problem with this approach. If you have multiple developers or machines you work on, each and every one must have this pre-commit script installed to make it work. My advise would be to include the script in your project’s doc directory.