Building Clojure projects with Nix#
Sat, 19 Aug 2017 23:23:07 +0000
Way back in May, or "the previous entry" as we like to call it in Telent Blog Time, I said
[using mvn2nix] doesn't actually work for Clojure/Leiningen projects. Look out for another blog entry soon that describes a whole completely different approach you can use there.
and then it turned out not to work, and then I thought I'd found a different approach, and then I got distracted, and then $WORK Happened, and, you know, Events. But here is an account of one way you can do it (at least, It Worked For Me) and some things I think I remember about the dead-ends I hit along the way.
Starting with the conclusion
Ingredients:
- a small amount of code that uses cemerick.pomegranate.aether to resolve all the transitive dependencies and create sha256 hashes, then write out a json file. You arrange somehow to call this code each time you add a dependency (I wrote a boot task ).
- a Nix derivation that reads the json file and makes Nix download each of those jars into its own store path
- a Nix package builder that sets
CLASSPATH
to include all those downloaded jars and then runs the Clojure and Clojurescript compilers on the project.
Try it. Send feedback
Why Leiningen didn't work
What we planned to do with Leiningen was not completely dissimilar. At "release time" we were going to
- use Pomegranate to find all the dependencies
- write that data to a file
then at "deploy time",
- have Nix read the file, download everything, and create a maven repo (symlink farm or whatever)
- run Leningen in "offline" mode
The trouble with that was that Leiningen appears to want to do dependency resolution all over again whenever it's run, which means that we had to download not only the required jar but in some cases one or more older versions too, just so it can persuade itself it has the right versions.
Kick me harder
Then I tried converting it to use boot. On the face of it this
should be much simpler because boot has a rather lovely with-cp
task
which lets one explicitly specify the CLASSPATH. In practice, though,
boot itself wants to auto-update and install stuff in
$HOME which is a problem
for NixOS autobuilders which run in a context where $HOME is set to a
non-existent directory.
I'm still using boot in development and to provide the scaffolding for the bit of code that creates the json file (I made it a boot task), but I can't use it to do the production build.
Next up, use NixOps to creat the rest of the machine on which the app is going to run