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:
- You stage your files to commit, like usual and run the
git commit
command. - Before git makes the actual commit, it runs the
pre-commit
script, which generates the necessare CSS files. You’ll a message likeCompiling public/stylesheets/sass/app.scss -> public/stylesheets/app.css
. - 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.