@babashka/cli 0.12.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.html +1229 -0
- package/README.md +1837 -0
- package/index.mjs +70 -0
- package/js/babashka/cli/internal.mjs +36 -0
- package/js/babashka/cli.mjs +2797 -0
- package/package.json +28 -0
package/README.html
ADDED
|
@@ -0,0 +1,1229 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
4
|
+
|
|
5
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
6
|
+
|
|
7
|
+
<head>
|
|
8
|
+
<title>README.html</title>
|
|
9
|
+
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
|
10
|
+
|
|
11
|
+
</head>
|
|
12
|
+
|
|
13
|
+
<body>
|
|
14
|
+
|
|
15
|
+
<h1 id="babashka.cli">babashka.cli</h1>
|
|
16
|
+
<p><a href="https://clojars.org/org.babashka/cli"><img
|
|
17
|
+
src="https://img.shields.io/clojars/v/org.babashka/cli.svg"
|
|
18
|
+
alt="Clojars Project" /></a> <a
|
|
19
|
+
href="https://book.babashka.org#badges"><img
|
|
20
|
+
src="https://raw.githubusercontent.com/babashka/babashka/master/logo/built-in-badge.svg"
|
|
21
|
+
alt="bb built-in" /></a></p>
|
|
22
|
+
<p>Turn Clojure functions into CLIs! This library can be used from: - <a
|
|
23
|
+
href="https://github.com/babashka/babashka">babashka</a> - included as a
|
|
24
|
+
built-in library - <a
|
|
25
|
+
href="https://www.clojure.org/guides/install_clojure">Clojure on the
|
|
26
|
+
JVM</a> - we support Clojure 1.10.3 and above on Java 11 and above - <a
|
|
27
|
+
href="https://clojurescript.org">ClojureScript</a> - we test against the
|
|
28
|
+
current release</p>
|
|
29
|
+
<h2 id="api"><a href="API.md">API</a></h2>
|
|
30
|
+
<h2 id="status">Status</h2>
|
|
31
|
+
<p>This library is still in design phase and may still undergo breaking
|
|
32
|
+
changes. Check <a href="CHANGELOG.md#breaking-changes">breaking
|
|
33
|
+
changes</a> before upgrading!</p>
|
|
34
|
+
<h2 id="installation">Installation</h2>
|
|
35
|
+
<p>Add to your <code>deps.edn</code> or <code>bb.edn</code>
|
|
36
|
+
<code>:deps</code> entry:</p>
|
|
37
|
+
<div class="sourceCode" id="cb1"><pre
|
|
38
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}</span></code></pre></div>
|
|
39
|
+
<h2 id="intro">Intro</h2>
|
|
40
|
+
<p>Turn a Clojure function into a CLI that takes Unix-style command line
|
|
41
|
+
arguments:</p>
|
|
42
|
+
<div class="sourceCode" id="cb2"><pre
|
|
43
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>$ cli command --long-opt1 v1 -o v2</span></code></pre></div>
|
|
44
|
+
<p>The main ideas:</p>
|
|
45
|
+
<ul>
|
|
46
|
+
<li>Put as little effort as possible into turning a clojure function
|
|
47
|
+
into a CLI, similar to <code>-X</code> style invocations. For lazy
|
|
48
|
+
people like me! If you are not familiar with <code>clj -X</code>, read
|
|
49
|
+
the docs <a
|
|
50
|
+
href="https://clojure.org/reference/clojure_cli#use_fn">here</a>.</li>
|
|
51
|
+
<li>But with a better UX by not having to use quotes on the command line
|
|
52
|
+
as a result of having to pass EDN directly: <code>--dir foo</code>
|
|
53
|
+
instead of <code>:dir '"foo"'</code> (or who knows how to write the
|
|
54
|
+
latter in <code>cmd.exe</code> or Powershell?).</li>
|
|
55
|
+
<li>By default, employ an open world assumption: passing extra arguments
|
|
56
|
+
does not break and arguments can be re-used in multiple contexts.</li>
|
|
57
|
+
<li>But also support incremental restrictions and validations as a form
|
|
58
|
+
of polishing a CLI for production use.</li>
|
|
59
|
+
</ul>
|
|
60
|
+
<p>See <a href="#clojure-cli">clojure CLI</a> for how to turn your exec
|
|
61
|
+
functions into CLIs.</p>
|
|
62
|
+
<h2 id="projects-using-babashka-cli">Projects using babashka CLI</h2>
|
|
63
|
+
<ul>
|
|
64
|
+
<li><a href="https://github.com/borkdude/jet">jet</a></li>
|
|
65
|
+
<li><a
|
|
66
|
+
href="https://github.com/babashka/http-server">http-server</a></li>
|
|
67
|
+
<li><a href="https://github.com/babashka/neil">neil</a></li>
|
|
68
|
+
<li><a
|
|
69
|
+
href="https://github.com/borkdude/quickdoc#clojure-cli">quickdoc</a></li>
|
|
70
|
+
<li><a
|
|
71
|
+
href="https://github.com/seancorfield/clj-new#babashka-cli">clj-new</a></li>
|
|
72
|
+
<li><a
|
|
73
|
+
href="https://github.com/seancorfield/deps-new#babashka-cli">deps-new</a></li>
|
|
74
|
+
</ul>
|
|
75
|
+
<h2 id="toc">TOC</h2>
|
|
76
|
+
<ul>
|
|
77
|
+
<li><a href="#simple-example">Simple example</a></li>
|
|
78
|
+
<li><a href="#options">Options</a></li>
|
|
79
|
+
<li><a href="#arguments">Arguments</a></li>
|
|
80
|
+
<li><a href="#subcommands">Subcommands</a></li>
|
|
81
|
+
<li><a href="#completions">Completions</a></li>
|
|
82
|
+
<li><a href="#adding-production-polish">Adding Production
|
|
83
|
+
Polish</a></li>
|
|
84
|
+
<li><a href="#babashka-tasks">Babashka tasks</a></li>
|
|
85
|
+
<li><a href="#clojure-cli">Clojure CLI</a></li>
|
|
86
|
+
<li><a href="#leiningen">Leiningen</a></li>
|
|
87
|
+
</ul>
|
|
88
|
+
<h2 id="simple-example">Simple example</h2>
|
|
89
|
+
<p>Babashka CLI works in Clojure, ClojureScript and <a
|
|
90
|
+
href="https://book.babashka.org/">babashka</a>. Here is an example
|
|
91
|
+
babashka script to get you started!</p>
|
|
92
|
+
<div class="sourceCode" id="cb3"><pre
|
|
93
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>#!/usr/bin/env bb</span>
|
|
94
|
+
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>(<span class="kw">require</span> '[babashka.cli <span class="at">:as</span> cli]</span>
|
|
95
|
+
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> '[babashka.fs <span class="at">:as</span> fs])</span>
|
|
96
|
+
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
|
|
97
|
+
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> dir-exists? </span>[<span class="kw">path</span>]</span>
|
|
98
|
+
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> (fs/directory? <span class="kw">path</span>))</span>
|
|
99
|
+
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a></span>
|
|
100
|
+
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec</span></span>
|
|
101
|
+
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> {<span class="at">:num</span> {<span class="at">:coerce</span> <span class="at">:long</span></span>
|
|
102
|
+
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:n</span> <span class="co">; adds -n alias for --num</span></span>
|
|
103
|
+
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"Number of some items"</span></span>
|
|
104
|
+
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:validate</span> <span class="kw">pos?</span> <span class="co">; tests if supplied --num > 0</span></span>
|
|
105
|
+
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a> <span class="at">:require</span> <span class="va">true</span>} <span class="co">; --num,-n is required</span></span>
|
|
106
|
+
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:dir</span> {<span class="at">:alias</span> <span class="at">:d</span></span>
|
|
107
|
+
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"Directory name to do stuff"</span></span>
|
|
108
|
+
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:validate</span> <span class="co">; tests if --dir exists,</span></span>
|
|
109
|
+
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> {<span class="at">:pred</span> dir-exists? <span class="co">; with a custom error message</span></span>
|
|
110
|
+
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a> <span class="at">:ex-msg</span> (<span class="kw">fn</span> [{<span class="at">:keys</span> [value]}]</span>
|
|
111
|
+
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> (<span class="kw">str</span> <span class="st">"Directory does not exist: "</span> value))}}</span>
|
|
112
|
+
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a> <span class="at">:flag</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="co">; defines a boolean flag</span></span>
|
|
113
|
+
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"I am just a flag"</span>}})</span>
|
|
114
|
+
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
|
|
115
|
+
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> run </span>[{<span class="at">:keys</span> [opts]}]</span>
|
|
116
|
+
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">"Here are your cli args!:"</span> opts))</span>
|
|
117
|
+
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
|
|
118
|
+
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> -main </span>[& args]</span>
|
|
119
|
+
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a> (cli/dispatch {<span class="at">:fn</span> run <span class="at">:spec</span> spec} args {<span class="at">:prog</span> <span class="st">"try-me"</span> <span class="at">:help</span> <span class="va">true</span>}))</span>
|
|
120
|
+
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a></span>
|
|
121
|
+
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a>(<span class="kw">apply</span> -main <span class="va">*command-line-args*</span>)</span></code></pre></div>
|
|
122
|
+
<p>In the above example, <code>:help true</code> wires up automatic
|
|
123
|
+
<code>--help</code>/<code>-h</code> support and terse error messages for
|
|
124
|
+
you. See <a href="#help">Subcommands > Help</a> for customizing
|
|
125
|
+
it.</p>
|
|
126
|
+
<p>The first argument to <code>dispatch</code> is a <a
|
|
127
|
+
href="#tree-format">tree</a> of subcommands. Since this CLI has no
|
|
128
|
+
subcommands, this is all you need to write. See <a
|
|
129
|
+
href="#subcommands">Subcommands</a> for more info.</p>
|
|
130
|
+
<p>And this is how you run it:</p>
|
|
131
|
+
<pre><code>$ bb try-me.clj --num 1 --dir my_dir --flag
|
|
132
|
+
Error: Directory does not exist: my_dir
|
|
133
|
+
|
|
134
|
+
Usage: try-me [options]
|
|
135
|
+
|
|
136
|
+
Run "try-me --help" for more information.</code></pre>
|
|
137
|
+
<p>The directory is validated with <code>dir-exists?</code>. A custom
|
|
138
|
+
error message is produced by the <code>:ex-msg</code> function. Since
|
|
139
|
+
the dir does not exist, let’s create it and try again.</p>
|
|
140
|
+
<pre><code>$ mkdir my_dir
|
|
141
|
+
$ bb try-me.clj --num 1 --dir my_dir --flag
|
|
142
|
+
Here are your cli args!: {:num 1, :dir my_dir, :flag true}
|
|
143
|
+
|
|
144
|
+
$ bb try-me.clj --help
|
|
145
|
+
Usage: try-me [options]
|
|
146
|
+
|
|
147
|
+
Options:
|
|
148
|
+
-n, --num Number of some items (required)
|
|
149
|
+
-d, --dir Directory name to do stuff
|
|
150
|
+
--flag I am just a flag
|
|
151
|
+
-h, --help Show this help
|
|
152
|
+
|
|
153
|
+
$ bb try-me.clj
|
|
154
|
+
Error: Required option: --num
|
|
155
|
+
|
|
156
|
+
Usage: try-me [options]
|
|
157
|
+
|
|
158
|
+
Run "try-me --help" for more information.</code></pre>
|
|
159
|
+
<h3 id="adding-subcommands">Adding subcommands</h3>
|
|
160
|
+
<p>To add subcommands to this CLI, we need to specify a subcommand
|
|
161
|
+
structure. We’ll just give an example here. See <a
|
|
162
|
+
href="#subcommands">Subcommands</a> for more info.</p>
|
|
163
|
+
<div class="sourceCode" id="cb6"><pre
|
|
164
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> run </span>[{<span class="at">:keys</span> [opts]}]</span>
|
|
165
|
+
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">"Here are your cli args!:"</span> opts))</span>
|
|
166
|
+
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
167
|
+
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> version </span>[_]</span>
|
|
168
|
+
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">"try-me 1.0"</span>))</span>
|
|
169
|
+
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a></span>
|
|
170
|
+
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> tree</span></span>
|
|
171
|
+
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmd</span> {<span class="st">"run"</span> {<span class="at">:fn</span> run <span class="at">:doc</span> <span class="st">"Run the thing"</span> <span class="at">:spec</span> spec}</span>
|
|
172
|
+
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="st">"version"</span> {<span class="at">:fn</span> version <span class="at">:doc</span> <span class="st">"Print version"</span>}}})</span>
|
|
173
|
+
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
|
|
174
|
+
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> -main </span>[& args]</span>
|
|
175
|
+
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> (cli/dispatch tree args {<span class="at">:prog</span> <span class="st">"try-me"</span> <span class="at">:help</span> <span class="va">true</span>}))</span></code></pre></div>
|
|
176
|
+
<p><code>--help</code> now lists the commands:</p>
|
|
177
|
+
<pre><code>$ bb try-me.clj --help
|
|
178
|
+
Usage: try-me [options] <command>
|
|
179
|
+
|
|
180
|
+
Commands:
|
|
181
|
+
run Run the thing
|
|
182
|
+
version Print version
|
|
183
|
+
|
|
184
|
+
Options:
|
|
185
|
+
-h, --help Show this help
|
|
186
|
+
|
|
187
|
+
Run "try-me <command> --help" for more information on a command.</code></pre>
|
|
188
|
+
<p><code>bb try-me.clj run --num 1 --flag</code> calls <code>run</code>;
|
|
189
|
+
<code>bb try-me.clj version</code> calls <code>version</code>. See <a
|
|
190
|
+
href="#subcommands">Subcommands</a> for shared options, inheritance and
|
|
191
|
+
help customization.</p>
|
|
192
|
+
<h2 id="options">Options</h2>
|
|
193
|
+
<p>For parsing options, use either <a
|
|
194
|
+
href="/API.md#parse-opts"><code>parse-opts</code></a> or <a
|
|
195
|
+
href="/API.md#parse-args"><code>parse-args</code></a>.</p>
|
|
196
|
+
<p>On the command line, a named option is written
|
|
197
|
+
<code>--opt val</code>, or <code>-o val</code> using an <a
|
|
198
|
+
href="#aliases">alias</a>. Babashka CLI also accepts a
|
|
199
|
+
<code>:</code>-prefixed form, <code>:opt val</code>, to match the
|
|
200
|
+
Clojure CLI <code>-X</code> invocation style. The two cannot be mixed in
|
|
201
|
+
a single invocation. Use <code>--</code>/<code>-</code> or
|
|
202
|
+
<code>:</code>, not both.</p>
|
|
203
|
+
<p>Options are configured with a <a href="#spec">spec</a> (short for
|
|
204
|
+
“options spec”, not <code>clojure.spec</code>): a map keyed by option
|
|
205
|
+
name, each value a map of <code>:coerce</code>, <code>:alias</code>,
|
|
206
|
+
<code>:validate</code>, <code>:require</code>, <code>:desc</code>, etc.,
|
|
207
|
+
passed under <code>:spec</code>:</p>
|
|
208
|
+
<div class="sourceCode" id="cb8"><pre
|
|
209
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:alias</span> <span class="at">:p</span>}}}</span></code></pre></div>
|
|
210
|
+
<p>A terser shape is also supported, where each key is lifted to the top
|
|
211
|
+
level and keyed by option name:
|
|
212
|
+
<code>{:coerce {:port :long} :alias {:p :port}}</code>. It is handy for
|
|
213
|
+
quick scripts and partial parsing, but only a spec can carry
|
|
214
|
+
<code>:desc</code>/<code>:ref</code>, so generated help and option
|
|
215
|
+
printing need a spec. The two are otherwise equivalent. The examples
|
|
216
|
+
below use the spec shape.</p>
|
|
217
|
+
<p>Examples:</p>
|
|
218
|
+
<p>Parse <code>{:port 1339}</code> from command line arguments:</p>
|
|
219
|
+
<div class="sourceCode" id="cb9"><pre
|
|
220
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">require</span> '[babashka.cli <span class="at">:as</span> cli])</span>
|
|
221
|
+
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
|
|
222
|
+
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--port"</span> <span class="st">"1339"</span>] {<span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span>}}})</span>
|
|
223
|
+
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:port 1339}</span></span></code></pre></div>
|
|
224
|
+
<p>Use an alias (short option):</p>
|
|
225
|
+
<div class="sourceCode" id="cb10"><pre
|
|
226
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"-p"</span> <span class="st">"1339"</span>] {<span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:alias</span> <span class="at">:p</span>}}})</span>
|
|
227
|
+
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="co">;; {:port 1339}</span></span></code></pre></div>
|
|
228
|
+
<p>Coerce values into a collection:</p>
|
|
229
|
+
<div class="sourceCode" id="cb11"><pre
|
|
230
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--paths"</span> <span class="st">"src"</span> <span class="st">"--paths"</span> <span class="st">"test"</span>] {<span class="at">:spec</span> {<span class="at">:paths</span> {<span class="at">:coerce</span> []}}})</span>
|
|
231
|
+
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:paths ["src" "test"]}</span></span>
|
|
232
|
+
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
233
|
+
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--paths"</span> <span class="st">"src"</span> <span class="st">"test"</span>] {<span class="at">:spec</span> {<span class="at">:paths</span> {<span class="at">:coerce</span> []}}})</span>
|
|
234
|
+
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:paths ["src" "test"]}</span></span></code></pre></div>
|
|
235
|
+
<p>Transforming to a collection of a certain type:</p>
|
|
236
|
+
<div class="sourceCode" id="cb12"><pre
|
|
237
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--foo"</span> <span class="st">"bar"</span> <span class="st">"--foo"</span> <span class="st">"baz"</span>] {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:coerce</span> [<span class="at">:keyword</span>]}}})</span>
|
|
238
|
+
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="co">;; => {:foo [:bar :baz]}</span></span></code></pre></div>
|
|
239
|
+
<p>Besides the built-in coercion keywords, <code>:coerce</code> accepts
|
|
240
|
+
any function (called with the string value):</p>
|
|
241
|
+
<div class="sourceCode" id="cb13"><pre
|
|
242
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--letter"</span> <span class="st">"alpha"</span>] {<span class="at">:spec</span> {<span class="at">:letter</span> {<span class="at">:coerce</span> (<span class="kw">fn</span> [s] (<span class="kw">subs</span> s <span class="dv">0</span> <span class="dv">1</span>))}}})</span>
|
|
243
|
+
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:letter "a"}</span></span></code></pre></div>
|
|
244
|
+
<p>Booleans need no explicit <code>true</code> value and
|
|
245
|
+
<code>:coerce</code> option:</p>
|
|
246
|
+
<div class="sourceCode" id="cb14"><pre
|
|
247
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--verbose"</span>])</span>
|
|
248
|
+
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:verbose true}</span></span>
|
|
249
|
+
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
250
|
+
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"-v"</span> <span class="st">"-v"</span> <span class="st">"-v"</span>] {<span class="at">:spec</span> {<span class="at">:verbose</span> {<span class="at">:alias</span> <span class="at">:v</span> <span class="at">:coerce</span> []}}})</span>
|
|
251
|
+
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:verbose [true true true]}</span></span></code></pre></div>
|
|
252
|
+
<p>Long options also support the syntax <code>--foo=bar</code>:</p>
|
|
253
|
+
<div class="sourceCode" id="cb15"><pre
|
|
254
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--foo=bar"</span>])</span>
|
|
255
|
+
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:foo "bar"}</span></span></code></pre></div>
|
|
256
|
+
<p>Flags may be combined into a single short option:</p>
|
|
257
|
+
<div class="sourceCode" id="cb16"><pre
|
|
258
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"-abc"</span>])</span>
|
|
259
|
+
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:a true :b true :c true}</span></span></code></pre></div>
|
|
260
|
+
<p>Arguments that start with <code>--no-</code> arg parsed as negative
|
|
261
|
+
flags:</p>
|
|
262
|
+
<div class="sourceCode" id="cb17"><pre
|
|
263
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--no-colors"</span>])</span>
|
|
264
|
+
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:colors false}</span></span></code></pre></div>
|
|
265
|
+
<p>This works for any option. For a boolean option where the negation is
|
|
266
|
+
meaningful, set <code>:negatable true</code> in its spec to advertise it
|
|
267
|
+
in help as <code>--[no-]colors</code>.</p>
|
|
268
|
+
<h2 id="spec">Spec</h2>
|
|
269
|
+
<p>A spec (short for “options spec”, not <code>clojure.spec</code>) is a
|
|
270
|
+
map keyed by option name; each value configures one option. Alongside
|
|
271
|
+
the parsing keys (<code>:coerce</code>, <code>:alias</code>,
|
|
272
|
+
<code>:validate</code>, …) it carries <code>:desc</code> and
|
|
273
|
+
<code>:ref</code>, used when printing options (see <a
|
|
274
|
+
href="#printing-options">Printing options</a>). For example:</p>
|
|
275
|
+
<div class="sourceCode" id="cb18"><pre
|
|
276
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec </span>{<span class="at">:from</span> {<span class="at">:ref</span> <span class="st">"<format>"</span></span>
|
|
277
|
+
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"The input format. <format> can be edn, json or transit."</span></span>
|
|
278
|
+
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> <span class="at">:keyword</span></span>
|
|
279
|
+
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:i</span></span>
|
|
280
|
+
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">"edn"</span></span>
|
|
281
|
+
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> <span class="at">:edn</span>}</span>
|
|
282
|
+
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:to</span> {<span class="at">:ref</span> <span class="st">"<format>"</span></span>
|
|
283
|
+
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"The output format. <format> can be edn, json or transit."</span></span>
|
|
284
|
+
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> <span class="at">:keyword</span></span>
|
|
285
|
+
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:o</span></span>
|
|
286
|
+
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">"json"</span></span>
|
|
287
|
+
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> <span class="at">:json</span>}</span>
|
|
288
|
+
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a> <span class="at">:pretty</span> {<span class="at">:desc</span> <span class="st">"Pretty-print output."</span></span>
|
|
289
|
+
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:p</span>}</span>
|
|
290
|
+
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:paths</span> {<span class="at">:desc</span> <span class="st">"Paths of files to transform."</span></span>
|
|
291
|
+
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> []</span>
|
|
292
|
+
<span id="cb18-17"><a href="#cb18-17" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> [<span class="st">"src"</span> <span class="st">"test"</span>]</span>
|
|
293
|
+
<span id="cb18-18"><a href="#cb18-18" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">"src test"</span>}})</span></code></pre></div>
|
|
294
|
+
<p>You can pass the spec to <code>parse-opts</code> under the
|
|
295
|
+
<code>:spec</code> key: <code>(parse-opts args {:spec spec})</code>. An
|
|
296
|
+
explanation of each key:</p>
|
|
297
|
+
<ul>
|
|
298
|
+
<li><code>:ref</code>: a name which can be used as a reference in the
|
|
299
|
+
description (<code>:desc</code>)</li>
|
|
300
|
+
<li><code>:desc</code>: a description of the option.</li>
|
|
301
|
+
<li><code>:coerce</code>: coerce a string value to a type. Built-in
|
|
302
|
+
keywords: <code>:boolean</code> (<code>:bool</code>), <code>:int</code>
|
|
303
|
+
(<code>:long</code>), <code>:double</code>, <code>:number</code>,
|
|
304
|
+
<code>:symbol</code>, <code>:keyword</code>, <code>:string</code>,
|
|
305
|
+
<code>:edn</code>, <code>:auto</code>. A collection collects repeated
|
|
306
|
+
values: <code>[]</code> (vector), <code>#{}</code> (set) or
|
|
307
|
+
<code>()</code> (list); put a coercion keyword inside it to coerce each
|
|
308
|
+
element (e.g. <code>[:keyword]</code>, <code>#{:int}</code>). A function
|
|
309
|
+
is also accepted: it is called with the string and returns the
|
|
310
|
+
value.</li>
|
|
311
|
+
<li><code>:alias</code>: mapping of short name to long name.</li>
|
|
312
|
+
<li><code>:default</code>: default value.</li>
|
|
313
|
+
<li><code>:default-desc</code>: a string representation of the default
|
|
314
|
+
value.</li>
|
|
315
|
+
<li><code>:require</code>: <code>true</code> make this opt
|
|
316
|
+
required.</li>
|
|
317
|
+
<li><code>:validate</code>: a function used to validate the value of
|
|
318
|
+
this opt (as described in the <a href="#validate">Validate</a>
|
|
319
|
+
section).</li>
|
|
320
|
+
<li><code>:collect</code>: collect repeated values into a collection
|
|
321
|
+
(<code>[]</code> vector, <code>#{}</code> set or <code>()</code> list),
|
|
322
|
+
or a function <code>(fn [coll arg-value] ...)</code> for custom
|
|
323
|
+
collection</li>
|
|
324
|
+
<li><code>:negatable</code>: <code>true</code> shows a boolean option as
|
|
325
|
+
<code>--[no-]name</code> in help (the <code>--no-name</code> form parses
|
|
326
|
+
regardless)</li>
|
|
327
|
+
</ul>
|
|
328
|
+
<h3 id="custom-collection-handling">Custom collection handling</h3>
|
|
329
|
+
<p>Usually the above will suffice, but for custom transformation to a
|
|
330
|
+
collection, you can use <code>:collect</code>. Here’s an example of
|
|
331
|
+
parsing out <code>,</code> separated multi-arg-values:</p>
|
|
332
|
+
<div class="sourceCode" id="cb19"><pre
|
|
333
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--foo"</span> <span class="st">"a,b"</span> <span class="st">"--foo=c,d,e"</span> <span class="st">"--foo"</span> <span class="st">"f"</span>]</span>
|
|
334
|
+
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:collect</span> (<span class="kw">fn</span> [coll arg-value]</span>
|
|
335
|
+
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a> (<span class="kw">into</span> (<span class="kw">or</span> coll [])</span>
|
|
336
|
+
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a> (str/split arg-value <span class="ss">#","</span>)))}}})</span>
|
|
337
|
+
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="co">;; => {:foo ["a" "b" "c" "d" "e" "f"]}</span></span></code></pre></div>
|
|
338
|
+
<h3 id="auto-coercion">Auto-coercion</h3>
|
|
339
|
+
<p>Babashka CLI auto-coerces values that have no explicit coercion with
|
|
340
|
+
<a href="/API.md#auto-coerce"><code>auto-coerce</code></a>: it
|
|
341
|
+
automatically tries to convert booleans, numbers and keywords.</p>
|
|
342
|
+
<h3 id="aliases">Aliases</h3>
|
|
343
|
+
<p>An <code>:alias</code> specifies a mapping from short to long
|
|
344
|
+
name.</p>
|
|
345
|
+
<p>The library can distinguish aliases with characters in common, so a
|
|
346
|
+
way to implement the common <code>-v</code>/<code>-vv</code> unix
|
|
347
|
+
pattern is:</p>
|
|
348
|
+
<div class="sourceCode" id="cb20"><pre
|
|
349
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec </span>{<span class="at">:verbose</span> {<span class="at">:alias</span> <span class="at">:v</span></span>
|
|
350
|
+
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"Enable verbose output."</span>}</span>
|
|
351
|
+
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:very-verbose</span> {<span class="at">:alias</span> <span class="at">:vv</span></span>
|
|
352
|
+
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"Enable very verbose output."</span>}})</span></code></pre></div>
|
|
353
|
+
<p>You get:</p>
|
|
354
|
+
<div class="sourceCode" id="cb21"><pre
|
|
355
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"-v"</span>] {<span class="at">:spec</span> spec})</span>
|
|
356
|
+
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:verbose true}</span></span>
|
|
357
|
+
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
358
|
+
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"-vv"</span>] {<span class="at">:spec</span> spec})</span>
|
|
359
|
+
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:very-verbose true}</span></span></code></pre></div>
|
|
360
|
+
<p>Another way would be to collect the flags in a vector with
|
|
361
|
+
<code>:coerce</code> (and base verbosity on the size of that
|
|
362
|
+
vector):</p>
|
|
363
|
+
<div class="sourceCode" id="cb22"><pre
|
|
364
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec </span>{<span class="at">:verbose</span> {<span class="at">:alias</span> <span class="at">:v</span></span>
|
|
365
|
+
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"Enable verbose output."</span></span>
|
|
366
|
+
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> []}})</span>
|
|
367
|
+
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a></span>
|
|
368
|
+
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a>user=> (cli/parse-opts [<span class="st">"-vvv"</span>] {<span class="at">:spec</span> spec})</span>
|
|
369
|
+
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a>{<span class="at">:verbose</span> [<span class="va">true</span> <span class="va">true</span> <span class="va">true</span>]}</span></code></pre></div>
|
|
370
|
+
<h2 id="arguments">Arguments</h2>
|
|
371
|
+
<p>To parse positional arguments, you can use <code>parse-args</code>
|
|
372
|
+
and/or the <code>:args->opts</code> option. E.g. to parse arguments
|
|
373
|
+
for the <code>git push</code> command:</p>
|
|
374
|
+
<div class="sourceCode" id="cb23"><pre
|
|
375
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--force"</span> <span class="st">"ssh://foo"</span>] {<span class="at">:spec</span> {<span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}}})</span>
|
|
376
|
+
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:args ["ssh://foo"], :opts {:force true}}</span></span>
|
|
377
|
+
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
378
|
+
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"ssh://foo"</span> <span class="st">"--force"</span>] {<span class="at">:spec</span> {<span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}}})</span>
|
|
379
|
+
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:args ["ssh://foo"], :opts {:force true}}</span></span></code></pre></div>
|
|
380
|
+
<p>Note that this library can only disambiguate correctly between values
|
|
381
|
+
for options and trailing arguments with enough <code>:coerce</code>
|
|
382
|
+
information available. Without the <code>:coerce :boolean</code> info,
|
|
383
|
+
we get:</p>
|
|
384
|
+
<div class="sourceCode" id="cb24"><pre
|
|
385
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--force"</span> <span class="st">"ssh://foo"</span>])</span>
|
|
386
|
+
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:opts</span> {<span class="at">:force</span> <span class="st">"ssh://foo"</span>}}</span></code></pre></div>
|
|
387
|
+
<p>In case of ambiguity <code>--</code> may also be used to communicate
|
|
388
|
+
the boundary between options and arguments:</p>
|
|
389
|
+
<div class="sourceCode" id="cb25"><pre
|
|
390
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--paths"</span> <span class="st">"src"</span> <span class="st">"test"</span> <span class="st">"--"</span> <span class="st">"ssh://foo"</span>] {<span class="at">:spec</span> {<span class="at">:paths</span> {<span class="at">:coerce</span> []}}})</span>
|
|
391
|
+
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:args</span> [<span class="st">"ssh://foo"</span>], <span class="at">:opts</span> {<span class="at">:paths</span> [<span class="st">"src"</span> <span class="st">"test"</span>]}}</span></code></pre></div>
|
|
392
|
+
<h3 id="args-opts">:args->opts</h3>
|
|
393
|
+
<p>To fold positional arguments into the parsed options, you can use
|
|
394
|
+
<code>:args->opts</code>:</p>
|
|
395
|
+
<div class="sourceCode" id="cb26"><pre
|
|
396
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> cli-opts </span>{<span class="at">:spec</span> {<span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}} <span class="at">:args-</span>>opts [<span class="at">:url</span>]})</span>
|
|
397
|
+
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a></span>
|
|
398
|
+
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"--force"</span> <span class="st">"ssh://foo"</span>] cli-opts)</span>
|
|
399
|
+
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:force true, :url "ssh://foo"}</span></span></code></pre></div>
|
|
400
|
+
<div class="sourceCode" id="cb27"><pre
|
|
401
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"ssh://foo"</span> <span class="st">"--force"</span>] cli-opts)</span>
|
|
402
|
+
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:url "ssh://foo", :force true}</span></span></code></pre></div>
|
|
403
|
+
<p>If you want to fold a variable amount of arguments, you can coerce
|
|
404
|
+
into a vector and specify the variable number of arguments with
|
|
405
|
+
<code>repeat</code>:</p>
|
|
406
|
+
<div class="sourceCode" id="cb28"><pre
|
|
407
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> cli-opts </span>{<span class="at">:spec</span> {<span class="at">:bar</span> {<span class="at">:coerce</span> []}} <span class="at">:args-</span>>opts (<span class="kw">cons</span> <span class="at">:foo</span> (<span class="kw">repeat</span> <span class="at">:bar</span>))})</span>
|
|
408
|
+
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"arg1"</span> <span class="st">"arg2"</span> <span class="st">"arg3"</span> <span class="st">"arg4"</span>] cli-opts)</span>
|
|
409
|
+
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:foo "arg1", :bar ["arg2" "arg3" "arg4"]}</span></span></code></pre></div>
|
|
410
|
+
<p>Options may be interspersed with the positional arguments:</p>
|
|
411
|
+
<div class="sourceCode" id="cb29"><pre
|
|
412
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> cli-opts </span>{<span class="at">:spec</span> {<span class="at">:bar</span> {<span class="at">:coerce</span> []} <span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}}</span>
|
|
413
|
+
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:args-</span>>opts (<span class="kw">cons</span> <span class="at">:foo</span> (<span class="kw">repeat</span> <span class="at">:bar</span>))})</span>
|
|
414
|
+
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">"arg1"</span> <span class="st">"arg2"</span> <span class="st">"--force"</span> <span class="st">"arg3"</span>] cli-opts)</span>
|
|
415
|
+
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:foo "arg1", :bar ["arg2" "arg3"], :force true}</span></span></code></pre></div>
|
|
416
|
+
<p>This also holds for a subcommand leaf in <a
|
|
417
|
+
href="#subcommands">dispatch</a>: a command with variadic
|
|
418
|
+
<code>:args->opts</code> parses options before, among or after its
|
|
419
|
+
positional arguments. Without <code>:args->opts</code>,
|
|
420
|
+
<code>dispatch</code> stops at the first positional (to route
|
|
421
|
+
subcommands), so trailing options would not be parsed.</p>
|
|
422
|
+
<h2 id="subcommands">Subcommands</h2>
|
|
423
|
+
<p>To handle subcommands, use <a
|
|
424
|
+
href="/API.md#dispatch">dispatch</a>.</p>
|
|
425
|
+
<p>Say we want a CLI called as:</p>
|
|
426
|
+
<pre><code>$ example copy <file> --dry-run
|
|
427
|
+
$ example delete <file> --recursive --depth 3</code></pre>
|
|
428
|
+
<div class="sourceCode" id="cb31"><pre
|
|
429
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">ns</span> example</span>
|
|
430
|
+
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a> (<span class="at">:require</span> [babashka.cli <span class="at">:as</span> cli]))</span>
|
|
431
|
+
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
432
|
+
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> copy </span>[{<span class="at">:keys</span> [opts]}]</span>
|
|
433
|
+
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">prn</span> <span class="at">:copy</span> opts))</span>
|
|
434
|
+
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a></span>
|
|
435
|
+
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> delete </span>[{<span class="at">:keys</span> [opts]}]</span>
|
|
436
|
+
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a> (<span class="kw">prn</span> <span class="at">:delete</span> opts))</span>
|
|
437
|
+
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a></span>
|
|
438
|
+
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
|
|
439
|
+
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">"copy"</span>] <span class="at">:fn</span> copy <span class="at">:doc</span> <span class="st">"Copy a file"</span> <span class="at">:args-</span>>opts [<span class="at">:file</span>]</span>
|
|
440
|
+
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:spec</span> {<span class="at">:dry-run</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:desc</span> <span class="st">"Do a dry run"</span>}}}</span>
|
|
441
|
+
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">"delete"</span>] <span class="at">:fn</span> delete <span class="at">:doc</span> <span class="st">"Delete a file"</span> <span class="at">:args-</span>>opts [<span class="at">:file</span>]</span>
|
|
442
|
+
<span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:spec</span> {<span class="at">:recursive</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:desc</span> <span class="st">"Recurse"</span>}</span>
|
|
443
|
+
<span id="cb31-15"><a href="#cb31-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:depth</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:desc</span> <span class="st">"Max depth"</span>}}}</span>
|
|
444
|
+
<span id="cb31-16"><a href="#cb31-16" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">"debug"</span>] <span class="at">:fn</span> <span class="kw">prn</span> <span class="at">:doc</span> <span class="st">"Dump internal state"</span> <span class="at">:no-doc</span> <span class="va">true</span>}])</span>
|
|
445
|
+
<span id="cb31-17"><a href="#cb31-17" aria-hidden="true" tabindex="-1"></a></span>
|
|
446
|
+
<span id="cb31-18"><a href="#cb31-18" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> -main </span>[& args]</span>
|
|
447
|
+
<span id="cb31-19"><a href="#cb31-19" aria-hidden="true" tabindex="-1"></a> (cli/dispatch table args {<span class="at">:prog</span> <span class="st">"example"</span> <span class="at">:help</span> <span class="va">true</span>}))</span></code></pre></div>
|
|
448
|
+
<p>Run it with <code>clojure -M -m example ...</code> or
|
|
449
|
+
<code>bb -m example ...</code>. <code>dispatch</code> matches the
|
|
450
|
+
longest <code>:cmds</code> path in the args and calls that entry’s
|
|
451
|
+
<code>:fn</code> with the parsed result. <code>:help true</code> wires
|
|
452
|
+
up <code>--help</code>/<code>-h</code> and terse errors (see <a
|
|
453
|
+
href="#help">Help</a>):</p>
|
|
454
|
+
<pre><code>$ example --help
|
|
455
|
+
Usage: example [options] <command>
|
|
456
|
+
|
|
457
|
+
Commands:
|
|
458
|
+
copy Copy a file
|
|
459
|
+
delete Delete a file
|
|
460
|
+
|
|
461
|
+
Options:
|
|
462
|
+
-h, --help Show this help
|
|
463
|
+
|
|
464
|
+
Run "example <command> --help" for more information on a command.</code></pre>
|
|
465
|
+
<p>The <code>Commands:</code> summaries above come from each entry’s
|
|
466
|
+
<code>:doc</code>, a string documenting that (sub)command. Its first
|
|
467
|
+
line is shown in the parent’s command list. The <code>debug</code>
|
|
468
|
+
command is absent from that list because <code>:no-doc true</code> hides
|
|
469
|
+
a command from <code>--help</code> and from completions. The same
|
|
470
|
+
<code>:no-doc true</code> on a spec option hides that option the same
|
|
471
|
+
way. The option still parses, so it works for deprecated or internal
|
|
472
|
+
flags. The full text of <code>:doc</code> is shown as the description on
|
|
473
|
+
the command’s own <code>--help</code> output, between the usage line and
|
|
474
|
+
<code>Options:</code>:</p>
|
|
475
|
+
<pre><code>$ example copy --help
|
|
476
|
+
Usage: example copy [options] <file>
|
|
477
|
+
|
|
478
|
+
Copy a file
|
|
479
|
+
|
|
480
|
+
Options:
|
|
481
|
+
--dry-run Do a dry run
|
|
482
|
+
-h, --help Show this help</code></pre>
|
|
483
|
+
<p>Running <code>example copy the-file --dry-run</code> calls
|
|
484
|
+
<code>copy</code>, which prints:</p>
|
|
485
|
+
<div class="sourceCode" id="cb34"><pre
|
|
486
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="at">:copy</span> {<span class="at">:file</span> <span class="st">"the-file"</span>, <span class="at">:dry-run</span> <span class="va">true</span>}</span></code></pre></div>
|
|
487
|
+
<p>The <code>:fn</code> is called with a map of the parsed result:</p>
|
|
488
|
+
<ul>
|
|
489
|
+
<li><code>:opts</code>: the parsed options
|
|
490
|
+
(<code>{:file "the-file" :dry-run true}</code>; <code>:file</code> comes
|
|
491
|
+
from <code>:args->opts</code>)</li>
|
|
492
|
+
<li><code>:dispatch</code>: the matched command path
|
|
493
|
+
(<code>["copy"]</code>)</li>
|
|
494
|
+
<li><code>:args</code>: any leftover positional args (<code>nil</code>
|
|
495
|
+
here)</li>
|
|
496
|
+
</ul>
|
|
497
|
+
<p>An unknown or missing subcommand prints a terse message and exits
|
|
498
|
+
1:</p>
|
|
499
|
+
<pre><code>$ example bogus
|
|
500
|
+
Unknown command: bogus
|
|
501
|
+
|
|
502
|
+
Commands:
|
|
503
|
+
copy
|
|
504
|
+
delete
|
|
505
|
+
|
|
506
|
+
Run "example --help" for more information.</code></pre>
|
|
507
|
+
<p>See <a href="https://github.com/babashka/neil">neil</a> for a
|
|
508
|
+
real-world CLI using subcommands.</p>
|
|
509
|
+
<p>Each table entry accepts any <a href="#options">parse-args</a> option
|
|
510
|
+
(<code>:spec</code>, <code>:args->opts</code>, <code>:alias</code>,
|
|
511
|
+
<code>:restrict</code>, …). The order of entries in the table doesn’t
|
|
512
|
+
matter for invocation on the command line, but is used for
|
|
513
|
+
<code>--help</code> output and completions.</p>
|
|
514
|
+
<h3 id="tree-format">Tree format</h3>
|
|
515
|
+
<p>Subcommands can also be specified in a tree format. The root accepts
|
|
516
|
+
a <code>:spec</code> for top-level options. The first level of
|
|
517
|
+
subcommands is specified under <code>:cmd</code> in a map of strings to
|
|
518
|
+
subcommand options, which are the same as in the table above, minus the
|
|
519
|
+
<code>:cmds</code> entry. You can nest arbitrarily deep.</p>
|
|
520
|
+
<div class="sourceCode" id="cb36"><pre
|
|
521
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> tree</span></span>
|
|
522
|
+
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:verbose</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:inherit</span> <span class="va">true</span> <span class="at">:desc</span> <span class="st">"Verbose output"</span>}}</span>
|
|
523
|
+
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:cmd</span> {<span class="st">"copy"</span> {<span class="at">:fn</span> copy <span class="at">:doc</span> <span class="st">"Copy a file"</span> <span class="at">:args-</span>>opts [<span class="at">:file</span>]</span>
|
|
524
|
+
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:spec</span> {<span class="at">:dry-run</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:desc</span> <span class="st">"Do a dry run"</span>}}}</span>
|
|
525
|
+
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a> <span class="st">"cache"</span> {<span class="at">:doc</span> <span class="st">"Manage the cache"</span></span>
|
|
526
|
+
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:cmd</span> {<span class="st">"clean"</span> {<span class="at">:fn</span> clean <span class="at">:doc</span> <span class="st">"Clean the cache"</span>}}}}})</span>
|
|
527
|
+
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a></span>
|
|
528
|
+
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a>(cli/dispatch tree args {<span class="at">:prog</span> <span class="st">"example"</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
|
|
529
|
+
<p>The table or tree format can be used interchangeably in
|
|
530
|
+
<code>dispatch</code>, <code>format-command-help</code> and the
|
|
531
|
+
like.</p>
|
|
532
|
+
<p>The map’s order is used for help output. This can be problematic with
|
|
533
|
+
more than 8 entries since those maps turn into unordered hash-maps. In
|
|
534
|
+
that case you can use <code>:cmd-order</code> to specify the order for
|
|
535
|
+
printing. Commands not mentioned in <code>:cmd-order</code> are left out
|
|
536
|
+
of printed output, but are still callable on the command line.</p>
|
|
537
|
+
<div class="sourceCode" id="cb37"><pre
|
|
538
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:cmd-order</span> [<span class="st">"copy"</span> <span class="st">"cache"</span>]</span>
|
|
539
|
+
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:cmd</span> {<span class="st">"copy"</span> {...}</span>
|
|
540
|
+
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a> <span class="st">"cache"</span> {...}}}</span></code></pre></div>
|
|
541
|
+
<p>Options can be supplied at each level, before and between the
|
|
542
|
+
subcommands. The root level (<code>:cmds []</code>) holds options
|
|
543
|
+
available to every command. For example:</p>
|
|
544
|
+
<div class="sourceCode" id="cb38"><pre
|
|
545
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> global-spec </span>{<span class="at">:foo</span> {<span class="at">:coerce</span> <span class="at">:keyword</span>}})</span>
|
|
546
|
+
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> sub1-spec </span>{<span class="at">:bar</span> {<span class="at">:coerce</span> <span class="at">:keyword</span>}})</span>
|
|
547
|
+
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> sub2-spec </span>{<span class="at">:baz</span> {<span class="at">:coerce</span> <span class="at">:keyword</span>}})</span>
|
|
548
|
+
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a></span>
|
|
549
|
+
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
|
|
550
|
+
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [] <span class="at">:spec</span> global-spec}</span>
|
|
551
|
+
<span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">"sub1"</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub1-spec}</span>
|
|
552
|
+
<span id="cb38-8"><a href="#cb38-8" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">"sub1"</span> <span class="st">"sub2"</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub2-spec}])</span>
|
|
553
|
+
<span id="cb38-9"><a href="#cb38-9" aria-hidden="true" tabindex="-1"></a></span>
|
|
554
|
+
<span id="cb38-10"><a href="#cb38-10" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"--foo"</span> <span class="st">"a"</span> <span class="st">"sub1"</span> <span class="st">"--bar"</span> <span class="st">"b"</span> <span class="st">"sub2"</span> <span class="st">"--baz"</span> <span class="st">"c"</span> <span class="st">"arg"</span>])</span>
|
|
555
|
+
<span id="cb38-11"><a href="#cb38-11" aria-hidden="true" tabindex="-1"></a></span>
|
|
556
|
+
<span id="cb38-12"><a href="#cb38-12" aria-hidden="true" tabindex="-1"></a><span class="co">;;=></span></span>
|
|
557
|
+
<span id="cb38-13"><a href="#cb38-13" aria-hidden="true" tabindex="-1"></a></span>
|
|
558
|
+
<span id="cb38-14"><a href="#cb38-14" aria-hidden="true" tabindex="-1"></a>{<span class="at">:dispatch</span> [<span class="st">"sub1"</span> <span class="st">"sub2"</span>],</span>
|
|
559
|
+
<span id="cb38-15"><a href="#cb38-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:opts</span> {<span class="at">:foo</span> <span class="at">:a</span>, <span class="at">:bar</span> <span class="at">:b</span>, <span class="at">:baz</span> <span class="at">:c</span>},</span>
|
|
560
|
+
<span id="cb38-16"><a href="#cb38-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:args</span> [<span class="st">"arg"</span>]}</span></code></pre></div>
|
|
561
|
+
<p>It is possible to use <code>:args->opts</code>, but subcommands
|
|
562
|
+
are always prioritized over arguments:</p>
|
|
563
|
+
<div class="sourceCode" id="cb39"><pre
|
|
564
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
|
|
565
|
+
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">"sub1"</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub1-spec <span class="at">:args-</span>>opts [<span class="at">:some-opt</span>]}</span>
|
|
566
|
+
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">"sub1"</span> <span class="st">"sub2"</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub2-spec}])</span>
|
|
567
|
+
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a></span>
|
|
568
|
+
<span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"sub1"</span> <span class="st">"dude"</span>]) <span class="co">;;=> {:dispatch ["sub1"], :opts {:some-opt "dude"}}</span></span>
|
|
569
|
+
<span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"sub1"</span> <span class="st">"sub2"</span>]) <span class="co">;;=> {:dispatch ["sub1" "sub2"], :opts {}}</span></span></code></pre></div>
|
|
570
|
+
<p>Specs are not merged across levels: an option is parsed with the spec
|
|
571
|
+
of the level it appears at, so it must be supplied before its
|
|
572
|
+
subcommand. With <code>:restrict</code>, supplying it after the
|
|
573
|
+
subcommand is an error:</p>
|
|
574
|
+
<div class="sourceCode" id="cb40"><pre
|
|
575
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
|
|
576
|
+
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">"group"</span>] <span class="at">:spec</span> {<span class="at">:registry</span> {}}}</span>
|
|
577
|
+
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">"group"</span> <span class="st">"sub"</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> {<span class="at">:format</span> {}}}])</span>
|
|
578
|
+
<span id="cb40-4"><a href="#cb40-4" aria-hidden="true" tabindex="-1"></a></span>
|
|
579
|
+
<span id="cb40-5"><a href="#cb40-5" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"group"</span> <span class="st">"--registry"</span> <span class="st">"X"</span> <span class="st">"sub"</span>] {<span class="at">:restrict</span> <span class="va">true</span>})</span>
|
|
580
|
+
<span id="cb40-6"><a href="#cb40-6" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:dispatch ["group" "sub"], :opts {:registry "X"}}</span></span>
|
|
581
|
+
<span id="cb40-7"><a href="#cb40-7" aria-hidden="true" tabindex="-1"></a></span>
|
|
582
|
+
<span id="cb40-8"><a href="#cb40-8" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"group"</span> <span class="st">"sub"</span> <span class="st">"--registry"</span> <span class="st">"X"</span>] {<span class="at">:restrict</span> <span class="va">true</span>})</span>
|
|
583
|
+
<span id="cb40-9"><a href="#cb40-9" aria-hidden="true" tabindex="-1"></a><span class="co">;; throws: Unknown option: --registry</span></span></code></pre></div>
|
|
584
|
+
<p>Mark an option with <code>:inherit true</code> to also accept it at
|
|
585
|
+
any descendant level (after the subcommand). It is coerced and
|
|
586
|
+
restrict-checked wherever it appears:</p>
|
|
587
|
+
<div class="sourceCode" id="cb41"><pre
|
|
588
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
|
|
589
|
+
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">"group"</span>] <span class="at">:spec</span> {<span class="at">:registry</span> {<span class="at">:inherit</span> <span class="va">true</span>}}}</span>
|
|
590
|
+
<span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">"group"</span> <span class="st">"sub"</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> {<span class="at">:format</span> {}}}])</span>
|
|
591
|
+
<span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a></span>
|
|
592
|
+
<span id="cb41-5"><a href="#cb41-5" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"group"</span> <span class="st">"sub"</span> <span class="st">"--registry"</span> <span class="st">"X"</span>] {<span class="at">:restrict</span> <span class="va">true</span>})</span>
|
|
593
|
+
<span id="cb41-6"><a href="#cb41-6" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:dispatch ["group" "sub"], :opts {:registry "X"}}</span></span></code></pre></div>
|
|
594
|
+
<p>A descendant may redefine it in its own spec, in which case the
|
|
595
|
+
descendant’s definition wins.</p>
|
|
596
|
+
<p>Instead of marking individual options, pass <code>:inherit</code> to
|
|
597
|
+
<code>dispatch</code>: <code>true</code> to inherit all options, or a
|
|
598
|
+
set of keys to inherit only those:</p>
|
|
599
|
+
<div class="sourceCode" id="cb42"><pre
|
|
600
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"group"</span> <span class="st">"sub"</span> <span class="st">"--registry"</span> <span class="st">"X"</span>] {<span class="at">:inherit</span> <span class="va">true</span>})</span>
|
|
601
|
+
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">"group"</span> <span class="st">"sub"</span> <span class="st">"--registry"</span> <span class="st">"X"</span>] {<span class="at">:inherit</span> #{<span class="at">:registry</span>}})</span></code></pre></div>
|
|
602
|
+
<h3 id="help">Help</h3>
|
|
603
|
+
<p>Pass <code>:help true</code> to <code>dispatch</code> (and
|
|
604
|
+
<code>:prog</code>, the program name) to add help to a CLI, no
|
|
605
|
+
<code>:restrict</code> needed:</p>
|
|
606
|
+
<div class="sourceCode" id="cb43"><pre
|
|
607
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args {<span class="at">:prog</span> <span class="st">"example"</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
|
|
608
|
+
<ul>
|
|
609
|
+
<li><p><code>--help</code>/<code>-h</code> print help for the command in
|
|
610
|
+
front of them and return (the process ends with status 0). So
|
|
611
|
+
<code>example deps outdated --help</code> shows help for
|
|
612
|
+
<code>deps outdated</code>.</p></li>
|
|
613
|
+
<li><p>A mistyped or missing subcommand prints a terse message and exits
|
|
614
|
+
with 1.</p></li>
|
|
615
|
+
<li><p><code>-h, --help</code> is listed in each command’s options,
|
|
616
|
+
appended last. To control the order, give the command entry an
|
|
617
|
+
<code>:order</code> (a vector of option keys); it is used verbatim, so
|
|
618
|
+
you decide the order, which options to list, and whether to list
|
|
619
|
+
<code>--help</code> at all (omit <code>:help</code> to hide it; it still
|
|
620
|
+
works):</p>
|
|
621
|
+
<div class="sourceCode" id="cb44"><pre
|
|
622
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:cmds</span> [...] <span class="at">:spec</span> {...} <span class="at">:order</span> [<span class="at">:help</span> <span class="at">:port</span> <span class="at">:verbose</span>]} <span class="co">; --help first</span></span></code></pre></div>
|
|
623
|
+
<p>Without <code>:order</code>, the order is taken from the spec (a
|
|
624
|
+
vec-of-pairs spec keeps its order; a map follows its key order, which
|
|
625
|
+
Clojure does not guarantee), and <code>--help</code> is
|
|
626
|
+
appended.</p></li>
|
|
627
|
+
<li><p><code>--help</code>/<code>-h</code> are reserved while
|
|
628
|
+
<code>:help</code> is on (a command may still define its own
|
|
629
|
+
<code>:help</code>).</p></li>
|
|
630
|
+
<li><p>An entry’s <code>:epilog</code> (a string) is rendered verbatim
|
|
631
|
+
after that command’s options, for examples, notes or links. Put it on
|
|
632
|
+
the root entry (<code>:cmds []</code>) for the top-level help.</p></li>
|
|
633
|
+
</ul>
|
|
634
|
+
<p>It works for a single-command CLI too: a one-entry table whose
|
|
635
|
+
<code>:cmds</code> is <code>[]</code>. <code>example --help</code> then
|
|
636
|
+
shows Usage + Options:</p>
|
|
637
|
+
<div class="sourceCode" id="cb45"><pre
|
|
638
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch [{<span class="at">:cmds</span> [] <span class="at">:fn</span> run <span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:desc</span> <span class="st">"Port"</span>}}}]</span>
|
|
639
|
+
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a> args</span>
|
|
640
|
+
<span id="cb45-3"><a href="#cb45-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:prog</span> <span class="st">"example"</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
|
|
641
|
+
<p><code>--help</code>/<code>-h</code> are a success path: they print
|
|
642
|
+
help and return (no exit call), so your <code>-main</code> ends and the
|
|
643
|
+
process exits 0, like a normal command. Errors go through the dynamic
|
|
644
|
+
<code>*exit-fn*</code>, which exits non-zero:</p>
|
|
645
|
+
<table>
|
|
646
|
+
<colgroup>
|
|
647
|
+
<col style="width: 50%" />
|
|
648
|
+
<col style="width: 50%" />
|
|
649
|
+
</colgroup>
|
|
650
|
+
<thead>
|
|
651
|
+
<tr class="header">
|
|
652
|
+
<th>invocation</th>
|
|
653
|
+
<th>outcome</th>
|
|
654
|
+
</tr>
|
|
655
|
+
</thead>
|
|
656
|
+
<tbody>
|
|
657
|
+
<tr class="odd">
|
|
658
|
+
<td><code>--help</code> / <code>-h</code></td>
|
|
659
|
+
<td>print help, return (status 0), no <code>*exit-fn*</code></td>
|
|
660
|
+
</tr>
|
|
661
|
+
<tr class="even">
|
|
662
|
+
<td>group, no subcommand</td>
|
|
663
|
+
<td>terse message, <code>*exit-fn*</code> exit 1,
|
|
664
|
+
<code>:cause :input-exhausted</code></td>
|
|
665
|
+
</tr>
|
|
666
|
+
<tr class="odd">
|
|
667
|
+
<td>unknown subcommand</td>
|
|
668
|
+
<td>terse message, <code>*exit-fn*</code> exit 1,
|
|
669
|
+
<code>:cause :no-match</code></td>
|
|
670
|
+
</tr>
|
|
671
|
+
<tr class="even">
|
|
672
|
+
<td>flag error</td>
|
|
673
|
+
<td>terse message, <code>*exit-fn*</code> exit 1, <code>:cause</code> =
|
|
674
|
+
the babashka.cli cause</td>
|
|
675
|
+
</tr>
|
|
676
|
+
</tbody>
|
|
677
|
+
</table>
|
|
678
|
+
<p>A bare group is a usage error (exit 1), like <code>git bisect</code>
|
|
679
|
+
with no subcommand; its full help is one keystroke away via
|
|
680
|
+
<code>--help</code>. <code>*exit-fn*</code> is called only on errors,
|
|
681
|
+
with <code>{:exit :cause :dispatch :data}</code> (<code>:cause</code> is
|
|
682
|
+
the dispatch cause: <code>:no-match</code>,
|
|
683
|
+
<code>:input-exhausted</code>, or a flag cause; <code>:data</code> holds
|
|
684
|
+
the raw <code>dispatch</code> error data). The default exits the process
|
|
685
|
+
(<code>System/exit</code> on JVM, <code>js/process.exit</code> on Node);
|
|
686
|
+
rebind it to not exit (tests, REPL) or to remap codes by
|
|
687
|
+
<code>:cause</code>:</p>
|
|
688
|
+
<div class="sourceCode" id="cb46"><pre
|
|
689
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a><span class="co">;; treat a bare group as success (exit 0) instead of a usage error</span></span>
|
|
690
|
+
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>(<span class="kw">binding</span> [cli/*exit-fn* (<span class="kw">fn</span> [{<span class="at">:keys</span> [exit cause]}]</span>
|
|
691
|
+
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a> (System/exit (<span class="kw">if</span> (<span class="kw">=</span> <span class="at">:input-exhausted</span> cause) <span class="dv">0</span> exit)))]</span>
|
|
692
|
+
<span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a> (cli/dispatch table args {<span class="at">:prog</span> <span class="st">"example"</span> <span class="at">:help</span> <span class="va">true</span>}))</span></code></pre></div>
|
|
693
|
+
<p>Both handlers are overridable: pass your own <code>:help-fn</code>
|
|
694
|
+
(called with <code>{:tree :dispatch :prog :inherit}</code>) and/or
|
|
695
|
+
<code>:error-fn</code> to <code>dispatch</code>. To render the standard
|
|
696
|
+
help and add to it, call <code>format-command-help</code>, the same
|
|
697
|
+
renderer the default uses:</p>
|
|
698
|
+
<div class="sourceCode" id="cb47"><pre
|
|
699
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args</span>
|
|
700
|
+
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:prog</span> <span class="st">"example"</span> <span class="at">:help</span> <span class="va">true</span></span>
|
|
701
|
+
<span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:help-fn</span> (<span class="kw">fn</span> [{<span class="at">:keys</span> [tree dispatch prog inherit]}]</span>
|
|
702
|
+
<span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">"my-tool v1.2.3"</span>)</span>
|
|
703
|
+
<span id="cb47-5"><a href="#cb47-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> (cli/format-command-help</span>
|
|
704
|
+
<span id="cb47-6"><a href="#cb47-6" aria-hidden="true" tabindex="-1"></a> {<span class="at">:table</span> tree <span class="at">:cmds</span> dispatch <span class="at">:prog</span> prog <span class="at">:inherit</span> inherit})))})</span></code></pre></div>
|
|
705
|
+
<p>The function <code>format-command-help</code> is also usable on its
|
|
706
|
+
own (without <code>dispatch</code>): pass <code>:table</code> (a
|
|
707
|
+
<code>dispatch</code> <a href="#tree-format">table or tree</a>),
|
|
708
|
+
<code>:cmds</code> (the command path, default <code>[]</code>),
|
|
709
|
+
<code>:prog</code>, and optional <code>:inherit</code>. It returns the
|
|
710
|
+
help string.</p>
|
|
711
|
+
<p>A custom <code>:error-fn</code> receives the dispatch error data
|
|
712
|
+
(<code>{:cause :dispatch :prog :inherit :tree :msg ...}</code>) and is
|
|
713
|
+
responsible for exiting (call <code>*exit-fn*</code> or exit yourself).
|
|
714
|
+
To keep the standard terse message and add to it, call
|
|
715
|
+
<code>format-command-error</code> (the same renderer the default uses)
|
|
716
|
+
and exit afterwards:</p>
|
|
717
|
+
<div class="sourceCode" id="cb48"><pre
|
|
718
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args</span>
|
|
719
|
+
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:prog</span> <span class="st">"example"</span> <span class="at">:help</span> <span class="va">true</span></span>
|
|
720
|
+
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:error-fn</span> (<span class="kw">fn</span> [data]</span>
|
|
721
|
+
<span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> (cli/format-command-error data))</span>
|
|
722
|
+
<span id="cb48-5"><a href="#cb48-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">"See https://example.com/docs"</span>)</span>
|
|
723
|
+
<span id="cb48-6"><a href="#cb48-6" aria-hidden="true" tabindex="-1"></a> (cli/*exit-fn* {<span class="at">:exit</span> <span class="dv">1</span> <span class="at">:cause</span> (<span class="at">:cause</span> data)}))})</span></code></pre></div>
|
|
724
|
+
<h2 id="completions">Completions</h2>
|
|
725
|
+
<p>The <code>dispatch</code> function can generate dynamic shell
|
|
726
|
+
completions for <code>bash</code>, <code>zsh</code>, <code>fish</code>,
|
|
727
|
+
<code>powershell</code> and <code>nushell</code>. Shells call back into
|
|
728
|
+
your program on each TAB to generate completions. The <code>:prog</code>
|
|
729
|
+
(program name) value is essential in the <code>dispatch</code> call. The
|
|
730
|
+
generated snippet registers completion for that name, so it must match
|
|
731
|
+
the command you type, and it must be a plain command name consisting of
|
|
732
|
+
only alphanumeric characters, <code>.</code>, <code>_</code> or
|
|
733
|
+
<code>-</code>.</p>
|
|
734
|
+
<div class="sourceCode" id="cb49"><pre
|
|
735
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args {<span class="at">:prog</span> <span class="st">"mycli"</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
|
|
736
|
+
<p>If the installed command has a different name, e.g. when a distro
|
|
737
|
+
renames it, pass <code>--prog <name></code> when generating the
|
|
738
|
+
snippet to register that name instead:</p>
|
|
739
|
+
<div class="sourceCode" id="cb50"><pre
|
|
740
|
+
class="sourceCode bash"><code class="sourceCode bash"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a><span class="ex">mycli</span> org.babashka.cli/completions snippet <span class="at">--shell</span> zsh <span class="at">--prog</span> sq</span></code></pre></div>
|
|
741
|
+
<p>The completions call goes through a hidden
|
|
742
|
+
<code>org.babashka.cli/completions</code> subcommand group that
|
|
743
|
+
<code>dispatch</code> adds for you. Running
|
|
744
|
+
<code>mycli org.babashka.cli/completions snippet --shell <shell></code>
|
|
745
|
+
prints the install snippet for that specific shell to stdout. It does
|
|
746
|
+
not write files or edit your shell config for you.</p>
|
|
747
|
+
<p>Subcommands and options come with completion support out of the box.
|
|
748
|
+
Descriptions come from the same <code>:desc</code> (options) and
|
|
749
|
+
<code>:doc</code> (subcommands) you already write for
|
|
750
|
+
<code>--help</code>. A <code>:no-doc</code> subcommand or option is
|
|
751
|
+
hidden. Options that already appeared are filtered out of later
|
|
752
|
+
suggestions, except repeatable options
|
|
753
|
+
(e.g. <code>:coerce [:string]</code>).</p>
|
|
754
|
+
<p>Here follow the instructions to enable auto-completions in your
|
|
755
|
+
shell.</p>
|
|
756
|
+
<h3 id="bash">Bash</h3>
|
|
757
|
+
<p>Add this code to your bash init file:</p>
|
|
758
|
+
<div class="sourceCode" id="cb51"><pre
|
|
759
|
+
class="sourceCode bash"><code class="sourceCode bash"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a><span class="bu">source</span> <span class="op"><(</span><span class="ex">mycli</span> org.babashka.cli/completions snippet <span class="at">--shell</span> bash<span class="op">)</span></span></code></pre></div>
|
|
760
|
+
<p>Bash completes values only and does not show descriptions. For
|
|
761
|
+
correct handling of <code>=</code> and <code>:</code> inside values,
|
|
762
|
+
install the bash-completion package, which needs bash 4.1 or newer. The
|
|
763
|
+
macOS system bash 3.2 still works for the common cases.</p>
|
|
764
|
+
<h3 id="zsh">Zsh</h3>
|
|
765
|
+
<p>Add this to your zsh init file, after <code>compinit</code>:</p>
|
|
766
|
+
<div class="sourceCode" id="cb52"><pre
|
|
767
|
+
class="sourceCode bash"><code class="sourceCode bash"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a><span class="bu">source</span> <span class="op"><(</span><span class="ex">mycli</span> org.babashka.cli/completions snippet <span class="at">--shell</span> zsh<span class="op">)</span></span></code></pre></div>
|
|
768
|
+
<p>or save the output as <code>_mycli</code> on your
|
|
769
|
+
<code>$fpath</code>. Option and subcommand descriptions show inline.
|
|
770
|
+
Completions also fire when the program is invoked by path, such as
|
|
771
|
+
<code>./mycli</code>.</p>
|
|
772
|
+
<h3 id="fish">Fish</h3>
|
|
773
|
+
<pre class="fish"><code>mycli org.babashka.cli/completions snippet --shell fish | source</code></pre>
|
|
774
|
+
<p>Option and subcommand descriptions show inline. Completion also fires
|
|
775
|
+
on a path invocation.</p>
|
|
776
|
+
<h3 id="powershell">Powershell</h3>
|
|
777
|
+
<p>Add this to your <code>$PROFILE</code>:</p>
|
|
778
|
+
<div class="sourceCode" id="cb54"><pre
|
|
779
|
+
class="sourceCode powershell"><code class="sourceCode powershell"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>mycli org<span class="op">.</span><span class="fu">babashka</span><span class="op">.</span><span class="fu">cli</span><span class="op">/</span>completions snippet <span class="op">--</span>shell powershell <span class="op">|</span> <span class="fu">Out-String</span> <span class="op">|</span> <span class="fu">Invoke-Expression</span></span></code></pre></div>
|
|
780
|
+
<p>Descriptions show in menu-completion mode, which you can enable with
|
|
781
|
+
<code>Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete</code>.</p>
|
|
782
|
+
<h3 id="nushell">Nushell</h3>
|
|
783
|
+
<p>Nushell cannot <code>source</code> from a pipe, so save the snippet
|
|
784
|
+
to a file in the autoload directory and restart <code>nu</code>:</p>
|
|
785
|
+
<pre class="nu"><code>mkdir ($nu.user-autoload-dirs | first)
|
|
786
|
+
mycli org.babashka.cli/completions snippet --shell nushell | save -f (($nu.user-autoload-dirs | first) | path join "mycli.nu")</code></pre>
|
|
787
|
+
<p>On nushell versions without autoload dirs, save it anywhere and add
|
|
788
|
+
<code>source <literal path></code> to your <code>config.nu</code>.
|
|
789
|
+
Descriptions show in the completion menu.</p>
|
|
790
|
+
<p>Unlike the other shells, nushell has no per-command completion
|
|
791
|
+
registration. Instead, one global hook
|
|
792
|
+
(<code>$env.config.completions.external.completer</code>) handles TAB
|
|
793
|
+
for all external commands. The snippet does not overwrite a completer
|
|
794
|
+
you already have there: it saves the previous one and falls back to it
|
|
795
|
+
for every command other than <code>mycli</code>, so several tools can
|
|
796
|
+
install side by side.</p>
|
|
797
|
+
<h3 id="developing-completions">Developing completions</h3>
|
|
798
|
+
<p>Completions are registered for the command name <code>:prog</code>,
|
|
799
|
+
so the command you type must match it. During development you usually
|
|
800
|
+
invoke the build directly, e.g. <code>./run.clj</code>, under a
|
|
801
|
+
different name. Make the dev build callable under your
|
|
802
|
+
<code>:prog</code> name on <code>PATH</code>. On unix shells, symlink it
|
|
803
|
+
and prepend its directory:</p>
|
|
804
|
+
<div class="sourceCode" id="cb56"><pre
|
|
805
|
+
class="sourceCode bash"><code class="sourceCode bash"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a><span class="fu">ln</span> <span class="at">-sf</span> <span class="st">"</span><span class="va">$PWD</span><span class="st">/run.clj"</span> /tmp/mycli <span class="co"># name the link :prog</span></span>
|
|
806
|
+
<span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">PATH</span><span class="op">=</span><span class="st">"/tmp:</span><span class="va">$PATH</span><span class="st">"</span> <span class="co"># bash and zsh</span></span></code></pre></div>
|
|
807
|
+
<p>In fish use <code>set -gx PATH /tmp $PATH</code>, in nushell
|
|
808
|
+
<code>$env.PATH = ($env.PATH | prepend /tmp)</code>. On Windows, put a
|
|
809
|
+
<code>mycli</code> wrapper script on your <code>PATH</code> instead of a
|
|
810
|
+
symlink.</p>
|
|
811
|
+
<p>Then source the snippet in the shell you are testing, using the
|
|
812
|
+
install command from its section above, and re-source it after each
|
|
813
|
+
change to your CLI so new commands and options show up.</p>
|
|
814
|
+
<p>Now <code>mycli <TAB></code> completes commands and
|
|
815
|
+
<code>mycli sub --<TAB></code> its options. To see the completer’s
|
|
816
|
+
raw output directly, without a shell, call the hidden subcommand
|
|
817
|
+
yourself. The tokens after <code>--</code> are what the shell would pass
|
|
818
|
+
on TAB, here the subcommand <code>sub</code> and a <code>--</code> to
|
|
819
|
+
complete its options. It prints one candidate per line, as the value, a
|
|
820
|
+
tab, then the description:</p>
|
|
821
|
+
<div class="sourceCode" id="cb57"><pre
|
|
822
|
+
class="sourceCode bash"><code class="sourceCode bash"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a><span class="ex">mycli</span> org.babashka.cli/completions complete <span class="at">--shell</span> zsh <span class="at">--</span> sub <span class="at">--</span></span></code></pre></div>
|
|
823
|
+
<h3 id="completing-option-values">Completing option values</h3>
|
|
824
|
+
<p>To complete an option’s value, give it one of:</p>
|
|
825
|
+
<ul>
|
|
826
|
+
<li><code>:complete</code> - a static collection of values (or
|
|
827
|
+
<code>{:value .. :description ..}</code> maps)</li>
|
|
828
|
+
<li>A set-valued <code>:validate</code>, whose members double as
|
|
829
|
+
completions</li>
|
|
830
|
+
<li><code>:complete-fn</code> - a function for dynamic completion</li>
|
|
831
|
+
</ul>
|
|
832
|
+
<div class="sourceCode" id="cb58"><pre
|
|
833
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:env</span> {<span class="at">:coerce</span> <span class="at">:string</span></span>
|
|
834
|
+
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:complete</span> [<span class="st">"dev"</span> <span class="st">"staging"</span> <span class="st">"prod"</span>]} <span class="co">; static list</span></span>
|
|
835
|
+
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:level</span> {<span class="at">:coerce</span> <span class="at">:keyword</span></span>
|
|
836
|
+
<span id="cb58-4"><a href="#cb58-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:validate</span> #{<span class="at">:local</span> <span class="at">:global</span> <span class="at">:system</span>}} <span class="co">; reused as completions</span></span>
|
|
837
|
+
<span id="cb58-5"><a href="#cb58-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:branch</span> {<span class="at">:coerce</span> <span class="at">:string</span></span>
|
|
838
|
+
<span id="cb58-6"><a href="#cb58-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:complete-fn</span> (<span class="kw">fn</span> [{<span class="at">:keys</span> [to-complete opts]}] <span class="co">; dynamic</span></span>
|
|
839
|
+
<span id="cb58-7"><a href="#cb58-7" aria-hidden="true" tabindex="-1"></a> (git-branches to-complete))}}</span></code></pre></div>
|
|
840
|
+
<p>The <code>:complete-fn</code> is called with
|
|
841
|
+
<code>{:to-complete <partial> :opts <opts parsed so far> :option <key>}</code>
|
|
842
|
+
and returns values (strings) (or
|
|
843
|
+
<code>{:value .. :description ..}</code> maps). All three sources are
|
|
844
|
+
prefix-filtered against the partial value for you.</p>
|
|
845
|
+
<p>An option value with none of these defaults to the shell’s own file
|
|
846
|
+
completion. For a value where file suggestions are not appropriate, you
|
|
847
|
+
can opt out with <code>:complete false</code>.</p>
|
|
848
|
+
<p>Positional arguments mapped with <a
|
|
849
|
+
href="#args-opts"><code>:args->opts</code></a> complete in the same
|
|
850
|
+
way. A positional resolves to its spec key by position, so the same
|
|
851
|
+
<code>:complete</code>, <code>:complete-fn</code> or set
|
|
852
|
+
<code>:validate</code> on that key completes the positional too. With
|
|
853
|
+
<code>:args->opts [:env]</code> and
|
|
854
|
+
<code>:env {:complete ["dev" "prod"]}</code>,
|
|
855
|
+
<code>mycli deploy <TAB></code> completes
|
|
856
|
+
<code>dev</code>/<code>prod</code>.</p>
|
|
857
|
+
<p>A positional declared in <code>:args->opts</code> with no value
|
|
858
|
+
completion defaults to the shell’s own file completion the same way. So
|
|
859
|
+
<code>:args->opts [:file]</code> with a bare <code>:file</code> makes
|
|
860
|
+
<code>mycli cat <TAB></code> complete filenames and
|
|
861
|
+
<code>:complete false</code> opts out here too.</p>
|
|
862
|
+
<h2 id="adding-production-polish">Adding Production Polish</h2>
|
|
863
|
+
<p>Babashka cli lets you get up and running quickly. As you move toward
|
|
864
|
+
production quality, it’s helpful to let users know when their inputs are
|
|
865
|
+
invalid. Strict validation can be introduced with <a
|
|
866
|
+
href="#restrict">:restrict</a>, <a href="#require">:require</a>, and <a
|
|
867
|
+
href="#validate">:validate</a>.</p>
|
|
868
|
+
<p>As you add polish, you’ll likely make use of a <a
|
|
869
|
+
href="#spec">:spec</a>, a custom <a
|
|
870
|
+
href="#error-handling">:error_fn</a>, and maybe <a
|
|
871
|
+
href="#subcommands">subcommand dispatching</a>.</p>
|
|
872
|
+
<h2 id="restrict">Restrict</h2>
|
|
873
|
+
<p>Use the <code>:restrict</code> option to restrict options to only
|
|
874
|
+
those explicitly mentioned in configuration:</p>
|
|
875
|
+
<div class="sourceCode" id="cb59"><pre
|
|
876
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--foo"</span>] {<span class="at">:spec</span> {<span class="at">:bar</span> {}} <span class="at">:restrict</span> <span class="va">true</span>})</span>
|
|
877
|
+
<span id="cb59-2"><a href="#cb59-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=></span></span>
|
|
878
|
+
<span id="cb59-3"><a href="#cb59-3" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:357</span>).</span>
|
|
879
|
+
<span id="cb59-4"><a href="#cb59-4" aria-hidden="true" tabindex="-1"></a>Unknown option: <span class="at">:foo</span></span></code></pre></div>
|
|
880
|
+
<h2 id="require">Require</h2>
|
|
881
|
+
<p>Mark an option required in its <a href="#spec">spec</a> with
|
|
882
|
+
<code>:require true</code>; parsing throws when it is not present:</p>
|
|
883
|
+
<div class="sourceCode" id="cb60"><pre
|
|
884
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--foo"</span>] {<span class="at">:spec</span> {<span class="at">:bar</span> {<span class="at">:require</span> <span class="va">true</span>}}})</span>
|
|
885
|
+
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=></span></span>
|
|
886
|
+
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:363</span>).</span>
|
|
887
|
+
<span id="cb60-4"><a href="#cb60-4" aria-hidden="true" tabindex="-1"></a>Required option: <span class="at">:bar</span></span></code></pre></div>
|
|
888
|
+
<p>Required options are shown as <code>(required)</code> in
|
|
889
|
+
<code>--help</code>, in the slot a default would otherwise occupy.</p>
|
|
890
|
+
<h2 id="validate">Validate</h2>
|
|
891
|
+
<div class="sourceCode" id="cb61"><pre
|
|
892
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--foo"</span> <span class="st">"0"</span>] {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:validate</span> <span class="kw">pos?</span>}}})</span>
|
|
893
|
+
<span id="cb61-2"><a href="#cb61-2" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:378</span>).</span>
|
|
894
|
+
<span id="cb61-3"><a href="#cb61-3" aria-hidden="true" tabindex="-1"></a>Invalid value <span class="kw">for</span> option <span class="at">:foo</span>: <span class="dv">0</span></span></code></pre></div>
|
|
895
|
+
<p>To gain more control over the error message, use <code>:pred</code>
|
|
896
|
+
and <code>:ex-msg</code>:</p>
|
|
897
|
+
<div class="sourceCode" id="cb62"><pre
|
|
898
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--foo"</span> <span class="st">"0"</span>] {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:validate</span> {<span class="at">:pred</span> <span class="kw">pos?</span> <span class="at">:ex-msg</span> (<span class="kw">fn</span> [m] (<span class="kw">str</span> <span class="st">"Not a positive number: "</span> (<span class="at">:value</span> m)))}}}})</span>
|
|
899
|
+
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=></span></span>
|
|
900
|
+
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:378</span>).</span>
|
|
901
|
+
<span id="cb62-4"><a href="#cb62-4" aria-hidden="true" tabindex="-1"></a>Not a positive number: <span class="dv">0</span></span></code></pre></div>
|
|
902
|
+
<h2 id="error-handling">Error handling</h2>
|
|
903
|
+
<p>By default, an exception will be thrown in the following situations:
|
|
904
|
+
- A restricted option is encountered - A required option is missing -
|
|
905
|
+
Validation fails for an option - Coercion fails for an option</p>
|
|
906
|
+
<p>You may supply a custom error handler function with
|
|
907
|
+
<code>:error-fn</code>. The function will be called with a map
|
|
908
|
+
containing the following keys: - <code>:type</code> -
|
|
909
|
+
<code>:org.babashka/cli</code> (for filtering out other types of
|
|
910
|
+
errors). - <code>:cause</code> - one of: - <code>:restrict</code> - a
|
|
911
|
+
restricted option was encountered. - <code>:require</code> - a required
|
|
912
|
+
option was missing. - <code>:validate</code> - validation failed for an
|
|
913
|
+
option. - <code>:coerce</code> - coercion failed for an option. -
|
|
914
|
+
<code>:msg</code> - default error message. - <code>:option</code> - the
|
|
915
|
+
option being parsed when the error occurred. - <code>:spec</code> - the
|
|
916
|
+
spec passed into <code>parse-opts</code> (see the <a
|
|
917
|
+
href="#spec">Spec</a> section).</p>
|
|
918
|
+
<p>The following keys are present depending on <code>:cause</code>: -
|
|
919
|
+
<code>:cause :restrict</code> - <code>:restrict</code> - the value of
|
|
920
|
+
the <code>:restrict</code> opt to <code>parse-args</code> (see the <a
|
|
921
|
+
href="#restrict">Restrict</a> section). - <code>:cause :require</code> -
|
|
922
|
+
<code>:require</code> - the value of the <code>:require</code> opt to
|
|
923
|
+
<code>parse-args</code> (see the <a href="#require">Require</a>
|
|
924
|
+
section). - <code>:cause :validate</code> - <code>:value</code> - the
|
|
925
|
+
value of the option that failed validation. - <code>:validate</code> -
|
|
926
|
+
the value of the <code>:validate</code> opt to <code>parse-args</code>
|
|
927
|
+
(see the <a href="#validate">Validate</a> section). -
|
|
928
|
+
<code>:cause :coerce</code> - <code>:value</code> - the value of the
|
|
929
|
+
option that failed coercion.</p>
|
|
930
|
+
<p>By default, babashka cli will throw exception on errors it detects.
|
|
931
|
+
You can do the same from your custom error handler.</p>
|
|
932
|
+
<p>For a more polished user experience, you might choose to have your
|
|
933
|
+
custom error handler print the error and exit. For example:</p>
|
|
934
|
+
<div class="sourceCode" id="cb63"><pre
|
|
935
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts</span>
|
|
936
|
+
<span id="cb63-2"><a href="#cb63-2" aria-hidden="true" tabindex="-1"></a> []</span>
|
|
937
|
+
<span id="cb63-3"><a href="#cb63-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:desc</span> <span class="st">"You know what this is."</span></span>
|
|
938
|
+
<span id="cb63-4"><a href="#cb63-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:ref</span> <span class="st">"<val>"</span></span>
|
|
939
|
+
<span id="cb63-5"><a href="#cb63-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:require</span> <span class="va">true</span>}}</span>
|
|
940
|
+
<span id="cb63-6"><a href="#cb63-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:error-fn</span></span>
|
|
941
|
+
<span id="cb63-7"><a href="#cb63-7" aria-hidden="true" tabindex="-1"></a> (<span class="kw">fn</span> [{<span class="at">:keys</span> [spec <span class="kw">type</span> cause msg option] <span class="at">:as</span> data}]</span>
|
|
942
|
+
<span id="cb63-8"><a href="#cb63-8" aria-hidden="true" tabindex="-1"></a> (<span class="kw">if</span> (<span class="kw">=</span> <span class="at">:org.babashka/cli</span> <span class="kw">type</span>)</span>
|
|
943
|
+
<span id="cb63-9"><a href="#cb63-9" aria-hidden="true" tabindex="-1"></a> (<span class="kw">case</span> cause</span>
|
|
944
|
+
<span id="cb63-10"><a href="#cb63-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:require</span></span>
|
|
945
|
+
<span id="cb63-11"><a href="#cb63-11" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span></span>
|
|
946
|
+
<span id="cb63-12"><a href="#cb63-12" aria-hidden="true" tabindex="-1"></a> (<span class="kw">format</span> <span class="st">"Missing required argument:</span><span class="sc">\n</span><span class="st">%s"</span></span>
|
|
947
|
+
<span id="cb63-13"><a href="#cb63-13" aria-hidden="true" tabindex="-1"></a> (cli/format-opts {<span class="at">:spec</span> (<span class="kw">select-keys</span> spec [option])})))</span>
|
|
948
|
+
<span id="cb63-14"><a href="#cb63-14" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> msg))</span>
|
|
949
|
+
<span id="cb63-15"><a href="#cb63-15" aria-hidden="true" tabindex="-1"></a> (<span class="kw">throw</span> (ex-info msg data)))</span>
|
|
950
|
+
<span id="cb63-16"><a href="#cb63-16" aria-hidden="true" tabindex="-1"></a> (System/exit <span class="dv">1</span>))})</span></code></pre></div>
|
|
951
|
+
<p>would print:</p>
|
|
952
|
+
<pre><code>Missing required argument:
|
|
953
|
+
--foo <val> You know what this is.</code></pre>
|
|
954
|
+
<p>You can also choose collect and then report all detected errors (see
|
|
955
|
+
<code>babashka.cli-test/error-fn-test</code> for an example of
|
|
956
|
+
this).</p>
|
|
957
|
+
<h2 id="adding-default-args">Adding default args</h2>
|
|
958
|
+
<p>You can supply default args with <code>:exec-args</code>:</p>
|
|
959
|
+
<div class="sourceCode" id="cb65"><pre
|
|
960
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--foo"</span> <span class="st">"0"</span>] {<span class="at">:exec-args</span> {<span class="at">:bar</span> <span class="dv">1</span>}})</span>
|
|
961
|
+
<span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:foo 0, :bar 1}</span></span></code></pre></div>
|
|
962
|
+
<p>Note that args specified in <code>args</code> will override defaults
|
|
963
|
+
in <code>:exec-args</code>:</p>
|
|
964
|
+
<div class="sourceCode" id="cb66"><pre
|
|
965
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">"--foo"</span> <span class="st">"0"</span> <span class="st">"--bar"</span> <span class="st">"42"</span>] {<span class="at">:exec-args</span> {<span class="at">:bar</span> <span class="dv">1</span>}})</span>
|
|
966
|
+
<span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=> {:foo 0, :bar 42}</span></span></code></pre></div>
|
|
967
|
+
<h2 id="printing-options">Printing options</h2>
|
|
968
|
+
<p>Given a <a href="#spec">spec</a> (like the
|
|
969
|
+
<code>from</code>/<code>to</code>/<code>paths</code>/<code>pretty</code>
|
|
970
|
+
one above), print its options with <code>format-opts</code>:</p>
|
|
971
|
+
<div class="sourceCode" id="cb67"><pre
|
|
972
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb67-1"><a href="#cb67-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">println</span> (cli/format-opts {<span class="at">:spec</span> spec <span class="at">:order</span> [<span class="at">:from</span> <span class="at">:to</span> <span class="at">:paths</span> <span class="at">:pretty</span>]}))</span></code></pre></div>
|
|
973
|
+
<p>This will print:</p>
|
|
974
|
+
<pre><code> -i, --from <format> The input format. <format> can be edn, json or transit. (default: edn)
|
|
975
|
+
-o, --to <format> The output format. <format> can be edn, json or transit. (default: json)
|
|
976
|
+
--paths Paths of files to transform. (default: src test)
|
|
977
|
+
-p, --pretty Pretty-print output.</code></pre>
|
|
978
|
+
<p>As options can often be re-used in multiple subcommands, you can
|
|
979
|
+
determine the order <em>and</em> selection of printed options with
|
|
980
|
+
<code>:order</code>. If you don’t want to use <code>:order</code> and
|
|
981
|
+
simply want to present the options as written, you can also use a vector
|
|
982
|
+
of vectors for the spec:</p>
|
|
983
|
+
<div class="sourceCode" id="cb69"><pre
|
|
984
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb69-1"><a href="#cb69-1" aria-hidden="true" tabindex="-1"></a>[[<span class="at">:pretty</span> {<span class="at">:desc</span> <span class="st">"Pretty-print output."</span></span>
|
|
985
|
+
<span id="cb69-2"><a href="#cb69-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:p</span>}]</span>
|
|
986
|
+
<span id="cb69-3"><a href="#cb69-3" aria-hidden="true" tabindex="-1"></a> [<span class="at">:paths</span> {<span class="at">:desc</span> <span class="st">"Paths of files to transform."</span></span>
|
|
987
|
+
<span id="cb69-4"><a href="#cb69-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> []</span>
|
|
988
|
+
<span id="cb69-5"><a href="#cb69-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> [<span class="st">"src"</span> <span class="st">"test"</span>]</span>
|
|
989
|
+
<span id="cb69-6"><a href="#cb69-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">"src test"</span>}]]</span></code></pre></div>
|
|
990
|
+
<p>If you need more flexibility, you can also use
|
|
991
|
+
<code>opts->table</code>, which turns a spec into a vector of
|
|
992
|
+
vectors, representing rows of a table. You can then
|
|
993
|
+
use<code>format-table</code> to produce a table as returned by
|
|
994
|
+
<code>format-opts</code>. For example to add a header row with labels
|
|
995
|
+
for each column, you could do something like:</p>
|
|
996
|
+
<div class="sourceCode" id="cb70"><pre
|
|
997
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb70-1"><a href="#cb70-1" aria-hidden="true" tabindex="-1"></a>(cli/format-table</span>
|
|
998
|
+
<span id="cb70-2"><a href="#cb70-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:rows</span> (<span class="kw">concat</span> [[<span class="st">"alias"</span> <span class="st">"option"</span> <span class="st">"ref"</span> <span class="st">"default"</span> <span class="st">"description"</span>]]</span>
|
|
999
|
+
<span id="cb70-3"><a href="#cb70-3" aria-hidden="true" tabindex="-1"></a> (cli/opts->table</span>
|
|
1000
|
+
<span id="cb70-4"><a href="#cb70-4" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:alias</span> <span class="at">:f</span>, <span class="at">:default</span> <span class="st">"yupyupyupyup"</span>, <span class="at">:ref</span> <span class="st">"<foo>"</span></span>
|
|
1001
|
+
<span id="cb70-5"><a href="#cb70-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"Thingy"</span>}</span>
|
|
1002
|
+
<span id="cb70-6"><a href="#cb70-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:bar</span> {<span class="at">:alias</span> <span class="at">:b</span>, <span class="at">:default</span> <span class="st">"sure"</span>, <span class="at">:ref</span> <span class="st">"<bar>"</span></span>
|
|
1003
|
+
<span id="cb70-7"><a href="#cb70-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">"Barbarbar"</span> <span class="at">:default-desc</span> <span class="st">"Mos def"</span>}}}))</span>
|
|
1004
|
+
<span id="cb70-8"><a href="#cb70-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:indent</span> <span class="dv">2</span>})</span></code></pre></div>
|
|
1005
|
+
<h3 id="terminal-width">Terminal width</h3>
|
|
1006
|
+
<p><code>format-opts</code> and <code>format-table</code> wrap long
|
|
1007
|
+
descriptions to the terminal width, aligning continuation lines under
|
|
1008
|
+
the description column:</p>
|
|
1009
|
+
<pre><code> --copy-resources <resource> Copy non cljs/cljc files from --paths as
|
|
1010
|
+
resources; a keyword matches by extension,
|
|
1011
|
+
otherwise by regex</code></pre>
|
|
1012
|
+
<p>On by default; <code>:wrap false</code> disables it.</p>
|
|
1013
|
+
<p>The width comes from <code>:max-width-fn</code>, a
|
|
1014
|
+
<code>(fn [cfg] -> width)</code> defaulting to
|
|
1015
|
+
<code>cli/default-width-fn</code>: on node it reads
|
|
1016
|
+
<code>process.stdout.columns</code>; on the JVM it reads
|
|
1017
|
+
<code>$COLUMNS</code> then probes JLine (when on the classpath). Falls
|
|
1018
|
+
back to 80. Override per call:</p>
|
|
1019
|
+
<div class="sourceCode" id="cb72"><pre
|
|
1020
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb72-1"><a href="#cb72-1" aria-hidden="true" tabindex="-1"></a>(cli/format-opts {<span class="at">:spec</span> spec <span class="at">:max-width-fn</span> (<span class="kw">constantly</span> <span class="dv">80</span>)})</span></code></pre></div>
|
|
1021
|
+
<p>On the JVM, <code>default-width-fn</code> reads the real width via
|
|
1022
|
+
JLine when it is on the classpath. babashka bundles it, so bb scripts
|
|
1023
|
+
get it for free; without JLine the width falls back to
|
|
1024
|
+
<code>$COLUMNS</code>/80. If you want real-width detection on another
|
|
1025
|
+
JVM, you can add a JLine provider (FFM is the lightest):</p>
|
|
1026
|
+
<div class="sourceCode" id="cb73"><pre
|
|
1027
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb73-1"><a href="#cb73-1" aria-hidden="true" tabindex="-1"></a><span class="co">;; deps.edn</span></span>
|
|
1028
|
+
<span id="cb73-2"><a href="#cb73-2" aria-hidden="true" tabindex="-1"></a>org.jline/jline-terminal {<span class="at">:mvn/version</span> <span class="st">"3.30.4"</span>}</span>
|
|
1029
|
+
<span id="cb73-3"><a href="#cb73-3" aria-hidden="true" tabindex="-1"></a>org.jline/jline-terminal-ffm {<span class="at">:mvn/version</span> <span class="st">"3.30.4"</span>}</span></code></pre></div>
|
|
1030
|
+
<h2 id="babashka-tasks">Babashka tasks</h2>
|
|
1031
|
+
<p>For documentation on babashka tasks, go <a
|
|
1032
|
+
href="https://book.babashka.org/#tasks">here</a>.</p>
|
|
1033
|
+
<p>Since babashka <code>0.9.160</code>, <code>babashka.cli</code> has
|
|
1034
|
+
become a built-in and has better integration through <code>-x</code> and
|
|
1035
|
+
<code>exec</code>. Read about that in the <a
|
|
1036
|
+
href="https://book.babashka.org/#cli">babashka book</a>.</p>
|
|
1037
|
+
<h2 id="clojure-cli">Clojure CLI</h2>
|
|
1038
|
+
<p>You can control parsing behavior by adding
|
|
1039
|
+
<code>:org.babashka/cli</code> metadata to Clojure functions. It does
|
|
1040
|
+
not introduce a dependency on <code>babashka.cli</code> itself. Not
|
|
1041
|
+
adding any metadata will result in string values, which in many cases
|
|
1042
|
+
may already be a reasonable default.</p>
|
|
1043
|
+
<p>Adding support for this library will cause less friction with shell
|
|
1044
|
+
usage, especially on Windows since you need less quoting. You can
|
|
1045
|
+
support the same function for both <code>clojure -X</code> and
|
|
1046
|
+
<code>clojure -M</code> style invocations without writing extra
|
|
1047
|
+
boilerplate.</p>
|
|
1048
|
+
<p>In your <code>deps.edn</code> <code>:aliases</code> entry, add:</p>
|
|
1049
|
+
<div class="sourceCode" id="cb74"><pre
|
|
1050
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb74-1"><a href="#cb74-1" aria-hidden="true" tabindex="-1"></a><span class="at">:exec</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}}</span>
|
|
1051
|
+
<span id="cb74-2"><a href="#cb74-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span>]}</span></code></pre></div>
|
|
1052
|
+
<p>Now you can call any function that accepts a map argument. E.g.:</p>
|
|
1053
|
+
<div class="sourceCode" id="cb75"><pre
|
|
1054
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb75-1"><a href="#cb75-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:exec</span> clojure.core <span class="kw">prn</span> <span class="at">:a</span> <span class="dv">1</span> <span class="at">:b</span> <span class="dv">2</span></span>
|
|
1055
|
+
<span id="cb75-2"><a href="#cb75-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:a</span> <span class="st">"1"</span>, <span class="at">:b</span> <span class="st">"2"</span>}</span></code></pre></div>
|
|
1056
|
+
<p>Use <code>:org.babashka/cli</code> metadata for coercions:</p>
|
|
1057
|
+
<div class="sourceCode" id="cb76"><pre
|
|
1058
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb76-1"><a href="#cb76-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">ns</span> my-ns)</span>
|
|
1059
|
+
<span id="cb76-2"><a href="#cb76-2" aria-hidden="true" tabindex="-1"></a></span>
|
|
1060
|
+
<span id="cb76-3"><a href="#cb76-3" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> foo</span></span>
|
|
1061
|
+
<span id="cb76-4"><a href="#cb76-4" aria-hidden="true" tabindex="-1"></a> {<span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:a</span> <span class="at">:symbol</span></span>
|
|
1062
|
+
<span id="cb76-5"><a href="#cb76-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:b</span> <span class="at">:long</span>}}}</span>
|
|
1063
|
+
<span id="cb76-6"><a href="#cb76-6" aria-hidden="true" tabindex="-1"></a> <span class="co">;; map argument:</span></span>
|
|
1064
|
+
<span id="cb76-7"><a href="#cb76-7" aria-hidden="true" tabindex="-1"></a> [m]</span>
|
|
1065
|
+
<span id="cb76-8"><a href="#cb76-8" aria-hidden="true" tabindex="-1"></a> <span class="co">;; print map argument:</span></span>
|
|
1066
|
+
<span id="cb76-9"><a href="#cb76-9" aria-hidden="true" tabindex="-1"></a> (<span class="kw">prn</span> m))</span></code></pre></div>
|
|
1067
|
+
<div class="sourceCode" id="cb77"><pre
|
|
1068
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb77-1"><a href="#cb77-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:exec</span> my-ns foo <span class="at">:a</span> foo/bar <span class="at">:b</span> <span class="dv">2</span> <span class="at">:c</span> vanilla</span>
|
|
1069
|
+
<span id="cb77-2"><a href="#cb77-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:a</span> foo/bar, <span class="at">:b</span> <span class="dv">2</span>, <span class="at">:c</span> <span class="st">"vanilla"</span>}</span></code></pre></div>
|
|
1070
|
+
<p>Note that any library can add support for babashka CLI without
|
|
1071
|
+
depending on babashka CLI.</p>
|
|
1072
|
+
<p>An example that specializes <code>babashka.cli</code> usage to a
|
|
1073
|
+
function:</p>
|
|
1074
|
+
<div class="sourceCode" id="cb78"><pre
|
|
1075
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb78-1"><a href="#cb78-1" aria-hidden="true" tabindex="-1"></a><span class="at">:prn</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}}</span>
|
|
1076
|
+
<span id="cb78-2"><a href="#cb78-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span> <span class="st">"clojure.core"</span> <span class="st">"prn"</span>]}</span></code></pre></div>
|
|
1077
|
+
<div class="sourceCode" id="cb79"><pre
|
|
1078
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb79-1"><a href="#cb79-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:prn</span> --foo=bar --baz</span>
|
|
1079
|
+
<span id="cb79-2"><a href="#cb79-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:foo</span> <span class="st">"bar"</span> <span class="at">:baz</span> <span class="va">true</span>}</span></code></pre></div>
|
|
1080
|
+
<p>You can also pre-define the exec function in
|
|
1081
|
+
<code>:exec-fn</code>:</p>
|
|
1082
|
+
<div class="sourceCode" id="cb80"><pre
|
|
1083
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb80-1"><a href="#cb80-1" aria-hidden="true" tabindex="-1"></a><span class="at">:prn</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}}</span>
|
|
1084
|
+
<span id="cb80-2"><a href="#cb80-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> clojure.core/prn</span>
|
|
1085
|
+
<span id="cb80-3"><a href="#cb80-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span>]}</span></code></pre></div>
|
|
1086
|
+
<p>To alter the parsing behavior of functions you don’t control, you can
|
|
1087
|
+
add <code>:org.babashka/cli</code> data in the <code>deps.edn</code>
|
|
1088
|
+
alias:</p>
|
|
1089
|
+
<div class="sourceCode" id="cb81"><pre
|
|
1090
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb81-1"><a href="#cb81-1" aria-hidden="true" tabindex="-1"></a><span class="at">:prn</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}}</span>
|
|
1091
|
+
<span id="cb81-2"><a href="#cb81-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> clojure.core/prn</span>
|
|
1092
|
+
<span id="cb81-3"><a href="#cb81-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span>]</span>
|
|
1093
|
+
<span id="cb81-4"><a href="#cb81-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:foo</span> <span class="at">:long</span>}}}</span></code></pre></div>
|
|
1094
|
+
<div class="sourceCode" id="cb82"><pre
|
|
1095
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb82-1"><a href="#cb82-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:prn</span> --foo=<span class="dv">1</span></span>
|
|
1096
|
+
<span id="cb82-2"><a href="#cb82-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:foo</span> <span class="dv">1</span>}</span></code></pre></div>
|
|
1097
|
+
<h3 id="antq"><a href="https://github.com/liquidz/antq">antq</a></h3>
|
|
1098
|
+
<p><code>.clojure/deps.edn</code> alias:</p>
|
|
1099
|
+
<div class="sourceCode" id="cb83"><pre
|
|
1100
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb83-1"><a href="#cb83-1" aria-hidden="true" tabindex="-1"></a><span class="at">:antq</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}</span>
|
|
1101
|
+
<span id="cb83-2"><a href="#cb83-2" aria-hidden="true" tabindex="-1"></a> com.github.liquidz/antq {<span class="at">:mvn/version</span> <span class="st">"1.7.798"</span>}}</span>
|
|
1102
|
+
<span id="cb83-3"><a href="#cb83-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:paths</span> []</span>
|
|
1103
|
+
<span id="cb83-4"><a href="#cb83-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span> <span class="st">"antq.tool"</span> <span class="st">"outdated"</span>]</span>
|
|
1104
|
+
<span id="cb83-5"><a href="#cb83-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:skip</span> []}}}</span></code></pre></div>
|
|
1105
|
+
<p>On the command line you can now run it with:</p>
|
|
1106
|
+
<div class="sourceCode" id="cb84"><pre
|
|
1107
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb84-1"><a href="#cb84-1" aria-hidden="true" tabindex="-1"></a>$ clj -M<span class="at">:antq</span> --upgrade</span></code></pre></div>
|
|
1108
|
+
<p>Note that we are calling the same <code>outdated</code> function that
|
|
1109
|
+
you normally call with <code>-T</code>:</p>
|
|
1110
|
+
<div class="sourceCode" id="cb85"><pre
|
|
1111
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb85-1"><a href="#cb85-1" aria-hidden="true" tabindex="-1"></a>$ clj -Tantq outdated <span class="at">:upgrade</span> <span class="va">true</span></span></code></pre></div>
|
|
1112
|
+
<p>even though antq has its own <code>-main</code> function.</p>
|
|
1113
|
+
<p>Note that we added the
|
|
1114
|
+
<code>:org.babashka/cli {:coerce {:skip []}}</code> data in the alias to
|
|
1115
|
+
make sure that <code>--skip</code> options get collected into a
|
|
1116
|
+
vector:</p>
|
|
1117
|
+
<div class="sourceCode" id="cb86"><pre
|
|
1118
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb86-1"><a href="#cb86-1" aria-hidden="true" tabindex="-1"></a>clj -M<span class="at">:antq</span> --upgrade --skip github-action</span></code></pre></div>
|
|
1119
|
+
<p>vs.</p>
|
|
1120
|
+
<div class="sourceCode" id="cb87"><pre
|
|
1121
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb87-1"><a href="#cb87-1" aria-hidden="true" tabindex="-1"></a>clj -Tantq outdated <span class="at">:upgrade</span> <span class="va">true</span> <span class="at">:skip</span> '[<span class="st">"github-action"</span>]'</span></code></pre></div>
|
|
1122
|
+
<p>The following projects have added support for babashka CLI. Feel free
|
|
1123
|
+
to add a PR to list your project as well!</p>
|
|
1124
|
+
<h3 id="clj-new"><a
|
|
1125
|
+
href="https://github.com/seancorfield/clj-new#babashka-cli">clj-new</a></h3>
|
|
1126
|
+
<h3 id="codox"><a
|
|
1127
|
+
href="https://github.com/weavejester/codox">codox</a></h3>
|
|
1128
|
+
<p>In <code>deps.edn</code> create an alias:</p>
|
|
1129
|
+
<div class="sourceCode" id="cb88"><pre
|
|
1130
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb88-1"><a href="#cb88-1" aria-hidden="true" tabindex="-1"></a><span class="at">:codox</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}</span>
|
|
1131
|
+
<span id="cb88-2"><a href="#cb88-2" aria-hidden="true" tabindex="-1"></a> codox/codox {<span class="at">:mvn/version</span> <span class="st">"0.10.8"</span>}}</span>
|
|
1132
|
+
<span id="cb88-3"><a href="#cb88-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> codox.main/generate-docs</span>
|
|
1133
|
+
<span id="cb88-4"><a href="#cb88-4" aria-hidden="true" tabindex="-1"></a> <span class="co">;; default arguments:</span></span>
|
|
1134
|
+
<span id="cb88-5"><a href="#cb88-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-args</span> {<span class="at">:source-paths</span> [<span class="st">"src"</span>]}</span>
|
|
1135
|
+
<span id="cb88-6"><a href="#cb88-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:source-paths</span> []</span>
|
|
1136
|
+
<span id="cb88-7"><a href="#cb88-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:doc-paths</span> []</span>
|
|
1137
|
+
<span id="cb88-8"><a href="#cb88-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:themes</span> [<span class="at">:keyword</span>]}}</span>
|
|
1138
|
+
<span id="cb88-9"><a href="#cb88-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span>]}</span></code></pre></div>
|
|
1139
|
+
<p>CLI invocation:</p>
|
|
1140
|
+
<div class="sourceCode" id="cb89"><pre
|
|
1141
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb89-1"><a href="#cb89-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:codox</span> --output-path /tmp/out</span></code></pre></div>
|
|
1142
|
+
<h3 id="deps-new"><a
|
|
1143
|
+
href="https://github.com/seancorfield/deps-new#babashka-cli">deps-new</a></h3>
|
|
1144
|
+
<h3 id="kaocha"><a
|
|
1145
|
+
href="https://github.com/lambdaisland/kaocha">kaocha</a></h3>
|
|
1146
|
+
<p>In <code>deps.edn</code> create an alias:</p>
|
|
1147
|
+
<div class="sourceCode" id="cb90"><pre
|
|
1148
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb90-1"><a href="#cb90-1" aria-hidden="true" tabindex="-1"></a><span class="at">:kaocha</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}</span>
|
|
1149
|
+
<span id="cb90-2"><a href="#cb90-2" aria-hidden="true" tabindex="-1"></a> lambdaisland/kaocha {<span class="at">:mvn/version</span> <span class="st">"1.66.1034"</span>}}</span>
|
|
1150
|
+
<span id="cb90-3"><a href="#cb90-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> kaocha.runner/exec-fn</span>
|
|
1151
|
+
<span id="cb90-4"><a href="#cb90-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-args</span> {} <span class="co">;; insert default arguments here</span></span>
|
|
1152
|
+
<span id="cb90-5"><a href="#cb90-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:alias</span> {<span class="at">:watch</span> <span class="at">:watch</span>?</span>
|
|
1153
|
+
<span id="cb90-6"><a href="#cb90-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:fail-fast</span> <span class="at">:fail-fast</span>?}</span>
|
|
1154
|
+
<span id="cb90-7"><a href="#cb90-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> {<span class="at">:skip-meta</span> <span class="at">:keyword</span></span>
|
|
1155
|
+
<span id="cb90-8"><a href="#cb90-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:kaocha/reporter</span> [<span class="at">:symbol</span>]}}</span>
|
|
1156
|
+
<span id="cb90-9"><a href="#cb90-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span>]}</span></code></pre></div>
|
|
1157
|
+
<p>Now you are able to use kaocha’s exec-fn to be used as a CLI:</p>
|
|
1158
|
+
<div class="sourceCode" id="cb91"><pre
|
|
1159
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb91-1"><a href="#cb91-1" aria-hidden="true" tabindex="-1"></a>$ clj -M<span class="at">:kaocha</span> --watch --fail-fast --kaocha/reporter kaocha.report/documentation</span></code></pre></div>
|
|
1160
|
+
<h3 id="quickdoc"><a
|
|
1161
|
+
href="https://github.com/borkdude/quickdoc#clojure-cli">quickdoc</a></h3>
|
|
1162
|
+
<h3 id="tools.build"><a
|
|
1163
|
+
href="https://github.com/clojure/tools.build">tools.build</a></h3>
|
|
1164
|
+
<p>In <code>deps.edn</code> create an alias:</p>
|
|
1165
|
+
<div class="sourceCode" id="cb92"><pre
|
|
1166
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb92-1"><a href="#cb92-1" aria-hidden="true" tabindex="-1"></a><span class="at">:build</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}</span>
|
|
1167
|
+
<span id="cb92-2"><a href="#cb92-2" aria-hidden="true" tabindex="-1"></a> io.github.clojure/tools.build {<span class="at">:git/tag</span> <span class="st">"v0.8.2"</span> <span class="at">:git/sha</span> <span class="st">"ba1a2bf"</span>}}</span>
|
|
1168
|
+
<span id="cb92-3"><a href="#cb92-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:paths</span> [<span class="st">"."</span>]</span>
|
|
1169
|
+
<span id="cb92-4"><a href="#cb92-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:ns-default</span> build</span>
|
|
1170
|
+
<span id="cb92-5"><a href="#cb92-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span>]}</span></code></pre></div>
|
|
1171
|
+
<p>Now you can call your build functions as CLIs:</p>
|
|
1172
|
+
<div class="sourceCode" id="cb93"><pre
|
|
1173
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb93-1"><a href="#cb93-1" aria-hidden="true" tabindex="-1"></a>clj -M<span class="at">:build</span> jar --verbose</span></code></pre></div>
|
|
1174
|
+
<h3 id="tools.deps.graph"><a
|
|
1175
|
+
href="https://github.com/clojure/tools.deps.graph">tools.deps.graph</a></h3>
|
|
1176
|
+
<p>In <code>deps.edn</code> create an alias:</p>
|
|
1177
|
+
<div class="sourceCode" id="cb94"><pre
|
|
1178
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb94-1"><a href="#cb94-1" aria-hidden="true" tabindex="-1"></a><span class="at">:graph</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">"<latest-version>"</span>}</span>
|
|
1179
|
+
<span id="cb94-2"><a href="#cb94-2" aria-hidden="true" tabindex="-1"></a> org.clojure/tools.deps.graph {<span class="at">:mvn/version</span> <span class="st">"1.1.68"</span>}}</span>
|
|
1180
|
+
<span id="cb94-3"><a href="#cb94-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> clojure.tools.deps.graph/graph</span>
|
|
1181
|
+
<span id="cb94-4"><a href="#cb94-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-args</span> {} <span class="co">;; insert default arguments here</span></span>
|
|
1182
|
+
<span id="cb94-5"><a href="#cb94-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:trace-omit</span> [<span class="at">:symbol</span>]}}</span>
|
|
1183
|
+
<span id="cb94-6"><a href="#cb94-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span>]}</span></code></pre></div>
|
|
1184
|
+
<p>Then invoke on the command line:</p>
|
|
1185
|
+
<div class="sourceCode" id="cb95"><pre
|
|
1186
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb95-1"><a href="#cb95-1" aria-hidden="true" tabindex="-1"></a>clj -M<span class="at">:graph</span> --size --output graph.png</span></code></pre></div>
|
|
1187
|
+
<h2 id="leiningen">Leiningen</h2>
|
|
1188
|
+
<p>This tool can be used to run clojure exec functions with <a
|
|
1189
|
+
href="https://leiningen.org/">lein</a>.</p>
|
|
1190
|
+
<p>An example with <code>clj-new</code>:</p>
|
|
1191
|
+
<p>In <code>~/.lein/profiles.clj</code> put:</p>
|
|
1192
|
+
<div class="sourceCode" id="cb96"><pre
|
|
1193
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb96-1"><a href="#cb96-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:clj-1.11</span> {<span class="at">:dependencies</span> [[org.clojure/clojure <span class="st">"1.11.1"</span>]]}</span>
|
|
1194
|
+
<span id="cb96-2"><a href="#cb96-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:clj-new</span> {<span class="at">:dependencies</span> [[org.babashka/cli <span class="st">"<latest-version>"</span>]</span>
|
|
1195
|
+
<span id="cb96-3"><a href="#cb96-3" aria-hidden="true" tabindex="-1"></a> [com.github.seancorfield/clj-new <span class="st">"1.2.381"</span>]]}</span>
|
|
1196
|
+
<span id="cb96-4"><a href="#cb96-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:user</span> {<span class="at">:aliases</span> {<span class="st">"clj-new"</span> [<span class="st">"with-profiles"</span> <span class="st">"+clj-1.11,+clj-new"</span></span>
|
|
1197
|
+
<span id="cb96-5"><a href="#cb96-5" aria-hidden="true" tabindex="-1"></a> <span class="st">"run"</span> <span class="st">"-m"</span> <span class="st">"babashka.cli.exec"</span></span>
|
|
1198
|
+
<span id="cb96-6"><a href="#cb96-6" aria-hidden="true" tabindex="-1"></a> {<span class="at">:exec-args</span> {<span class="at">:env</span> {<span class="at">:description</span> <span class="st">"My project"</span>}}</span>
|
|
1199
|
+
<span id="cb96-7"><a href="#cb96-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> {<span class="at">:verbose</span> <span class="at">:long</span></span>
|
|
1200
|
+
<span id="cb96-8"><a href="#cb96-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:args</span> []}</span>
|
|
1201
|
+
<span id="cb96-9"><a href="#cb96-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> {<span class="at">:f</span> <span class="at">:force</span>}}</span>
|
|
1202
|
+
<span id="cb96-10"><a href="#cb96-10" aria-hidden="true" tabindex="-1"></a> <span class="st">"clj-new"</span>]}}}</span></code></pre></div>
|
|
1203
|
+
<p>After that you can use <code>lein clj-new app</code> to create a new
|
|
1204
|
+
app:</p>
|
|
1205
|
+
<div class="sourceCode" id="cb97"><pre
|
|
1206
|
+
class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb97-1"><a href="#cb97-1" aria-hidden="true" tabindex="-1"></a>$ lein clj-new app --name foobar/baz --verbose <span class="dv">3</span> -f</span></code></pre></div>
|
|
1207
|
+
<!-- ## Future ideas -->
|
|
1208
|
+
<!-- ### Command line syntax for `:coerce` and `:collect` -->
|
|
1209
|
+
<!-- Perhaps this library can consider a command line syntax for `:coerce` and -->
|
|
1210
|
+
<!-- `:collect`, e.g.: -->
|
|
1211
|
+
<!-- ``` clojure -->
|
|
1212
|
+
<!-- $ clj -M:example --skip.0=github-actions --skip.1=clojure-cli -->
|
|
1213
|
+
<!-- ``` -->
|
|
1214
|
+
<!-- ``` clojure -->
|
|
1215
|
+
<!-- $ clj -M:example --lib%sym=org.babashka/cli -->
|
|
1216
|
+
<!-- ``` -->
|
|
1217
|
+
<!-- Things to look out for here is if the delimiter works well with bash / zsh / -->
|
|
1218
|
+
<!-- cmd.exe and Powershell. -->
|
|
1219
|
+
<!-- ### Merge args from a file -->
|
|
1220
|
+
<!-- Merge default arguments from a file so you don't have to write them on the command line: -->
|
|
1221
|
+
<!-- ``` clojure -->
|
|
1222
|
+
<!-- --org.babashka/cli-defaults=foo.edn -->
|
|
1223
|
+
<!-- ``` -->
|
|
1224
|
+
<h2 id="license">License</h2>
|
|
1225
|
+
<p>Copyright © 2022-2026 Michiel Borkent</p>
|
|
1226
|
+
<p>Distributed under the MIT License. See LICENSE.</p>
|
|
1227
|
+
|
|
1228
|
+
</body>
|
|
1229
|
+
</html>
|