Easier Clojure Releases without Commits

Simple and clean releases by tagging the commit you want, instead of making new commits.

Advertisements

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:

  1. commit/merge to your production branch
  2. wait for green light on build
  3. tag the commit with your release version
  4. 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!

Author: brabster

Software developer in the North of England

3 thoughts on “Easier Clojure Releases without Commits”

  1. That’s a neat simple strategy for getting dynamic versions. Also checkout `lein-v` which is way of deriving versions from git tags & bumping versions without writing to project.clj!

    1. Thanks @rymndhng, I will check out `lein-v`, it might just be the Leiningen plugin that I need to perform this process without code in my `project.clj` or a special deploy script!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s