A Comprehensive Guide to Building and Publishing a Clojure Library with Slim

In this detailed tutorial, we will explore how to build a Clojure library from the ground up and subsequently publish it to Clojars, utilizing the innovative build tool Slim that I recently introduced. Slim is designed to simplify the process of managing Clojure projects, minimizing the complexity typically associated with such tasks.
Publishing an Existing Library
If you already have a library that you'd like to share with the community by publishing it to Clojars, Slim facilitates this process seamlessly with minimal setup required. Assuming you have a deps.edn
file already present in your project root, the first step is to incorporate Slim into your dependencies. You can accomplish this by modifying your deps.edn
file to include the necessary configurations:
{:aliases {:build {:deps {io.github.abogoyavlensky/slim {:mvn/version "0.3.2"} slipset/deps-deploy {:mvn/version "0.2.2"}} :ns-default slim.lib :exec-args {:version "0.1.0" :lib io.github.githubusername/libraryname :url "https:Be sure to replace the placeholders in the :exec-args
map with your librarys specific details. This simple adjustment allows you to set the environment variables CLOJARS_USERNAME
and CLOJARS_PASSWORD
accordingly, and then execute the command clojure -T:build deploy
to publish your library to Clojars.
For those who prefer to test their library before a full release, you can initially publish a snapshot version by running the command clojure -T:build deploy :snapshot true
. More options are available, and I encourage you to consult the documentation for a deeper understanding.
Publishing a New Library
For developers who are eager to create a brand new library based on fresh ideas, I've developed a template called clojure-lib-template
that streamlines the process of setting up a new Clojure library. This template comes with numerous benefits, including:
- A minimalistic design that is easy to grasp
- Integrated GitHub Actions workflows for continuous integration and deployment, which include publishing to Clojars
- A comprehensive setup for development tools, including linting, code formatting, dependency management, and testing
- Preconfigured build and deployment processes utilizing Slim
- Default licensing under the MIT License
Example Library Idea
To illustrate the process clearly, we will create a simple library that includes a function designed to find an available port on your local machine, aptly named freeport
.
Creating a New Project
The first step in developing this project is to establish its structure. If you haven't already installed deps-new
, you can do so using the following command:
clojure -Ttools install-latest :lib io.github.seancorfield/deps-new :as new
Next, create a new project with:
clojure -Sdeps '{:override-deps {org.clojure/clojure {:mvn/version "1.12.0"}}}' -Tnew create :template io.github.abogoyavlensky/clojure-lib-template :name io.github.yourusername/freeport
Remember to replace yourusername
with your actual GitHub username. If you already have Clojure version 1.12.x installed, you can skip the first argument and run the command directly.
Executing this command will generate a new project folder named freeport
containing the following structure:
.clj-kondo/ # Clojure linting configuration .github/ # GitHub Actions workflows and configurations dev/ # Development configuration directory user.clj # User-specific development configuration src/ # Source code directory freeport # Main namespace directory core.clj # Main namespace file test/ # Test files directory freeport # Test namespace directory core_test.clj # Test namespace file .cljfmt.edn # Formatting configuration .gitignore # Git ignore rules .mise.toml # mise-en-place configuration with system tools versions bb.edn # Babashka tasks configuration deps.edn # Clojure dependencies and aliases LICENSE # License file CHANGELOG.md # Changelog file README.md # Project documentation
Subsequently, youll want to install the required system dependencies. You can do this using the mise
tool:
mise trust && mise install
Alternatively, if you prefer a more manual approach, you can consult the .mise.toml
file to determine the necessary versions and install them accordingly.
Next, initialize a Git repository and make your first commit:
git initgit add .git commit -am 'Initial commit'
To ensure everything is functioning correctly, run checks for linting, formatting, outdated dependencies, and tests using the following command:
bb check
Should there be any changes resulting from formatting or dependency checks, be sure to commit those updates.
Adding Implementation
Lets now implement the core functionality for our library. Open the src/freeport/core.clj
file and insert the following code:
(ns freeport.core (:import (java.net ServerSocket)))(defn get-freeport [] (with-open [socket (ServerSocket. 0)] (.getLocalPort socket)))
Next, youll want to write tests to verify our implementation. Open the test/freeport/core_test.clj
file and add the following:
(ns freeport.core-test (:require [clojure.test :refer :all] [freeport.core :as core]) (:import [java.net ServerSocket]))(defn port-free? [port] (try (with-open [_socket (ServerSocket. port)] true) (catch Exception _ false)))(deftest test-port-is-free-ok (let [port (core/get-freeport)] (is (true? (port-free? port))) (is (<= 1024 port 65535))))
This test checks that the port returned is indeed available and falls within the commonly accepted range of 1024-65535. Run the tests and other checks again to confirm that everything is functioning as expected:
bb check
Once all checks pass, commit your changes:
git commit -am 'Add freeport implementation'
Publishing to Clojars
Before publishing your library, ensure to update the library details within your deps.edn
file. Specifically, you'll need to amend the :description
and :developer
fields:
{;... :aliases {:build {:deps {io.github.abogoyavlensky/slim {:mvn/version "0.3.2"} slipset/deps-deploy {:mvn/version "0.2.2"}} :ns-default slim.lib :exec-args {:version "0.1.0" :lib io.github.yourusername/freeport :url "https:Next, you must configure your Clojars credentials by setting them as environment variables:
export CLOJARS_USERNAME=yourusernameexport CLOJARS_PASSWORD=yourpassword
With that done, you can publish a snapshot version to Clojars for initial testing:
bb deploy-snapshot
After testing the snapshot in your project and ensuring everything operates correctly, you can proceed to publish a release version:
bb deploy-release
Publishing from GitHub Actions
While deploying from your local system is effective, automating the process through continuous integration and continuous deployment (CI/CD) significantly enhances efficiency. The template weve been using is already configured with GitHub Actions workflows tailored for this purpose.
To finalize this setup, simply establish the environment variables CLOJARS_USERNAME
and CLOJARS_PASSWORD
in your GitHub repository settings. The workflow is structured to:
- Publish a snapshot version upon every push to the main branch
- Publish a release version upon every tag push
Each time you push a new commit to the main branch, the workflow will automatically execute, publishing a snapshot version to Clojars within approximately a minute.
When your library reaches a state where its ready for a new version release, update the version number in your deps.edn
file:
{;... :aliases {:build {;... :exec-args {:version "0.1.1" ;... }}}}
After committing these changes to the main branch, create a new Git tag. You can accomplish this manually or utilize the provided command:
bb release
This command serves as a shortcut for both git tag
and git push
. It generates a new tag corresponding to the latest version defined in your deps.edn
file and pushes it to the remote repository, thereby triggering the release workflow.
Summary
In this article, weve navigated through the complete process of building and publishing Clojure libraries. We explored how to use Slim with existing libraries and demonstrated the creation of new libraries from scratch using the clojure-lib-template
. I trust that you found this guide beneficial and that it aids you in streamlining your workflow for future Clojure library projects!