Babashka tasks meets babashka CLI

In a previous blog post (link) I introduced babashka CLI. It offers you similar benefits as clojure -X but with a more Unixy command line interface.

In version 0.9.160 of babashka, babashka CLI was integrated. It is available as a built-in library so you don't need to declare it anymore in :deps in bb.edn unless you want to use a newer version than the built-in one.


For invoking functions from the command line, you can use the new -x flag (a pun to Clojure's -X of course!):

bb -x clojure.core/identity --hello there
{:hello "there"}

What we see in the above snippet is that a map {:hello "there"} is constructed by babashka CLI and then fed to the identity function. After that the result is printed to the console.

What if we want to influence how things are parsed by babashka CLI and provide some defaults? This can be done using metadata. Let's create a bb.edn and make a file available on the classpath:


{:paths ["."]}


(ns tasks
  {:org.babashka/cli {:exec-args {:ns-data 1}}})

(defn my-function
  {:org.babashka/cli {:exec-args {:fn-data 1}
                      :coerce {:num [:int]}
                      :alias {:n :num}}}

Now let's invoke:

$ bb -x tasks/my-function -n 1 2
{:ns-data 1, :fn-data 1, :num [1 2]}

As you can see, the namespace options are merged with the function options. Defaults can be provided with :exec-args, like you're used to from the clojure CLI.


What about task integration? Let's adapt our bb.edn:

{:paths ["."]
 :tasks {doit {:task (let [x (exec 'tasks/my-function)]
                       (prn :x x))
               :exec-args {:task-data 1234}}

and invoke the task:

$ bb doit --cli-option :yeah -n 1 2 3
:x {:ns-data 1, :fn-data 1, :task-data 1234, :cli-option :yeah, :num [1 2 3]}

As you can see it works similar to -x, but you can provide another set of defaults on the task level with :exec-args. Executing a function through babashka CLI is done using the babashka.task/exec function, available by default in tasks.

Hope you will enjoy this!

Discuss this post here.

Published: 2022-08-02

Tagged: clojure

New Clojure project quickstart

Clojure beginners sometimes struggle with setting up a new Clojure deps.edn project compared to setting up a lein project. This is one of the reasons I've built neil. But not only for beginners, I've been using neil myself a ton too, to add common features to existing deps.edn projects. You may think that neil is a pun on lein. Of course it is. But the name is also an hommage to Neil Peart, one of the greatest progressive rock drummers to have ever lived.

The intent of this post is to give you a starting point from where you can figure out things further. This post isn't going to explain any details of how deps.edn and related tooling works. For that I'm going to refer you to here.

Let's install neil which is available for brew, scoop (Windows), nix, Clojure JVM or can easily be installed manually. Unless you use clojure JVM, neil runs with babashka for fast startup time.

If you've already installed babashka (perhaps indirectly by installing neil) but didn't yet install the clojure CLI or have problems doing so, then you can run bb clojure instead of clojure for launching Clojure. Instead of clj, on linux/macOS you'll want to use rlwrap bb clojure. If you are on Windows and struggle with the official clojure Powershell-based installation, bb clojure may come in handy too.


To start a new Clojure project, run neil new --name myproject. This produces a myproject directory with a very basic project layout based on the deps-new scratch template. Now we are are going to incrementally add some functionality to this project. If you like to skip most of these steps, you can start from the more fully featured app template with neil new app --name myproject.

Adding library

Let's decide that we're going to need a library to deal with files. Let's search for one:

$ neil dep search "file system"
:lib fs/fs :version 1.3.3 :description "File system utilities for clojure"
:lib me.raynes/fs :version 1.4.6 :description "File system utilities for clojure"
:lib babashka/fs :version 0.1.6 :description "Babashka file system utilities."

Let's go with the babashka/fs library:

$ neil dep add :lib babashka/fs :version 0.1.6

Now the library is added to deps.edn and we can use it in our project:

$ clj
Clojure 1.11.0
user=> (require '[babashka.fs :as fs])


Let's start by adding a test runner. Type: neil add test. After doing this, you will be able to run:

clojure -M:test

We don't have any tests in this project, so let's add one by adding a file:


(ns myproject.core-test
  (:require [clojure.test :as t :refer [deftest is testing]]))

(deftest failing-test
  (testing "TODO: fix test"
    (is (= 3 4))))

Now run clojure -M:test again:

$ clojure -M:test

Running tests in #{"test"}

Testing myproject.core-test

FAIL in (failing-test) (core_test.clj:6)
TODO: fix test
expected: (= 3 4)
  actual: (not (= 3 4))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.

The test runner we added is the Cognitect Labs test-runner so check out the README of that project if you need to know more.

Update 2022-08-06: neil now comes with a new subcommand: test, so you can replace clojure -M:test with neil test. Also when you've created a project with neil new, neil add test will generate one default test for you.



neil add nrepl

to add a :nrepl alias to your project. Now you can run clj -M:nrepl to get a console REPL, but also a running nREPL server that you can connect to from your editor. Note that many editors also support jack-in and if you prefer to use that, you won't need this.


What's the equivalent of lein uberjar in the deps.edn world? You're going to need To create a build.clj file (the program that is going to build your uberjar), run:

neil add build

Since the default build.clj file is going to assume your project is under git version control, let's initialize a git repo first:

git init
git add deps.edn src test
git commit -m "initial commit"

Before creating the uberjar, we have to add :gen-class to src/scratch.clj:

(ns scratch

and we add :main 'scratch in the call to b/uber:

(b/uber {:class-dir class-dir
         :uber-file uber-file
         :basis basis
         :main 'scratch})

Now let's compile the uberjar:

clojure -T:build uber

And then let's run it:

$ java -jar target/lib1-1.2.1-standalone.jar 1 2 3
-main with (1 2 3)

Babashka tasks

If you have difficulty remembering the above invocations, you can write a bb.edn with some tasks:


 {:requires ([babashka.fs :as fs])

  test {:doc "Run tests"
        :task (apply clojure "-M:test" *command-line-args*)}

  nrepl {:doc "Start REPL"
         :task (if (fs/windows?)
                 (clojure "-M:nrepl")
                 (shell "rlwrap bb clojure -M:nrepl"))}

  uber {:doc "Build uberjar"
        :task (clojure "-T:build uber")}}}

The clojure function is built into babashka and is a drop-in replacement for the clojure CLI which does not require any installation. With

(apply ... *command-line-args*)

we send any args you pass to a task invocation to clojure. So to run a specific test, you can write:

bb test -v myproject.core-test/failing-test

If you prefer to use the installed clojure CLI, you can do this by using:

(apply shell "clojure" ... *command-line-args*)

Now whenever you forget what to do in the current project, run bb tasks:

$ bb tasks
The following tasks are available:

test  Run tests
nrepl Start REPL
uber  Build uberjar

Hope this helps!

Discuss this post here.

Published: 2022-08-02

Tagged: clojure

OSS Updates of May - June 2022

In this post I'll give updates about open source I worked on during May and June 2022.


But first off, I'd like to thank all the sponsors and contributors that make this work possible! Top sponsors: Clojurists Together, Roam Research, Adgoji, Cognitect, Nextjournal.

If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work via the following organizations:


Babashka CLI

Turn Clojure functions into CLIs!

This is one of my newest projects. It aims to close the gap between good command line UX and calling Clojure functions. It is very much inspired by the clojure CLI, but solves a problem which sometimes causes frustration, especially among Windows users: having to use quotes in a shell. It also offers support for subcommands. One project benefiting from that is neil. I blogged about babashka CLI here.


Serve static assets.

Another new project is http-server, which can be used in Clojure JVM and babashka to serve static assets in an http server.

Clj-kondo workshop

In June I had the honor and pleasure to give a workshop about clj-kondo at ClojureD. You can work through the material yourself if you'd like here. Feel free to join the clj-kondo channel on Clojurians Slack for questions. Here are some pictures from the event.


CLI to transform between JSON, EDN and Transit, powered with a minimal query language.


The jet binary is now available for Apple Silicon and adds specter as part of the standard library for transforming data. Also, the output is colorized and pretty-printed using puget now.



Configurable EDN/Clojure parser with location metadata. It has been stable for a while and reached version 1.0.0. The API is exposed now in babashka and nbb as well.


Quickdoc is a tool to generate documentation from namespace/var analysis done by clj-kondo. It's fast and spits out an file in the root of your project, so you can immediately view it on Github. It has undergone significant improvements in the last two months. I'm using quickdoc myself in several projects.


Scripting in Clojure on Node.js using SCI.


Added edamame.core, cljs.math, nREPL improvements and now has significant faster startup due to an improvement in SCI.


Clojure/Script Language Server (LSP) implementation.

This project is driven by the static analysis done by clj-kondo and used by many people to get IDE-like features in editors like emacs and VSCode.

I added support for Apple Silicon using Cirrus CI.


Native, fast starting Clojure interpreter for scripting.


Two new version of babashka were released:

0.8.2 and 0.8.156. The last segment of the version number now indicates the release count, so the last release is the 156th release.

Babashka now also has a new Apple Silicon binary built on Cirrus CI. What is very exciting is that babashka can now execute schema from source. Compatibility with malli is underway.


A linter for Clojure code that sparks joy.


New linters:

Clj-kondo now also has a new Apple Silicon binary built on Cirrus CI.


Configurable Clojure interpreter suitable for scripting and Clojure DSLs. Powering babashka, nbb, joyride and many other projects.


New releases: 0.3.5 - 0.3.32


SCI configs

A collection of ready to be used SCI configurations.

This project contains configurations for reagent, promesa, etc. and are used in nbb, clerk and other projects.

A recent addition was a configuration for cljs.test which is now shared by nbb and joyride.



New releases: 0.1.2 - 0.1.4


Support exec call in GraalVM native-images - this means you can replace the current process with another one.


The Small Clojure Interpreter exposed for usage in browser script tags.

Added support developing CLJS via nREPL. See docs.


Pure Clojure Webdriver protocol implementation.

This project is now compatible with babashka! Most of the work on this project was done by Lee Read. If you appreciate his work on this, or other projects like rewrite-clj, consider sponsoring him.


Brief mentions of miscellaneous other projects I worked on:

Discuss this post here.

Published: 2022-06-30

Tagged: clojure