Solving version conflicts with lein-pedantic
A couple months ago I wrote do not use version ranges in project.clj. While this helps reduce confusion about what version of dependencies a project will use, it does not eliminate all sources of confusion.
Different dependency versions
(defproject sample "0.0.1" :dependencies [[org.clojure/clojure "1.4.0"] [com.cemerick/friend "0.0.9"] [noir "1.3.0-beta9"]])
This project graph has two versions of
ring/ring-core that it wants
[ring/ring-core "1.0.2"] and
[ring/ring-core "1.1.0"] The dependency resolution in lein/maven/aether will end up
choosing the one closest to the project root, which in this case is
[ring/ring-core "1.0.2"]. This causes a problem because
wants to use
ring.middleware.head which was added in 1.1.0.
(defproject sample "0.0.1" :dependencies [[org.clojure/clojure "1.4.0"] [ring/ring-core "1.0.2"] [noir "1.3.0-beta9"]])
A similar problem can occur for top level dependencies.
To help solve these problems, I've written
lein-pedantic. It is a
plugin for leiningen that will reject
dependency graphs with common errors that would be confusing for
users. In addition, it tries to provide a helpful command to use to
fix the issue. For example, the
issue would result in:
Failing dependency resolution because: [com.cemerick/friend "0.0.9"] -> [ring/ring-core "1.0.2"] is overruling [noir "1.3.0-beta9"] -> [compojure "1.0.4"] -> [ring/ring-core "1.1.0"] Please use [com.cemerick/friend "0.0.9" :exclusions [ring/ring-core]] to get [ring/ring-core "1.1.0"] or use [noir "1.3.0-beta9" :exclusions [ring/ring-core]] to get [ring/ring-core "1.0.2"].
It is designed to use leiningen's hook system, and only requires being
added to the
:plugins vector. No extra tasks are added, it just
runs anytime leiningen attempts to resolve dependencies.
It works by determining the dependency graph for each of the project's dependencies on their own, and comparing them to the collective dependency graph. It will reject the graph if
- A top level dependency is overruled by another version.
- A transitive dependency is overruled by an older version.
Together, these help identify most of the problems "soft/recommended" dependencies cause. In addition, lein-pedantic does not change the dependency resolution rules, so the final dependencies put in project.clj will work the same in maven or another aether based system.