The power of the :deps alias
Updated 2025-10-18, for Clojure CLI 1.12.2.1571, which added the ability to
prepa tool.
Updated 2025-08-14 for Clojure CLI 1.12.1.1561, which added thebasisfunction.
Most of us who use the Clojure CLI are familiar with the -M (main), -X
(exec), and -T (tool) options, and may have used clojure -X:deps tree at
some point to figure out version conflicts in our dependencies. The :deps
alias can do a lot more, so let's take a look!
First off, what is the :deps alias and where does it come from?
The "root" deps.edn file is where the :deps alias is defined. If you run
clojure or clj with the -Sverbose option, it'll show you the root_deps
is the clojure-tools JAR, the "user" deps.edn (as user_deps), and if you are
in a project folder, the "project" deps.edn file (as project_deps).
You can see the "root" deps.edn file as a resource in the tools.deps project:
The root deps.edn resource
contains two :aliases: :deps and :test. The latter is a convenience
that adds the test folder to your classpath. The former has the following
entries:
:replace-paths []:replace-deps {org.clojure/tools.deps.cli {:mvn/version "0.11.93"}}(as of August 8th, 2025):ns-default clojure.tools.deps.cli.api:ns-aliases {help clojure.tools.deps.cli.help}
The first two entries ensure that when you use -X:deps, your user/project
paths and dependencies are ignored -- so that the deps tooling can operate
in a clean environment without any interference from your local setup.
The :ns-default entry is what allows -X:deps tree to run, without needing
to specify the namespace explicitly (so it executes clojure.tools.deps.cli.api/tree).
The :ns-aliases entry allows you to use the help alias instead of the full
namespace when invoking functions within clojure.tools.deps.cli.help.
The help namespace provides two public functions:
dir-- lists the (public) functions available in a namespacedoc-- displays the docstrings for (public) functions in a namespace
> clojure -X:deps help/dir :ns help
dir
doc
We specify :ns so that the help alias (namespace) is used when looking up
the functions. Without :ns, we get the functions from the CLI API namespace
instead:
> clojure -X:deps help/dir
aliases
basis
find-versions
git-resolve-tags
list
mvn-install
mvn-pom
prep
tree
Note:
basiswas added in CLI 1.12.1.1561.
We can use help/doc on a whole namespace (via :ns) or just a specific
function (via :fn):
> clojure -X:deps help/doc :fn help/dir
-------------------------
clojure.tools.deps.cli.help/dir
([{:keys [ns], :as args}])
Prints a sorted directory of public vars in a namespace. If a namespace is not
specified :ns-default is used instead.
:deps functions
We're going to look at each of the functions listed above, in turn, to see what they can be used for and, hopefully, provide some tips and tricks you might not expect.
Common Options
First off, it's worth noting that many of these functions accept what are called "Basis options" that let you control the execution environment for the function. These are all optional:
:dir-- the project directory, defaults to current directory:root-- if set tonil, the rootdeps.ednwill be ignored:user-- if set tonil, the userdeps.ednwill be ignored:project-- if set tonil, the projectdeps.ednwill be ignored:extra-- specify additionaldeps.edncontent as EDN:aliases-- specify aliases to use, as a sequence of keywords
Check each function's docstring for applicability. The :root, :user, and
:project options can be nil (to ignore), :standard (use the appropriate
deps.edn file), EDN (use that instead of a file),
or a path to a deps.edn file as a string. The :extra
option should be used instead of -Sdeps to specify additional dependencies.
aliases
Let's see what that aliases function is about:
> clojure -X:deps help/doc :fn aliases
-------------------------
clojure.tools.deps.cli.api/aliases
([params])
List all aliases available for use with the CLI using -M, -X or -T execution
(note that some aliases may be usable with more than one of these). Also, the
deps.edn sources of the alias are specified.
...
The docstring is much longer than that, and it shows you how to exclude the
root, user, and/or project deps.edn from consideration. For example, if we
want to see the aliases available from just the root and project deps, we can
run:
> clojure -X:deps aliases :user nil
:deps (root)
:test (root)
:build (project)
:fast (project)
:serve (project)
There are :deps and :test as mentioned above from the root file and for
my blog project (which uses Cryogen), there are :build, :fast, and :serve
aliases.
If the same alias is defined in multiple files, you'll see that in the output
(this was run in the next.jdbc project):
> clojure -X:deps aliases
...
:test (root, user, project)
...
This tells us there is a :test alias in the root (as noted above), and in the
user deps.edn, and in the project itself -- the latter version will be used.
basis
This prints out the entire computed basis for execution as EDN.
Like other commands here that deal with the basis, you can specify :aliases
and disable the root, user, and/or project deps.edn from consideration, as
well as providing :extra deps EDN content, or specifying the :dir directory
to consider as the project.
find-versions
When you add a new dependency to your project, how do you figure out the most
recent version so that you can specify that in deps.edn? Do you look in the
project README? Do you look on Clojars? Do you look at the releases (or tags)
page on GitHub?
Why not use a tool you already have right there on the command-line?
> clojure -X:deps help/doc :fn find-versions
-------------------------
clojure.tools.deps.cli.api/find-versions
([{:keys [lib tool n], :or {n 8}, :as args}])
Find available tool versions given either a lib (with :lib) or
existing installed tool (with :tool). If lib, check all registered
procurers and print one coordinate per line when found.
Options:
:lib Qualified lib symbol
:tool Tool name for installed tool
:n Number of coordinates to return, default = 8, :all for all
For example, what version of Hiccup should we use?
> clojure -X:deps find-versions :lib hiccup/hiccup
Downloading: hiccup/hiccup/maven-metadata.xml from clojars
{:mvn/version "2.0.0-alpha1"}
{:mvn/version "2.0.0-alpha2"}
{:mvn/version "2.0.0-RC1"}
{:mvn/version "2.0.0-RC2"}
{:mvn/version "2.0.0-RC3"}
{:mvn/version "2.0.0-RC4"}
{:mvn/version "2.0.0-RC5"}
{:mvn/version "2.0.0"}
Less useful perhaps, but you can also use this to lookup versions of tools
you have installed (with clojure -Ttools install...):
> clojure -X:deps find-versions :tool tools :n 3
{:git/tag "v0.3.2", :git/sha "886f893"}
{:git/tag "v0.3.3", :git/sha "2f4d299"}
{:git/tag "v0.3.4", :git/sha "0e9e6c8"}
git-resolve-tags
This is a weird one. It takes no arguments: it reads your (project) deps.edn
file and if you have :git/tag versions with no :git/sha, it will
rewrite your deps.edn file to add the appropriate :git/sha to those
:git/tag versions.
That rewrite will remove any comments in your deps.edn and completely
reformat it, likely changing the order of any hash map keys in it.
I cannot imagine using this on any of my projects, since I often have comments
in deps.edn and I generally take care to order my :aliases alphabetically.
list
In its simplest form, this produces an alphabetical list of the libraries that your project depends on, including transitive dependencies, along with the version selected and an indication of the license for each library.
> clojure -X:deps help/doc :fn list
-------------------------
clojure.tools.deps.cli.api/list
([params])
List all deps on the classpath, optimized for knowing the final set of included
libs. The `tree` program can provide more info on why or why not a particular
lib is included.
...
The license information can omitted (:license :none) or expanded
(:license :full). You can get the output as EDN if you want (:format :edn).
You can use the :aliases option to show all your test dependencies (for
example, from the next.jdbc project):
> clojure -X:deps list :aliases '[:test]' | head
camel-snake-kebab/camel-snake-kebab 0.4.3 (EPL-1.0)
com.google.protobuf/protobuf-java 4.31.1 (BSD-3-Clause)
com.h2database/h2 2.3.232 (MPL 2.0)
com.mchange/c3p0 0.11.2 (LGPL-2.1-or-later)
com.mchange/mchange-commons-java 0.3.2 (GNU Lesser General Public License, Version 2.1)
com.microsoft.sqlserver/mssql-jdbc 12.10.0.jre11 (MIT)
com.mysql/mysql-connector-j 9.4.0 (The GNU General Public License, v2 with Universal FOSS Exception, v1.0)
com.zaxxer/HikariCP 7.0.1 (Apache-2.0)
commons-codec/commons-codec 1.17.1 (Apache-2.0)
commons-io/commons-io 2.16.1 (Apache-2.0)
mvn-install
This function will install a JAR file into your local Maven cache. You must
specify a :jar option (a string that specifies the relative path to the
JAR file). If you have a fully-fleshed out pom.xml file, you can use the
:pom option to specify its location, or you can specify the group/artifact
ID via the :lib option and the library :version. You can also specify the
:classifier option, if needed:
clojure -X:deps mvn-install :jar '"target/my.jar"' :lib my.cool/new.library :version '"1.2.3"'
mvn-pom
This function will generate a minimal pom.xml file with the <dependencies>
populated from your deps.edn file. If a pom.xml already exists, it will
just update that file with the latest <dependencies> from deps.edn.
This minimal pom.xml is not sufficient for most libraries -- the groupId
and artifactId are derived from the project directory name, and a lot of the
information required by cljdoc is not included. Unlike
the mvn-install function above, mvn-pom does not let you specify :lib,
:version, or :classifier.
You are better off using
clojure.tools.build.api/write-pom
in your build.clj
file, so that you have more control over the generated POM file,
specifying the groupId, artifactId, and SCM information (via :pom-data).
prep
For projects / libraries that are made available in source form, as git
dependencies, there are situations where they need a step to be performed
before they can be used, such as compiling some code to class files.
Those projects will have a :deps/prep-lib key in their deps.edn file, that
specifies a function to run (via :fn), with an :alias (for -T invocation)
and an :ensure key that specifies a directory whose existence determines
whether the library needs to be "prepared".
From a consumer point of view, if you try to use such a library before it has been "prepared", you'll get an error like:
Error building classpath. The following libs must be prepared before use: ...
You can usually run clojure -X:deps prep to run the preparation steps across
any such libraries you are depending on.
Sometimes, it might be one of your test dependencies, in which case the above
won't work (it will only prepare your non-test dependencies). prep accepts
:aliases to specify the dependencies you need prepared:
clojure -X:deps prep :aliases '[:test]'
As of Clojure CLI 1.12.2.1571, you can also prepare installed tools and their dependencies:
clojure -X:deps prep :tool sometool
This will prepare sometool -- installed via clojure -Ttools install ... -- and
all its dependencies.
If you are working on a project that needs to be prepared before use, you can
ensure that your :deps/prep-lib configuration is correct by running prep
for that local project:
clojure -X:deps prep :current true
You may also need to re-run the preparation step, and instead of manually
deleting the :ensure directory, you can force the preparation step to run again:
clojure -X:deps prep :current true :force true
Note: you can use
:force trueto re-run the preparation step for dependencies too, of course, if needed.
Check the full docstring for additional options you can use, such as :log
which can help you debug problems with the preparation step:
> clojure -X:deps help/doc :fn prep
-------------------------
clojure.tools.deps.cli.api/prep
([{:keys [force log current], :or {log :info, current false}, :as params}])
Prep the unprepped libs found in the transitive lib set of basis.
This program accepts the same basis-modifying arguments from the `basis` program.
Each dep source value can be :standard, a string path, a deps edn map, or nil.
Sources are merged in the order - :root, :user, :project, :extra.
Options:
:force - flag on whether to force prepped libs to re-prep (default = false)
:current - flag on whether to prep current project too (default = false)
:log - :none, :info (default), or :debug
...
(basis options omitted for brevity)
tree
As noted at the start of this post, this is probably the :deps function
that most of us have used at some point to try to debug our dependencies.
Where list shows us the "finished product", in terms of dependency versions,
tree shows us how those versions are selected and the output is usually a lot
more verbose than list.
Going back to the next.jdbc project, here's the default tree output:
> clojure -X:deps tree
org.clojure/clojure 1.10.3
. org.clojure/spec.alpha 0.2.194
. org.clojure/core.specs.alpha 0.2.56
org.clojure/java.data 1.3.113
camel-snake-kebab/camel-snake-kebab 0.4.3
Transitive dependencies are shown indented with . indicating the version
was included and X indicating it was excluded, and a reason will be present
for exclusions (and some inclusions, when there are multiple versions in play).
Like list, the tree function accepts a :format output that can be used
to produce EDN output. In addition, tree accepts :indent (how many spaces
to use for indentation if you don't like the default of 2) and :hide-libs
that lets you exclude transitive subtrees from the output (not top-level
dependencies). By default, any transitive dependencies on Clojure itself are
omitted.
If you want to check the dependencies of a specific library, you can do so via
the :extra option:
> clojure -X:deps tree :project nil :extra '{:deps {com.github.seancorfield/next.jdbc {:mvn/version "1.3.1048"}}}'
org.clojure/clojure 1.12.1
. org.clojure/spec.alpha 0.5.238
. org.clojure/core.specs.alpha 0.4.74
com.github.seancorfield/next.jdbc 1.3.1048
. org.clojure/java.data 1.3.113
. camel-snake-kebab/camel-snake-kebab 0.4.3
Note: using
-Sdepswould not work here:
> clojure -Sdeps '{:deps {com.github.seancorfield/next.jdbc {:mvn/version "1.3.1048"}}}' -X:deps tree :project nil
org.clojure/clojure 1.12.1
. org.clojure/spec.alpha 0.5.238
. org.clojure/core.specs.alpha 0.4.74
As always, check the full docstring for other available options:
> clojure -X:deps help/doc :fn tree
-------------------------
clojure.tools.deps.cli.api/tree
([opts])
Print deps tree for the current project's deps.edn built from either the
a basis, or if provided, the trace file.
This program accepts the same basis-modifying arguments from the `basis` program.
Each dep source value can be :standard, a string path, a deps edn map, or nil.
Sources are merged in the order - :root, :user, :project, :extra.
...
build.clj help
The mighty :deps alias can help you learn about the build.clj file in a project.
For example, if we git clone the next.jdbc project, we can run the
following command to see what tasks are defined:
> clojure -T:deps:build help/dir
deploy
jar
We need both the tools.deps.cli library and the build namespace on the classpath,
which means we need to use -T so that the current directory is on the classpath in
order for the build.clj file to be found.
We could also use the more verbose clojure -A:deps -T:build help/dir here.
Now, we can use help/doc to see the full docstrings for the namespace and those
functions:
> clojure -T:deps:build help/doc
next.jdbc's build script.
clojure -T:build jar
clojure -T:build deploy
Run tests via:
bb test
For more information, run:
clojure -A:deps -T:build help/doc
-------------------------
build/deploy
([opts])
Deploy the JAR to Clojars.
-------------------------
build/jar
([opts])
Build the JAR file.
Project help
Since -X:deps replaces the paths/deps to avoid interference from your
local setup, you cannot use it directly to get help for your project's
source namespaces:
> clojure -X:deps help/dir :ns next.jdbc
Execution error (FileNotFoundException) at clojure.tools.deps.cli.help/dir (help.clj:76).
Could not locate next/jdbc__init.class, next/jdbc.clj or next/jdbc.cljc on classpath.
However, the CLI allows you to specify extra deps.edn data on the command-line
so we can add our project back in like this:
> clojure -Sdeps '{:deps {this/project {:local/root "."}}}' -X:deps help/dir :ns next.jdbc
active-tx?
execute!
execute-batch!
execute-one!
get-connection
get-datasource
on-connection
on-connection+options
plan
prepare
transact
with-logging
with-options
with-transaction
with-transaction+options
We can make that cleaner by adding an alias to our user deps.edn file:
:this {:deps {this/project {:local/root "."}}}
Now we can run:
> clojure -X:deps:this help/dir :ns next.jdbc
active-tx?
execute!
execute-batch!
execute-one!
...
Note: I have
:thisin my dot-clojuredeps.ednfile.