jkndrkn
jkndrkn

Reputation: 4062

What is a common convention for storing a project version number in a Clojure project?

Our team is working with Clojure projects that use deps.edn

We are used to NPM projects where we store the version number in the package.json file. It looks like Leiningen users may have stored the version number in a project.clj file before. Is there a community-wide convention for versioning Clojure projects? I have seen that people and official docs mention using Git tags but wondering if there is some popular way that developers using deps.edn store the version number in a file.

Thank you!

Upvotes: 1

Views: 606

Answers (4)

rdgd
rdgd

Reputation: 1446

I would say it depends on the intended use of the version number you would like to store, as well as the toolchain you are using. Since you're apparently not using Lein, this answer won't refer to any idioms related to Lein.

A convention in the deps.edn world, would be to store the project version data in the pom.xml file, which can be generated from your deps.edn file by invoking clojure -Spom in the same directory as your deps.edn file, and then editing the version number in that file manually or via a script. A fair amount of tooling in the deps ecosystem relies on the existence of a pom.xml file and directly or transitively reads version info from that file.

Unfortunately, XML isn't very ergonomic to interact with in Clojure, so if you need to parse the version in the running code of your project, this can be a bit of a nuisance. To achieve this, I have seen the independent maintenance of multiple versions before (not ideal) in the POM and in Clojure elsewhere, I have seen POM's read in as resources and the version parsed out, and I have seen libraries like versioneer used as well.

I personally would recommend using versioneer in your case, which will traverse system properties and then the pom.properties file, looking for a version. The source is straightforward and easy to understand. Both depstar and tools.build will generate a pom.properties file which versioneer can read.

Upvotes: 0

dorab
dorab

Reputation: 807

I don't know that there is an established convention, but this is what I use.

For an app (in contrast to a lib) what I've done is store the version in resources/version.edn (which is NOT kept under version control) and read that as part of the config of the app during startup. I use the major.minor.gitcount versioning, but you can change that easily to suit your needs. I use juxt/aero for configuration management, but any similar library or bespoke config management should work. Here is an excerpt from my build.clj.

(ns build
  "A build script for myapp."
  (:require [clojure.tools.build.api :as b]
            [org.corfield.build :as bb]))

(def major-version 0)
(def minor-version 2)
(def app 'com.myorg/myapp)
(def main 'com.myorg.myapp)
(def version (format "%d.%d.%s" major-version minor-version (b/git-count-revs nil)))
(def version-file "resources/version.edn")

(defn uber [opts]
  (b/write-file {:path version-file :content {:version-string version}})
  (-> opts
      (assoc :lib app :version version :main main)
      (bb/uber)))

Here is an excerpt from my config.edn (for juxt/aero)

{:app-config #merge [{:name "My App Name"
                      :credentials-file-path #or [#env CRED_PATH #join [#env HOME "/credentials.json"]]
                      :other-config "something"
                      }
                     #include "version.edn"]
}

And, the -main function looks like

(defn -main
  [& args]
  (try
    (log/info "")
    (log/info ">>>>>>>>>>>>>>>>> Starting Log >>>>>>>>>>>>>>>>>")
    (let [{:keys [options arguments exit-message ok?]} (validate-args args cli-options)]
      (log/debug "Options:" options)
      (log/debug "Arguments:" arguments)
      (cond exit-message
            (exit (if ok? 0 1) exit-message)
            ;;
            :else
            (let [system-config (config/system-config :prod options)]
              (log/info "Initializing My App.")
              (cond (not (config/config-valid? system-config)) (exit 1 (config/explain-config system-config))
                    (:version options) (exit 0 (str "Version: " (config/app-version-string system-config)))
                    :else
                    (start-the-system)
                    ))
    (catch Exception e (log/error e))))

Upvotes: 1

user17098470
user17098470

Reputation: 11

(defproject my.domain/service
  (-> "resources/service.VERSION" slurp .trim)
...

as for leiningen projects

Upvotes: 1

Alan Thompson
Alan Thompson

Reputation: 29958

I have only used Leiningen to publish JAR files so far. In this case, the version string is at the top of the file like

(defproject tupelo/demo "0.1.0-SNAPSHOT"

and it is most easily maintained by just editing project.clj.

Just last month, the final version of tools.build was released, which is the "official" way of publishing JAR files using the Deps/CLI system. I haven't had a chance to play with it yet but the example in the guide shows how to include the version string when building a JAR file. The example even uses an automation technique based on counting commits in git.

(ns build
  (:require [clojure.tools.build.api :as b]))

(def lib 'my/lib1)
(def version (format "1.2.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def jar-file (format "target/%s-%s.jar" (name lib) version))

(defn clean [_]
  (b/delete {:path "target"}))

(defn jar [_]
  (b/write-pom {:class-dir class-dir
                :lib lib
                :version version
                :basis basis
                :src-dirs ["src"]})
  (b/copy-dir {:src-dirs ["src" "resources"]
               :target-dir class-dir})
  (b/jar {:class-dir class-dir
          :jar-file jar-file}))

Upvotes: 6

Related Questions