Turn Clojure functions into CLIs!
This library is still in design phase and may still undergo breaking changes. Check breaking changes before upgrading!
Add to your deps.edn
or bb.edn
:deps
entry:
org.babashka/cli {:mvn/version "0.2.10"}
Command line arguments in clojure and babashka CLIs are often in the form:
$ tool subcommand :opt1 :v1 :opt2 :v2
or the more Unixy:
$ tool subcommand --long-opt1 v1 -o v2
This library eases that style of command line parsing.
It does not convert options into EDN automatically which, arguably, is more convenient for command line usage, especially on Windows. This library does offer a light-weight way to coerce strings.
Both :
and --
are supported as the initial characters of a named option.
Parse {:port 1339}
from command line arguments:
(require '[babashka.cli :as cli])
(cli/parse-opts ["--port" "1339"] {:coerce {:port :long}})
;;=> {:port 1339}
Use an alias (short option):
(cli/parse-opts ["-p" "1339"] {:aliases {:p :port} :coerce {:port :long}})
;; {:port 1339}
Collect values into a collection:
(cli/parse-opts ["--paths" "src" "--paths" "test"] {:collect {:paths []}})
;;=> {:paths ["src" "test"]}
(cli/parse-opts ["--paths" "src" "test"] {:collect {:paths []}})
;;=> {:paths ["src" "test"]}
Booleans need no explicit true
value and :coerce
option:
(cli/parse-opts ["--verbose"])
;;=> {:verbose true}
(cli/parse-opts ["-v" "-v" "-v"] {:aliases {:v :verbose}
:collect {:verbose []}})
;;=> {:verbose [true true true]}
Long options also support the syntax --foo=bar
:
(cli/parse-opts ["--foo=bar"])
;;=> {:foo "bar"}
For documentation on babashka tasks, go here.
To parse options to your tasks, add [babashka.cli :as cli]
to
:requires
. Then you can parse the options in :init
:
:init (def cmd-line-opts (cli/parse-opts *command-line-args*)))
and then use this in any task:
(when-not (:skip-bump cmd-line-opts)
(run 'bump-release))
and your tasks can then be called with options:
$ bb publish --skip-bump
You can control parsing behavior by adding :org.babashka/cli
metadata to
Clojure functions. It does not introduce a dependency on babashka.cli
itself. Not adding any metadata will result in string values, which in many
cases may already be a reasonable default.
Adding support for this library will cause less friction with shell usage,
especially on Windows since you need less quoting. You can support the same
function for both clojure -X
and clojure -M
style invocations without
writing extra boilerplate.
In your deps.edn
:aliases
entry, add:
:exec {:deps {org.babashka/cli {:mvn/version "0.2.10"}
:main-opts ["-m" "babashka.cli.exec"]}
Now you can call any function that accepts a map argument. E.g.:
$ clojure -M:exec clojure.core prn :a 1 :b 2
{:a "1", :b "2"}
Use :org.babashka/cli
metadata for coercions:
(ns my-ns)
(defn foo
{:org.babashka/cli {:coerce {:a :symbol :b :long}}}
;; map argument:
[m]
;; print map argument:
(prn m))
$ clojure -M:exec my-ns foo :a foo/bar :b 2 :c vanilla
{:a foo/bar, :b 2, :c "vanilla"}
Note that any library can add support for babashka CLI without depending on babashka CLI.
An example that specializes babashka.cli
usage to a function:
:prn {:deps {org.babashka/cli {:mvn/version "0.2.10"}}
:main-opts ["-m" "babashka.cli.exec" "clojure.core" "prn"]}
$ clojure -M:prn --foo=bar --baz
{:foo "bar" :baz true}
To alter the parsing behavior of functions you don't control, you can add
:org.babashka/cli
data in the deps.edn
alias:
:prn {:main-opts ["-m" "babashka.cli.exec" "clojure.core" "prn"]
:org.babashka/cli {:coerce {:foo :long}}}
$ clojure -M:prn --foo=1
{:foo 1}
.clojure/deps.edn
alias:
:antq {:deps {org.babashka/cli {:mvn/version "0.2.10"}
com.github.liquidz/antq {:mvn/version "1.7.798"}}
:main-opts ["-m" "babashka.cli.exec" "antq.tool" "outdated"]
:org.babashka/cli {:collect {:skip []}}}
On the command line you can now run it with:
$ clj -M:antq --upgrade
Note that we are calling the same outdated
function that you normally call
with -T
:
$ clj -Tantq outdated :upgrade true
even though antq has its own -main
function.
Note that we added the :org.babashka/cli {:collect {:skip []}}
data in the
alias to make sure that --skip
options get collected into a vector:
clj -M:antq --upgrade --skip github-action
vs.
clj -Tantq outdated :upgrade true :skip '["github-action"]'
.clojure/deps.edn
alias:
:new {:deps {org.babashka/cli {:mvn/version "0.2.10"}
com.github.seancorfield/clj-new {:mvn/version "1.2.381"}}
:main-opts ["-m" "babashka.cli.exec" "clj-new"]}
Usage:
$ clj -M:new app --name foo/bar --force --version 1.2.3
Generating a project called bar based on the 'app' template.
This tool can be used to run clojure exec functions with lein.
In ~/.lein/profiles.clj
put:
{:clj-1.11 {:dependencies [[org.clojure/clojure "1.11.1"]]}
:clj-new {:dependencies [[org.babashka/cli "0.2.10"]
[com.github.seancorfield/clj-new "1.2.381"]]}
:user {:aliases {"clj-new" ["with-profiles" "+clj-1.11,+clj-new"
"run" "-m" "babashka.cli.exec"
{:exec-args {:env {:description "My project"}}
:coerce {:verbose :long}
:collect {:args []}
:aliases {:f :force}}
"clj-new"]}}}
After that you can use lein clj-new app
to call an exec function:
$ lein clj-new app --name foobar/baz --verbose 3 -f
Copyright © 2022 Michiel Borkent
Distributed under the MIT License. See LICENSE.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close