Creating CLIs with `sub`
Some time ago, I wanted to add a custom CLI to my `~/.config/box` project, where I automated the configuration of various computers with a boatload of YAML on top of Ansible.
I can’t open source that project because it references clients and personal stuff Michel de Montagne rightfully identified as part of a separate sphere of life, but I can share the technique I used1 to set up this autocompleting, helpful CLI.
➜ box help
Usage: box <command> [<args>]
Some useful box commands are:
attach Attach to a convenient tmux session
clj-kondo-copy-configs Copies configurations provided by all dependencies into the current project
clj-upgrade Upgrade Clojure dependencies
colors Taste the rainbow of Terminal colours
commands List all box commands
creds Dump public credentials for the provided `format`
git-fetch Fetch upstream changes in many Git repositories
gpg-debug Dumps relevant GPG and SSH configuration
gpg-lookup Look up the given email address bypassing any local cache
gpg-tty Tell the GPG agent to update TTY
https Pull down HTTPS certs
imapnotify Enables/disables user's imapnotify processes
launch Fire up a local project, fast
mfa Generate an MFA token for a Yubikey oath
network Print useful network configuration and diagnostics
nixup Upgrade Nix channels, packages and configuration
pacup Update packages via both AUR and Pacman
ping Checks reachability via Ansible defaulting to all hosts
play Runs Ansible with named hosts
play-all Runs Ansible with ALL known hosts
pull Quickly pull my repos
pull-router-config Copy config.boot from router to destination, defaulting to ./config.boot
recompose Reloads ~/.Xcompose with minimal fuss
redoom Sync Doom and restart the Emacs server
repo-list Lists directories hosted on Files.
sound Switches Pulseaudio inputs and outputs
status Report on things we like to change regularly
switch Rebuilds the system via nix-darwin and home-manager
sync-iphone Sync images on USB-connected iPhone to ~/ios
truecolors Taste the rainbow of true colours
upgrade Upgrade Emacs, ports, and kegs
vlc-screen Start VLC with the video from one monitor
wake Sends a wake-on-lan packet to <host>
which-emacs Return the path to the preferred version Emacs
yubisshkey Print the public key associated with Yubikey
zprof Profile startup time of ZSH
See 'box help <command>' for information on a specific command.
The sub
project came out of the 37Signals team
back when they created rbenv
and it makes adding subcommands to a single entry
point a breeze.
One starts by cloning the sub
repo and running the provided prepare.sh
script to set your own name. I wont reproduce all of the documentation in the
project README here, but will provide an example from my own dotfiles of a
self-documented command with nested autocompletion.
#!/usr/bin/env -S zsh --login
# Usage: box mfa <id>
# Summary: Generate an MFA token for a Yubikey oath
declare -A accounts=(
[jcf-root]=arn:aws:iam::000000000000:mfa/jcf
[jcf]=arn:aws:iam::000000000000:mfa/jcf
[root]=arn:aws:iam::000000000000:mfa/root-account-mfa-device
[vouch]=arn:aws:iam::000000000000:mfa/jcf
)
# Provide box completions
if [[ "$1" = "--complete" ]]; then
print -l "${(@k)accounts}"
ykman oath accounts list
exit
fi
usage() {
echo >&2 "box mfa <id>"
echo >&2 ""
echo >&2 "id:"
ykman oath accounts list | sed 's/^/ /' >&2
}
clip() {
if command -v xclip &>/dev/null; then
xclip -selection clipboard -in "$@"
elif command -v pbcopy &>/dev/null; then
pbcopy
fi
}
oath() {
local pattern="$1"
ykman oath accounts code -s "${accounts[$pattern]:-$pattern}" \
| tee /dev/tty \
| tr -d "\n" \
| clip
}
if [[ $# -eq 1 ]]; then
oath "$1"
else
usage
fi
The script above wraps ykman
and both prints and copies my MFA codes with support for both macOS and Linux’s clipboards.
The use of the Usage
comment and the --complete
arguments provide helpful output and autocompletion with very little effort.
Footnotes
-
I’m in the process of retiring the Box project in favour of using Nix for everything I can. ↩