Liking cljdoc? Tell your friends :D

Kaven

A Clojure API for interacting with Maven repositories

Clojars Project

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.

Installation

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}}}

Quick Start

;; ./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.

Documentation

API

k16.kaven.deploy/deploy

Deploys 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 ...}]}.

Automatic POM File Discovery

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:

  1. If :pom-path is provided and is a valid file on disk, read it.
  2. If :pom-path is provided and it is not a file on disk, then search for a resource within the jar at this path.
  3. If :lib is provided, try find a POM file at zip://<jar-path>/META-INF/maven/<group-id>/<artifact-id>/pom.xml
  4. Finally, the jar resources are scanned for the first pom file in zip://<jar-path>/META-INF/maven/.*/pom.xml

If no POM can be found, deployment fails.

Maven Coordinate Discovery

Kaven needs three Maven coordinates to publish your artifacts:

  • groupId
  • artifactId
  • version

These are resolved in a predictable order.

For groupId and artifactId:

  1. If :lib is provided, it is treated as the source of truth.
  2. Otherwise, values are read from the resolved POM file.

For version:

  1. If :version is provided, it is treated as the source of truth.
  2. Otherwise, it is read from the resolved POM file.

The resolved coordinates are then applied to both uploaded artifacts:

  • the primary JAR artifact (.jar)
  • the generated POM artifact (.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"})

Repository Configuration

Kaven builds repository configuration from three sources:

  1. A set of repository configurations are constructed by analysing the POM file and extracting the section.

  2. 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.

  3. 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.

Repository Config Format

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"}}

Examples

Globally Configured Repository

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"})

Specify Credentials Explicitly

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

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")}}})

Inline the :lib and :version

By 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"})

Use a different POM file

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"})

Development

Prerequisites:

  • just
  • Docker (for integration tests)

Running tests

To 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

A Note On Dependency Versions

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

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close