As I wrote about the other day, my site’s styles are written using Sass, a Ruby-based CSS pre-compiler. When you’re the only one working on a site, compiling Sass files locally and committing the generated CSS isn’t the worst thing in the world. When you’re working in a team environment however, it’s necessary to consider that several developers of different experience levels may touch a site throughout its lifecycle. What would happen if a developer who had never used Sass needed to make an update to a site? How long would it be before someone starts complaining that the change they made to the CSS file was overwritten after the next compilation of the corresponding .scss file?
This technique has one goal: keep generated CSS files out of the git repository. In order to do this, we’ll need to make sure of a few things:
- Generated files are explicitly blocked from the repo (to prevent someone who doesn’t know any better from committing them)
- Automatically recompile our SASS files into CSS upon deployment (This assumes that you have Ruby, RubyGems, and Sass running on your target machine).
Keeping generated files out of the Git repository
This should be as simple as updating (or creating) your .gitignore file and explicitly excluding the generated files you want to block. If you want to be a real hard-ass, you could add *.css
, but if you go this route be sure to let in any CSS files that aren’t generated by SASS (remember: adding a !
before a rule in the .gitignore
file will allow that file to be tracked):
1 2 3 4 5 6 7 |
# This will prevent all .css files from being added to the repository *.css # These files aren't generated by Sass !css/my-plugin.css !css/default-cms-styles.css # etc. |
If you’re using a third-party tool/framework/library that has its own stylesheets (for instance: WordPress) you’ll need to explicitly allow those styles into the repo. For that reason I’d recommend blacklisting the generated files instead of whitelisting the non-generated.
Automatically recompile our Sass upon deployment
This is where the magic happens. Git has a nice little feature called “Git Hooks” which allow us to execute scripts at different points throughout the repository lifecycle. You can use git hooks to do things like validate commit message formatting, strip trailing whitespace, email someone whenever code is deployed, etc. In this case, we’ll be using the post-merge
hook, found in .git/hooks/post-merge
.
According to the manual, the post-merge
hook fires when a git merge
is invoked on a repository (generally speaking, git pull
is the same as a git fetch
followed by a git merge
) and, in my experience, only when updates have actually been made since the last pull (i.e. if you receive the “Already up-to-date.” response after performing a git pull
, your hook will not run). This means that by tying our SASS compilation into the post-merge
hook we can be certain that our CSS files will be generated if and only if something has changed in the repository.
Since I wanted to explicitly declare which Sass files to compile and their resulting filenames and keep this script under version control, I chose to put my calls to the Sass gem in a bash file within my repository:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env bash # Make sure that the 'sass' command exists # @see http://stackoverflow.com/a/677212/329911 command -v sass >/dev/null 2>&1 || { echo >&2 "Sass does not appear to be available. Unable to re-compile stylesheets"; exit 1; } # Define our paths and stylesheets echo "Re-compiling stylesheets..." cd wp-content/themes/grunwell2012/css/ sass style.scss style.css --style compressed echo "style.scss -> style.css (compressed)" sass ie8.scss ie8.css --style compressed echo "ie8.scss -> ie8.css (compressed)" echo "Sassification is complete" exit 0 |
This is a rather rudimentary script (I don’t have much experience with bash scripting), so improvements (like error checking) are more than welcome, but this gets the job done. When I run a git pull
on production, I get the regular merge-related messaging followed by:
1 2 3 4 |
Re-compiling stylesheets... style.scss -> style.css ie8.scss -> ie8.css Sassification is complete |
Then in my git hook:
1 2 3 4 |
#!/usr/bin/env sh # Generate CSS from SASS bash wp-content/themes/grunwell2012/utilities/recompile_sass |
If I wanted to I could certainly have put my entire recompile_sass script into .git/hooks/post-merge but, as I mentioned before, I wanted it to be tracked under version control. It’s also worth noting that when the git hook executes it does so from the root of the git repository, so you’ll need to set paths to any scripts appropriately.
Extra Credit
If you combine this technique with my tip on using git checksums to invalidate browser caches you can ensure that not only are your generated CSS files automatically regenerated upon deployment but they are also re-downloaded by browsers when updates have been made.
Wrapping Up
For many teams generating and committing CSS files from SASS locally may be just fine, but I’m a big proponent of keeping generated files out of version control. With minimal configuration you can keep your repos clean, prevent unaware developers from unknowingly editing a generated file, and reap the benefits of CSS pre-processors.
How do you and your team handle generated CSS? Is there a better way to pull this off? Please let me know in the comments!
Chris Van Patten
We use Capistrano tasks at Van Patten Media to handle this. It’s admittedly not the ideal way to handle it (your method is a bit smoother) but it’s imperative in our server environment that the compilation happens locally and not server-side.
Our compilation task actually creates a temporary clone of the repo in question (so you know you’re getting the current revision, and not your working copy), then compiles it and scp’s it from that temporary directory.
The full code for this is on Github, for any interested parties!
Peter Sorensen
Thanks! Exactly what I was looking for.
Jackson Darlow
I tried this, and I just get:
“remote: hooks/post-receive: 21: sass: not found”
I’ve made sure to add sass to ~/.bashrc for my user, and I can sass in CLI just fine as the user, but I still get that error when I push.
Ubuntu 10.04.4 LTS