Skip to content

Commit

Permalink
Merge pull request #18 from hlship/hls/pretty-3.2.0
Browse files Browse the repository at this point in the history
Update dependencies, improve documentation
  • Loading branch information
hlship authored Sep 21, 2024
2 parents 6a569c4 + 8f97997 commit 515120a
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 101 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/clojure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ jobs:

steps:
- name: Checkout
uses: actions/[email protected].6
uses: actions/[email protected].7

- name: Setup Java
uses: actions/setup-java@v4.2.1
uses: actions/setup-java@v4.3.0
with:
java-version: '11'
distribution: 'corretto'

- name: Install clojure tools
uses: DeLaGuardo/[email protected]
with:
cli: 1.11.3.1463
cli: 1.12.0.1479

- name: Cache clojure dependencies
uses: actions/cache@v4
Expand Down
84 changes: 68 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ a built-in `help` command to list out what commands are available.
for the kind of low-ceremony tools that `cli-tools` is intended for.

Although `cli-tools` can be used to build shared tools, it is also fully intended for developers to create a personal
toolkit of commands specific to their personal workflows, as an alternative to a collection of shell aliases and one-off shell scripts.
toolkit of commands specific to their individual workflows, as an alternative to a collection of shell aliases and one-off shell scripts.

Below is an example of the author's personal toolkit, `flow`:

![Example](images/example-usage.png)

Building on `cli-tools` provides discoverability and feedback, as the tool and every command inside the tool, will provide detailed help.
Building on `cli-tools` provides discoverability and feedback, as the tool (and every command inside the tool) will provide detailed help.

A more complete example is [dialog-tool](https://github.com/hlship/dialog-tool).

## defcommand

Expand Down Expand Up @@ -128,6 +130,10 @@ Babashka looks for the `bb.edn` file in the same directory as the script, and us
The final step is to add that `bin` directory to the shell `$PATH` environment variable; this is done in your
`~/.zshrc` file, or equivalent.

Alternately, if you already have a location for commands, such as `~/bin`, you can create a symbolic link there
that points to your `bin/app-admin` script; Babashka will follow links and find the neighboring `bb.edn` file
at the final location of the script. Handy!

With all that in place, we can now run `app-admin configure` through its paces:

```
Expand Down Expand Up @@ -180,7 +186,8 @@ Unless there are errors, the body of the command is invoked:
>
```

The body here just prints out the values passed in.
The body here just prints out the values passed in. That's not a bad starting point when creating new scripts.
I like to get all the command line parsing concerns out of the way before working on the _meat_ of the command.

## Abbreviated Commands

Expand All @@ -189,11 +196,10 @@ from the provided name on the command line, it will
find any commands whose name contains the provided name; so `app-admin conf` would work, as would `app-admin c` ...
as long as there aren't multiple matches for the substring.

When there are multiple matches, `dispatch` will abort and the error message will identify which commands matched the provided
string.
When there are multiple matches, `dispatch` will abort and the error message will identify which commands matched the provided string.

Exception: when the provided command name _exactly_ matches a command's name, then that command will be used even if
that command name is itself a prefix or substring of some other command name.
Exception: when the provided name _exactly_ matches a command's name, then that command will be used even if
the provided name is also a prefix or substring of some other command name.

## Positional Arguments

Expand All @@ -219,14 +225,14 @@ by the argument

* `:update-fn` - optional function used to update the (initially nil) entry for the argument in the arguments map

* `:assoc-fn` - optional function used to update the arguments map; passed the map, the id, and the parsed value
* `:assoc-fn` - optional function used to update the arguments map; passed the map, the argument id, and the parsed value

* `:update-fn` and `:assoc-fn` are mutually exclusive.

For repeatable arguments, the default update function will construct a vector of values.
For non-repeatable arguments, the default update function simply sets the value.

Only the final positional parameter may be repeatable.
Only the final positional argument may be repeatable.

Also note that all command line arguments _must be_ consumed, either as options or as positional arguments.
Any additional command line arguments will be reported as a validation error.
Expand Down Expand Up @@ -281,9 +287,9 @@ option-like string that isn't declared.
[verbose ["-v" "--verbose"]
:args
command ["COMMAND" "Remote command to execute"]
args ["ARGS" "Arguments to remote command"
:optional true
:repeatable true]]
remote-args ["ARGS" "Arguments to remote command"
:optional true
:repeatable true]]
...)
```

Expand All @@ -295,7 +301,7 @@ but is clumsy.

Instead, add `:in-order true` to the end of the interface, and any
unrecognized options will be parsed as positional arguments instead,
so `app-admin remote ls -lR` will work, and `-lR` will be provided as a string in the `args`
so `app-admin remote ls -lR` will work, and `-lR` will be provided as a string in the `remote-args`
seq.

### :let \<bindings\>
Expand All @@ -310,11 +316,11 @@ and arguments definitions; the `:let` keyword is followed by a vector of binding
:parse-fn keyword
:validate [allowed-modes (str "Must be one of " mode-names)]]
:let [allowed-modes #{:batch :async :real-time}
mode-names (->> allowed-modes (map name) sort (str/join ", "))]]
mode-names (->> allowed-modes (map name) sort (string/join ", "))]]
...)
```

> Note that the `select-option` function is an easier way to create such
> Note that the `new.lewisship.cli-tools/select-option` function is an easier way to create such
> an option.
In the expanded code, the bindings are moved to the top, before the option and argument
Expand Down Expand Up @@ -388,7 +394,7 @@ A category can also have a `:command-group` metadata value, a short string that
All commands in the same namespace/category are accessible via that group command. The built-in `help`
command will identify the command group when listing the commands in the category.

Command groups are useful for the largest tools with the most commands; it allows for shorter command names,
Command groups are useful when creating the largest tools with the most commands; it allows for shorter command names,
as the name only have to be unique within command group, not globally.


Expand Down Expand Up @@ -433,9 +439,55 @@ This may have an even more significant impact for a tool that is built on top of
Our mockup of 1500 commands across 250 namespaces executes approximately
twice as fast using the cache (approximately 8 seconds with the cache, vs. 17 seconds without).

Babashka is amazingly fast for these purposes; the same test executes in 0.23 seconds.

By default, `dispatch` will store its cache in the `~/.cli-tools-cache` directory; the environment variable
`CLI_TOOLS_CACHE_DIR` can override this default.

## Tips and Tricks

### Parsing Numbers

When an input is numeric, you can use Clojure's `parse-long` function to parse a number; it returns nil if
the string is not a number. You can then check using `some?` within :validate:

```
(defcommand kill-port
"Kills the listening process locking a port."
[force ["-f" "--force" "Kill process without asking for confirmation"]
:args
port ["PORT" "Port number to kill"
:parse-fn parse-long
:validate [some? "Not a number"
pos? "Must be at least 1"]]]
...)
```

This handles invalid input gracefully:

```
> flow kill-port abc
Usage: flow kill-port [OPTIONS] PORT
Kills the listening process locking a port.
Options:
-f, --force Kill process without asking for confirmation
-h, --help This command summary
Arguments:
PORT: Port number to kill
Error:
PORT: Not a number
```

You might be tempted to use `#(Long/parseLong %)` as the parse function; this works, but the message produced comes from the exception message, and is not very friendly:

```
Error:
PORT: Error in PORT: For input string: "abc"
```

## Job Board

For tools that run for a while, visual feedback can be provided to the user using the job board
Expand Down
6 changes: 3 additions & 3 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{:paths ["src"]
;; Needed to build and test w/ clojure
:deps {org.clojure/clojure {:mvn/version "1.11.3"}
:deps {org.clojure/clojure {:mvn/version "1.12.0"}
org.clojure/tools.cli {:mvn/version "1.1.230"}
babashka/process {:mvn/version "0.5.22"}
org.clj-commons/pretty {:mvn/version "2.6.0"}
org.clj-commons/pretty {:mvn/version "3.2.0"}
org.clj-commons/humanize {:mvn/version "1.0"}}

:net.lewisship.build/scm
Expand All @@ -17,7 +17,7 @@
{:extra-paths ["test" "test-resources"]
:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1"
:git/sha "dfb30dd"}
babashka/babashka {:mvn/version "1.3.190"}}
babashka/babashka {:mvn/version "1.4.192"}}
:exec-fn cognitect.test-runner.api/test
:jvm-opts ["-Dclj-commons.ansi.enabled=true"]
:exec-args
Expand Down
12 changes: 6 additions & 6 deletions test-resources/excess-values.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Usage: harness collect [OPTIONS] KEY VAL
Usage: harness collect [OPTIONS] KEY VAL
Collect key and value.

Options:
-h, --help This command summary
-h, --help This command summary

Arguments:
KEY: Key to set
VAL: Value to set
KEY: Key to set
VAL: Value to set

Error:
Unexpected argument 'the-extra'
Error:
Unexpected argument 'the-extra'
10 changes: 5 additions & 5 deletions test-resources/help.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Usage: harness configure [OPTIONS] HOST KV-DATA+
Usage: harness configure [OPTIONS] HOST KV-DATA+
Configures the system for some thing.

This is more detail.
Expand All @@ -8,9 +8,9 @@ This is more detail.
This is not indented.

Options:
-v, --verbose Enable verbose logging
-h, --help This command summary
-v, --verbose Enable verbose logging
-h, --help This command summary

Arguments:
HOST: System configuration URL
KV-DATA: Data to configure as KEY=VALUE
HOST: System configuration URL
KV-DATA: Data to configure as KEY=VALUE
12 changes: 6 additions & 6 deletions test-resources/insufficient-values.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Usage: harness collect [OPTIONS] KEY VAL
Usage: harness collect [OPTIONS] KEY VAL
Collect key and value.

Options:
-h, --help This command summary
-h, --help This command summary

Arguments:
KEY: Key to set
VAL: Value to set
KEY: Key to set
VAL: Value to set

Error:
No value for required argument VAL
Error:
No value for required argument VAL
10 changes: 5 additions & 5 deletions test-resources/let-directive.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Usage: harness set-mode [OPTIONS]
Usage: harness set-mode [OPTIONS]
Sets the execution mode

Options:
-m, --mode MODE Execution mode, one of async, batch, real-time
-h, --help This command summary
-m, --mode MODE Execution mode, one of async, batch, real-time
-h, --help This command summary

Error:
Failed to validate "-m unknown": Must be one of async, batch, real-time
Error:
Failed to validate "-m unknown": Must be one of async, batch, real-time
10 changes: 5 additions & 5 deletions test-resources/option-defaults.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Usage: harness default-variants [OPTIONS]
Usage: harness default-variants [OPTIONS]
Different option defaults.

Options:
 --foo FOO abc Foo option
 --bar BAR <computed> Bar option
 --bazz BAZZ Bazzy Bazz option
-h, --help This command summary
 --foo FOO abc Foo option
 --bar BAR <computed> Bar option
 --bazz BAZZ Bazzy Bazz option
-h, --help This command summary
14 changes: 7 additions & 7 deletions test-resources/pos-arg-validation-failure.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Usage: harness configure [OPTIONS] HOST KV-DATA+
Usage: harness configure [OPTIONS] HOST KV-DATA+
Configures the system for some thing.

This is more detail.
Expand All @@ -8,12 +8,12 @@ This is more detail.
This is not indented.

Options:
-v, --verbose Enable verbose logging
-h, --help This command summary
-v, --verbose Enable verbose logging
-h, --help This command summary

Arguments:
HOST: System configuration URL
KV-DATA: Data to configure as KEY=VALUE
HOST: System configuration URL
KV-DATA: Data to configure as KEY=VALUE

Error:
HOST: must be a URL
Error:
HOST: must be a URL
14 changes: 7 additions & 7 deletions test-resources/tool-help-flat.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
Usage: test-harness [TOOL OPTIONS] COMMAND ...
Usage: test-harness [TOOL OPTIONS] COMMAND ...

Example commands as part of unit test suite.

Even this docstring is part of the test.

Tool options:
 -C, --color Enable ANSI color output
 -N, --no-color Disable ANSI color output
 -h, --help This tool summary
 -C, --color Enable ANSI color output
 -N, --no-color Disable ANSI color output
 -h, --help This tool summary

Commands:
default: Default command summary
explicit: Explicit command summary
help: List available commands
default: Default command summary
explicit: Explicit command summary
help: List available commands
18 changes: 9 additions & 9 deletions test-resources/tool-help-grouped-flat.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
Usage: group-test [TOOL OPTIONS] COMMAND ...
Usage: group-test [TOOL OPTIONS] COMMAND ...

Group example namespace

Tool options:
 -C, --color Enable ANSI color output
 -N, --no-color Disable ANSI color output
 -h, --help This tool summary
 -C, --color Enable ANSI color output
 -N, --no-color Disable ANSI color output
 -h, --help This tool summary

Commands:
default: Default command summary
explicit: Explicit command summary
group echo: Echo a string
group edit: Edit a whatever
help: List available commands
default: Default command summary
explicit: Explicit command summary
group echo: Echo a string
group edit: Edit a whatever
help: List available commands
Loading

0 comments on commit 515120a

Please sign in to comment.