Kaven is a small Clojure library for deploying JARs and their POMs to Maven repositories, backed by Maven Resolver
(Aether). It is designed to work well in tools.build pipelines.
Add Kaven to your deps.edn:
{:aliases {:build {:extra-deps {com.kepler16/kaven {:mvn/version "RELEASE"}
io.github.clojure/tools.build {:mvn/version "0.10.5"}}
:extra-paths ["./build"]
:ns-default build}}}
;; ./build/build.clj
(ns build
(:require
[k16.kaven.deploy :as kaven.deploy]))
(def ^:private clojars-credentials
{:username (System/getenv "CLOJARS_USERNAME")
:password (System/getenv "CLOJARS_PASSWORD")})
(defn release [_]
(kaven.deploy/deploy
{:jar-path "target/lib.jar"
:repository "clojars"
:repositories {"clojars" {:credentials clojars-credentials}}}))
Then execute it with
clojure -T:build release
This deploys the JAR at target/lib.jar with it's embedded POM artifact to the configured clojars repository.
k16.kaven.deploy/deployDeploys a JAR and its POM to a target Maven repository.
Required options:
:jar-path - Path to the JAR on disk.:repository - Repository id (string) or full repository map.Optional options:
:pom-path - Path to a POM on disk, or within the JAR. If not set, Kaven automatically resolves the POM from inside
the JAR using well-known POM paths.:repositories - Map of repository id to repository config, merged with repositories discovered from the POM and
~/.m2/settings.xml.:lib - Symbol in the form group/artifact, used to override inferred coordinates.:version - Explicit artifact version override. Resolved from POM if not specified.Returns:
Map with deployed artifacts, e.g. {:artifacts [{:group ... :artifact ...}]}.
One of the features of Kaven is its ability to auto resolve most required information for deploy. This includes the POM file itself.
If no :pom-path is provided, or the :pom-path does not point to a valid file on disk, then Kaven will attempt to
resolve the POM from inside the jar at the provided :jar-path.
POM resolution works by using the first result returned from the following search:
:pom-path is provided and is a valid file on disk, read it.:pom-path is provided and it is not a file on disk, then search for a resource within the jar at this path.:lib is provided, try find a POM file at zip://<jar-path>/META-INF/maven/<group-id>/<artifact-id>/pom.xmlzip://<jar-path>/META-INF/maven/.*/pom.xmlIf no POM can be found, deployment fails.
Kaven needs three Maven coordinates to publish your artifacts:
groupIdartifactIdversionThese are resolved in a predictable order.
For groupId and artifactId:
:lib is provided, it is treated as the source of truth.For version:
:version is provided, it is treated as the source of truth.The resolved coordinates are then applied to both uploaded artifacts:
.jar).pom):lib should be a fully-qualified symbol in the form <group-id>/<artifact-id>, for example:
:lib 'com.example/my-lib
This makes it easy to keep using the JAR's embedded POM while overriding only the fields you care about.
(deploy {:jar-path "target/lib.jar"
:repository "clojars"
;; Override only the version, infer group/artifact from POM
:version "1.2.3"})
(deploy {:jar-path "target/lib.jar"
:repository "clojars"
;; Override all coordinates
:lib 'com.example/renamed-artifact
:version "2.0.0"})
Kaven builds repository configuration from three sources:
A set of repository configurations are constructed by analysing the POM file and extracting the section.
Credentials from ~/.m2/settings.xml are loaded and merged with repositories found in the POM file. This is done by
referencing the repositories :id field.
Repositories configured/overriden through the :repositories opt. Anything specified here will be merged with
automatically discovered repositories.
The chosen target repository to deploy to is selected by setting the :repository opt to the desired repository ID or
by providing the full repository config to :repository.
The :repository config option also accepts a partial repository map for you to specify the deploy target directly,
inline.
;; If no ID is specified, you must specify both :url and :credentials
{:url "https://repo.example.com/releases"
:credentials {:username "username"
:password "password"}}
;; If you specify an :id this config may be partial and will be merged with any Repository config
;; loaded from ~/.m2/settings.xml or the POM.
;;
;; You can omit :url and/or :credentials, and the resulting map will be merged.
{:id "repo-id"
;; optional
:url "https://repo.example.com/releases"
;; optional
:credentials {:username "username"
:password "password"}}
If you have a username/password for your target repository (Clojars, in this example) configured in settings.xml
then you only need to specify the repository id.
(deploy {:jar-path "target/lib.jar"
:repository "clojars"})
If you want to explicitly specify the repository credentials to use, you can modify the :repositories map directly.
This will merge the repository partial with any globally loaded properties for the repository with the same id.
(deploy {:jar-path "target/lib.jar"
:repository "clojars"
;; The clojars repository URL is typically always available - POMs generated with Clojure have the Clojars
;; repository included - so we don't need to specify the URL.
:repositories {"clojars" {:credentials {:username "username"
:password "password"}}}})
Deploy to a third-party repository, not configured anywhere globally. Let's use Github for this example.
For this use-case we can just specify the entire repository config directly.
(deploy {:jar-path "target/lib.jar"
:repository {:id "github"
:url "https://maven.pkg.github.com/<github-org>/<github-repo>"
:credentials {:username (System/getenv "GITHUB_USERNAME")
:password (System/getenv "GITHUB_TOKEN")}}})
:lib and :versionBy default, Kaven will attempt to infer the lib name (<group>/<artifact>) and the version from the POM file. If you
want to rather specify this directly, you can just set these fields yourself.
(deploy {:jar-path "target/lib.jar"
:repository "clojars"
:lib 'com.kepler16/kaven-2
:version "0.0.1-SNAPSHOT"})
Again, by default, Kaven will attempt to discover the POM file from the JAR directly. If you would rather use a
different POM or specify it yourself you can use :pom-path instead.
(deploy {:jar-path "target/lib.jar"
:pom-path "target/META-INF/maven/com.kepler16/kaven/pom.xml"
:repository "clojars"})
Prerequisites:
justTo run tests you need to standup the reposilite Docker instance. This is used to test deploys against a 'real' maven
repository:
docker compose up -d
just test
Kaven uses maven-resolver 1.8.2 and Maven 3.8.8 to align with the dependency versions used by tools.build. I tried
to initially use the latest versions of these maven coordinates but it causes conflicts with the versions used by
tools.build
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |