Skip to content

dcj/build-provenance

Repository files navigation

build-provenance

Clojars Project babashka compatible md-docs build-provenance

Tiny Clojure utility for embedding git provenance metadata into a JAR at build time, and reading it back at runtime.

What it does

When called from a tools.build build.clj immediately before the b/jar step, this lib emits two small parallel resource files containing the same provenance data:

META-INF/<group>/<artifact>/build-provenance.edn   ← canonical Clojure shape (kebab-case keys)
META-INF/<group>/<artifact>/build-provenance.json  ← for non-Clojure consumers (camelCase keys)

Both contain seven fields:

;; build-provenance.edn
{:artifact           "com.dcj/build-provenance"
 :version            "0.1.0"
 :commit             "0d84c2df7a50c8783d895ad0ca12c1ff018b23c6"
 :commit-abbreviated "0d84c2d"
 :branch             "main"
 :describe           "v0.1.0-2-g0d84c2d"
 :dirty              false}
// build-provenance.json
{
  "artifact": "com.dcj/build-provenance",
  "version": "0.1.0",
  "commit": "0d84c2df7a50c8783d895ad0ca12c1ff018b23c6",
  "commitAbbreviated": "0d84c2d",
  "branch": "main",
  "describe": "v0.1.0-2-g0d84c2d",
  "dirty": false
}

A consumer can read this back at runtime to identify exactly which build of the lib is loaded — useful for diagnostics, deployment audits, and provenance round-tripping into SBOM-adjacent contexts. Clojure consumers read the EDN; non-Clojure tooling (jq, CI bash, supply-chain scanners) reads the JSON.

Install

deps.edn:

{:deps {com.dcj/build-provenance {:mvn/version "0.2.0"}}}

Usage

From build.clj

(ns build
  (:require [clojure.tools.build.api :as b]
            [build-provenance.write :as bp]))

(def lib 'com.example/my-library)
(def version "1.0.0")
(def class-dir "target/classes")

(defn ci [opts]
  (b/copy-dir {:src-dirs ["src" "resources"] :target-dir class-dir})
  (bp/write! {:lib lib :version version :class-dir class-dir})
  (b/jar {:class-dir class-dir
          :jar-file (format "target/%s-%s.jar" (name lib) version)}))

Reading at runtime

(require '[build-provenance.read :as bp])

(bp/read 'com.example/my-library)
;; => {:artifact "com.example/my-library" :version "1.0.0" :commit "..." ...}
;; or nil if no build-provenance.edn is on the classpath for that artifact

Discovering every instrumented artifact

When you don't want to enumerate lib coords up front — e.g. a service /health endpoint that lists every build identity in the running JVM — use discover:

(require '[build-provenance.read :as bp])

(bp/discover)
;; => [{:artifact "com.dcj/build-provenance" :version "0.2.0" :commit "..." ...}
;;     {:artifact "com.example/my-library"   :version "1.0.0" :commit "..." ...}
;;     ...]

discover walks the classpath, finds every META-INF/<group>/<artifact>/build-provenance.edn resource, and returns the parsed maps sorted by :artifact. Returns an empty vector if no instrumented JARs are present. Useful for diagnostics, deployment audits, and SBOM-adjacent reporting.

The runtime reader is EDN-only — Clojure consumers don't need a JSON parser. Non-Clojure consumers can read the parallel .json resource directly:

unzip -p target/foo.jar META-INF/com.example/my-library/build-provenance.json | jq .

Shell CLI

A Babashka-based CLI ships in this repo for shell-side inspection of provenance metadata in JARs and exploded class-dirs — no JVM startup required. The same entry point also runs under the JVM (clj -M -m build-provenance.cli ...).

Install

Via bbin:

bbin install io.github.dcj/build-provenance --as bp

Or clone and run via the bb task:

git clone https://github.com/dcj/build-provenance && cd build-provenance
bb run bp --help

bp inspect [--json] FILE...

Print provenance from each JAR or directory. Default output is EDN sorted by :artifact; --json emits a JSON array of the canonical .json shapes (camelCase keys) read directly out of each JAR — no EDN→JSON conversion in the CLI, so the bytes are bit-identical to what is embedded.

bp inspect target/build-provenance-0.2.0.jar
bp inspect ~/.m2/repository/com/example/foo/1.0.0/foo-1.0.0.jar
bp inspect --json target/*.jar | jq 'sort_by(.artifact)'

bp discover [--json] DEPS_EDN

Resolve a deps.edn to a classpath via clojure -Spath, then list every instrumented artifact in the resolved set.

bp discover deps.edn
bp discover --json deps.edn | jq 'map({artifact, version, commit})'

For an arbitrary classpath without writing a deps.edn:

clojure -Spath | tr ':' '\n' | xargs bp inspect

Marking your library

If your library embeds build-provenance, advertise it in your README so downstream consumers know bp inspect and (build-provenance.read/discover) will surface it.

Drop this snippet near the top of your README:

[![build-provenance](https://img.shields.io/badge/build--provenance-included-blue)](https://github.com/dcj/build-provenance)

This JAR embeds [build-provenance](https://github.com/dcj/build-provenance) metadata at `META-INF/<group>/<artifact>/build-provenance.{edn,json}`. Inspect with `bp inspect path/to.jar` from a shell, or `(build-provenance.read/read 'group/artifact)` from a Clojure REPL.

Use the badge URL verbatim — the convention only works if every consumer's badge links back to this repo, so a clicker lands on documentation explaining what the metadata is for.

Failure modes

Best-effort. If git is unavailable on PATH, the project is not a git working tree, or HEAD is detached:

  • Unknowable string fields (:commit, :commit-abbreviated, :branch, :describe) are emitted as nil.
  • :dirty is conservatively set to true (we cannot prove the tree is clean).
  • The EDN is still written; the writer never throws on a git failure.

The writer DOES throw on programmer errors:

  • :lib must be a qualified symbol like 'group/artifact.
  • :version must be a string.
  • :class-dir (for write!) must be a string.

Why both EDN and JSON

The two formats are sourced from a single collect call inside write!, so they cannot drift. Each format optimizes for its audience:

  • EDN — canonical for Clojure tooling. Native (edn/read-string), kebab-case keys, no parser dependency on the reader.
  • JSON — canonical for everything else. camelCase keys per JSON convention. Hand-rolled writer (no JSON dependency in this library) since the schema is a flat seven-field map of strings/booleans/nils.

Resource-path convention

META-INF/<group>/<artifact>/build-provenance.{edn,json} mirrors Maven's convention of META-INF/maven/<group>/<artifact>/pom.xml. Per-artifact pathing matters: when multiple build-provenance-instrumented JARs sit on the same classpath, each artifact's metadata is independently addressable.

License

MIT License — Copyright (c) 2026 Clark Communications Corporation

About

Tiny Clojure utility for embedding git provenance metadata into a JAR at build time

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors