Have you ever tried to use the lein-release plugin to release an artifact into Clojars? It works by automatically updating version information in your project.clj
and adding commits to your source control system. Those commits can then be used with your build system to drive new builds and push a final artifact into Clojars.
I always found this approach unpalatable.
- It’s complicated – there are nine actions that the plugin takes by default. Nine!
- Version details are updated twice – the
-SNAPSHOT
suffix is removed, committed, then updated again to update the version and add-SNAPSHOT
back on before committing again – but that needs me to say what kind of update the next release is going to be! - I don’t like having to run automation on my local machine (or give my build system write credentials to my source control system!).
There are other irritations with this approach but rather than rant, I’ll share what I think is a better way. This is the approach I’ve been using with Crucible for a few months now and it’s been working well.
A Simpler Way
If you set the version to be looked up from an environment variable, you don’t need the actual source code to have the version details you intend to deploy hard-coded into a git commit. That’s really easy to do with Leiningen because the project.clj
gets evaluated when you run Leiningen. Here’s an example from the Crucible project.
(defproject crucible (or (System/getenv "PROJECT_VERSION") "0.0.0-SNAPSHOT") :description "AWS Cloudformation templates in Clojure" ...)
I’m setting a default version of 0.0.0-SNAPSHOT
here, so when I’m working locally I get something I’ll recognise. The version resolves to the value of the PROJECT_VERSION
environment variable when it’s set.
As Crucible builds on Travis I can trigger a build when I apply a git tag, and I can see the label of the tag in the build environment, as TRAVIS_TAG
. I have a small script that inspects the value of the TRAVIS_TAG
environment variable and sets PROJECT_VERSION
appropriately before running lein deploy
.
This release process is as simple as tagging an existing commit with the label you want to release as, which you can do after you see a green build. No extra commits. No extra builds where there have been changes in source control. No write access for your build system. Nothing to merge with your next changes. Just a tag. The final release is done by your build system, in Crucible’s case Travis, using Leiningen’s standard Clojars deployment capabilities.
Summarising
Once you’ve made your project.clj version dynamic, added your deploy script and told your build system when and how to deploy your code, you only have two actions (that aren’t waiting) to do each actual release:
- commit/merge to your production branch
- wait for green light on build
- tag the commit with your release version
- wait for build to deploy your new version to Clojars
Taking this process and Github releases, it’s so simple I can perform a release on my phone while I’m standing on the train. That can genuinely be useful if I get a great PR in!
Have fun!