kmdo#
Maintaining documentation can be prone to error and cumbersome. Especially for
demos, tutorials, and usage guides for command-line tools. To aid that, kmdo
runs through a directory and executes command-files, storing stdout and
stderr in corresponding output-files.
This documentation is an example of how this can be used in concert with Sphinx and GitHub Pages.
Installation#
Install kmdo system-wide via the pip using pipx:
pipx install kmdo
Note
Ensure that command-line tools installed using pipx are added to PATH by
running pipx ensurepath.
Usage#
usage: kmdo [-h] [-r] [-s SHELL] [-x PATTERN] [-o LABEL] [-k LABEL]
[-f {yaml,jsonl}] [-n] [-t TIMEOUT]
path
Run commands from .cmd files, storing output in .out files
positional arguments:
path Path to DIR containing .cmd files
options:
-h, --help show this help message and exit
-r, --recursive go deepah!
-s, --shell SHELL Absolute path to the Shell to use
-x, --exclude-name PATTERN
Skip command-files whose name contains PATTERN;
repeatable
-o, --only LABEL Run only command-files carrying LABEL; repeatable,
matches any
-k, --skip LABEL Skip command-files carrying LABEL; repeatable
-f, --output-format {yaml,jsonl}
Output format (default: yaml)
-n, --dry-run List commands without executing them
-t, --timeout TIMEOUT
Timeout in seconds for each command
Error-handling#
kmdo has exit code 0 upon success, that is when all commands succeed,
ignoring command errors from command-files with .uone in the file name. On
error, kmdo has a non zero exit code.
Additionally, kmdo outputs a YAML representation of what it has executed
to stdout. For example, when using kmdo to generate command output for the
documentation you are reading now.
kmdo src/examples
Outputs the following YAML:
args:
path: '/home/odus/git/kmdo/docs/src/examples'
recursive: false
results:
- out_fp: '/home/odus/git/kmdo/docs/src/examples/kmdo.out'
cmd_fp: '/home/odus/git/kmdo/docs/src/examples/kmdo.cmd'
cmd: 'kmdo --help'
rcode: 0
uone: false
err: false
- out_fp: '/home/odus/git/kmdo/docs/src/examples/kmdo.uone.out'
cmd_fp: '/home/odus/git/kmdo/docs/src/examples/kmdo.uone.cmd'
cmd: 'kmdo'
rcode: 2
uone: true
err: false
nerrs: 0
Empty command-file and update-on-error#
When the command-file is empty, then the fname part of the command-file file name is treated as the command to execute.
For example, the empty file named kmdo.uone.cmd, will execute the command
kmdo, and because of .uone in the file name then it create the output file
kmdo.uone.out:
usage: kmdo [-h] [-r] [-s SHELL] [-x PATTERN] [-o LABEL] [-k LABEL]
[-f {yaml,jsonl}] [-n] [-t TIMEOUT]
path
kmdo: error: the following arguments are required: path
Labels#
A documentation tree tends to accumulate command-files that should not all run every time. Some are slow, some need hardware that is not always present, some only make sense on one platform. Labels let you partition them.
A label is a dot-separated segment sitting between the fname and the
.cmd extension. The file xnvme_io.slow.cmd carries the label slow, and
xnvme_io.slow.linux.cmd carries both slow and linux. Labels are yours to
invent; kmdo gives meaning to uone only.
Without --only every command-file runs. Passing --only restricts the run
to the command-files carrying that label:
kmdo docs/ # runs everything
kmdo --only slow docs/ # runs only the 'slow' command-files
kmdo -o slow -o linux docs/ # runs those carrying 'slow' or 'linux'
Repeating --only matches any of them rather than all of them. To go the
other way, --skip drops command-files carrying a label, and is applied after
--only:
kmdo --skip slow docs/ # everything except the slow ones
kmdo -o linux -k slow docs/ # 'linux' ones, minus the slow ones
Since uone is a label like any other, -o uone selects the update-on-error
command-files and -k uone skips them.
Note
Labels and dotted fname parts occupy the same place in the file name, so
kmdo cannot tell them apart. This only matters for empty command-files, where
the file name supplies the command: an empty foo.sh.cmd runs foo with label
sh, not the command foo.sh. kmdo writes a warning to stderr when it
hits that case. Non-empty command-files are unaffected, since a label that
nobody selects on does nothing.
Excluding by name#
Labels are the way to carve a tree into subsets you have named in advance.
When you just need to drop something without labelling anything first,
--exclude-name skips command-files whose name contains the given text:
kmdo --exclude-name scratch docs/ # skips anything with 'scratch' in the name
kmdo -x scratch -x wip docs/ # repeatable, skips names matching either
It matches a substring of the whole file name, labels included, so -x slow
and --skip slow cover much the same ground on labelled files and differ on a
file that merely happens to be called slowpath.cmd. Prefer --skip where the
intent is a subset, and keep --exclude-name for the one-off case it suits.
Note
--exclude is still accepted as a spelling of --exclude-name, so existing
invocations keep working, but it is no longer listed in --help.