Frequently, inside my Clojure applications, I would put project
version string picked from Leiningen project.clj
file. This way, it
would solve me these problems:
- User can easily report what application version was affected with some issue.
- Version number is updated only once.
- Leiningen
lein change version leiningen.release/bump-version
(automatic version number increase) works as well.
In the past, used this code:
(defn get-version
"Return application version, depending if run from REPL or jar.
If fails, returns nil."
[app]
(if-let [str (System/getProperty (format "%s.version" app))]
str
(let [path (format "META-INF/maven/%s/%s/pom.properties" app app)
in (ClassLoader/getSystemResourceAsStream path)]
(when in
(with-open [in in]
(let [p (java.util.Properties.)]
(try
(.load p in)
(.getProperty p "version")
(catch Exception _))))))))
and would call it with:
(get-version <project-name>)
It worked well for my usecase: version string could be obtained from REPL, but also from uberjar as well.
In one occasion I had to build pure binary using
GraalVM compiler which makes above
approach useless - there is no jar to unpack and there is no
pom.properties
file. Also, given code isn't free - it had to
load and parse properties file just for displaying version string.
Nice thing about Leiningen is that it already publish project version
value in compilation phase via <project-name>.version
property
and thanks to Clojure macros, it can be exploited easily:
;; assume the project is called foo
(defmacro get-version
"Get project version in compilation phase. Only applicable
to Leiningen projects."
[]
`~(System/getProperty "foo.version"))
That should be it. Just call it whenever version string is needed.
Because the value is static (it is evaluated in compilation phase), it will work with uberjar, GraalVM compiled binary and from REPL without any problems. Best of all, it keeps resources usage to minimum.