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.
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
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 path-to-scss-dir path-to-public-css-dir
lein with-profile production compile :all
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 path-to-scss-dir path-to-public-css-dir
Should be changed to:
LC_ALL=en_US.UTF-8 gem install bundler # for some reason bundler fails with default LC_ALL
$GEM_HOME/bin/bundle install # make sure that required gems are available
$GEM_HOME/bin/bundle exec compass compile path-to-scss-dir path-to-public-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 grammar check. :)