lein-git-version

June 19, 2019 · View on GitHub

Clojars Project

Derive your Leiningen project version from your Git history.

Motivation

Leiningen projects, in their heritage from Maven, list an explicit version as the 3rd element of a project.clj file. For instance

(defproject foo "1.3.4"
  ...)

There are a couple of problems with this. First of all, until the arrival of leiningen.release/bump-version for the lein release task, there was really no sane way to update the version of a property short of a sed script to rewrite the project.clj.

While bump-version is a mostly acceptable solution, it still relies on storing the version identifier in a file, attached to some point in the history of the repository.

The problem with sticking a version identifier in the filesystem/version history is that it becomes a source of merge conflicts. To avoid the merge conflicts, teams sometimes build complicated merge hooks or automated workflows. Now you have two problems.

In monorepo patterns ala lein-modules, the problems above become amplified. Additionally, versions for inter-monorepo dependencies are not very meaningful as all dependencis are built from the same source commit, rather than depending on a specific Maven version identifier. Using a commit ID or version control tag/label is a more accurate and useful.

Usage

Add:

;; Add in the git-version plugin
:plugins [[me.arrdem/lein-git-version "2.0.3"]
          ...]

By default, my incarnation of lein-git-version doesn't do anything to your project, except make some additional keys visible in the project map.

{:tag       ;; Name of the last git tag if any
 :ahead     ;; Number of commits ahead of the last tag, or 0
 :ahead?    ;; Is the head ahead by more than 0 commits
 :ref       ;; The full current ref
 :ref-short ;; The "short" current ref
 :branch    ;; The name of the current branch
 :dirty?    ;; Optional. Boolean. Are there un-committed changes.
 :message   ;; Optional. The last commit message when clean.
 :timestamp ;; Optional. The last commit date when clean.
}

If the keyword :project/git-ref or :project/git-ref-short is used in place of a version string, lein-git-version will update the version string of the project to instead reflect the ref or short ref.

For instance,

(defproject bar :project/git-ref
  :plugins [[me.arrdem/lein-git-version "2.0.3"]]
  ...)

or

(defproject baz :project/git-ref-short
  :plugins [[me.arrdem/lein-git-version "2.0.3"]]
  ...)

lein-git-version can also be used to compute a versions string, as an arbitrary function of the current git status by specifying a status-to-version function of the above status structure in the :git-version map of your project.clj.

For instance, lein-git-version uses an earlier version of itself to compute its own version.

(defproject me.arrdem/lein-git-version "_"
  :plugins [[me.arrdem/lein-git-version "2.0.3"]]

  :git-version
    {:status-to-version
       (fn [{:keys [tag version branch ahead ahead? dirty?] :as git}]
          (assert (re-find #"\d+\.\d+\.\d+" tag)
                  "Tag is assumed to be a raw SemVer version")
          (if (and tag (not ahead?) (not dirty?))
             tag
             (let [[_ prefix patch] (re-find #"(\d+\.\d+)\.(\d+)" tag)
                   patch            (Long/parseLong patch)
                   patch+           (inc patch)]
               (format "%s.%d-%s-SNAPSHOT" prefix patch+ branch))))
  }
  ...)

Will use the last tag as the version if level with a tag, otherwise will produce a SNAPSHOT one SemVer patch ahead of the previous tag qualified with the name of the current branch.

This enables your release workflow to consist simply of creating a tag and doing a deploy. No source changes are required.

Like its predecessors, lein-git-version can be used to generate a version file. Previous incarnations tried to lay down a version namespace. This incarnation can be configured to lay down an EDN file containing git information at a specified location relative to the root of the project.

For instance,

(defproject com.my-app/cares-what-version-it-is :project/ref-short
  :plugins [[me.arrdem/lein-git-version "2.0.3"]]
  ...
  :git-version {:version-file "resources/com/my_app/version.edn"
                :version-file-keys [:ref :version :timestamp]}))

will cause lein-git-version to make the specified directory, and lay down an EDN file containing all the selected project status information from the project map.

It's suggested that rather than target a directory under source control, a source control ignored eg "gen-resources/..." directory be used to isolate generated build artifacts from user sources. Remember to add the target to your project's :resource-paths!

History

This repo is a fork and to my taste a massive cleanup of cvillecsteele's lein-git-version which is itself an un-maintained alternative to michalmarczyk's original project.

License

Copyright © 2017 Reid "arrdem" McKenzie

Derived from lein-git-version © 2016 Colin Steele

Derived from lein-git-version © 2011 Michał Marczyk

Distributed under the Eclipse Public License, the same as Clojure.