Saturday, April 27, 2013

Heroku: Build CSS using Compass/SASS for Clojure application

For our latest project (photo duel site) we have decided to use the Clojure language. In order to make it easy for us at the beginning, we chose Heroku to host our application so that we don't have to manage deployment, hosting and database by ourselves.

So far I'm really happy about this choice, but nevertheless there were a few issues along the way. In order to reuse some CSS stuff and knowledge from our other project (Albumino) we had to find out how to compile our CSS files using Compass. So how do you do that when using Heroku? First let's look how your Clojure application is being built.

Heroku build process

To deploy your application to Heroku you push all your source files to their Git repository. Heroku then builds and deploys your application. This process is language specific and so called buildpacks are used to support different languages.

Clojure is handled by heroku-buildpack-clojure which builds project using Leiningen. But it's to no surprise that it doesn't know how to build CSS files. :) Fortunately Clojure buildpack is easily extensible using a custom build script which you can put into bin/build.

For standard application it should contain:

#!/bin/bash

lein with-profile production compile :all

Using Compass gem

In order to build our CSS we need compass Ruby gem. By default it's not installed on the machine where Heroku performs builds, so we need to install it using gem install compass. It will fail due to permissions problem as it will try to install itself into system-wide gem directory which is read-only. But that could be configured using the GEM_HOME environment variable.

#!/bin/bash

export GEM_HOME=$PWD/gems

mkdir -p $GEM_HOME
gem install compass

$GEM_HOME/bin/compass compile scss-dir css-dir

lein with-profile production compile :all

Using Bundler

To make build processes more predictable and repeatable you should use Bundler to manage required gems. It will make sure that you use the same versions of the gems for each build of your application.

You simply call bundle init in the project's root directory and it will create an example Gemfile file. In our case we will modify it so that it requires the compass gem.

source "https://rubygems.org"

gem "compass"

bundle install will than install all the required gems specified in Gemfile and store their versions into Gemfile.lock file for later invocations.

In order to use bundler during build, you should modify your bin/build script so that it installs bundler and calls compass using bundle exec wrapper.

gem install compass

$GEM_HOME/bin/compass compile scss-dir css-dir

Should be changed to:

LC_ALL=en_US.UTF-8 gem install bundler   # LC_ALL required by bundler

$GEM_HOME/bin/bundle install
$GEM_HOME/bin/bundle exec compass compile scss-dir css-dir

When you commit all the files (i.e. bin/build Gemfile Gemfile.lock) and push this change to Heroku you will find that the build fails. The reason is that due to the presence of the Gemfile Heroku thinks that your application is using Ruby and not Clojure. The fix is pretty easy though. You force Heroku to use Clojure buildpack using heroku config:set command.

heroku config:set BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-clojure

Your next push should succeed.

Thanks gfredericks and pandeiro (of #clojure fame) for the grammar check. :)

No comments:

Post a Comment