mdrip
rips labeled command blocks from markdown files for execution.
mdrip
accepts one label argument and any number of file name arguments, where the files are assumed to contain markdown. It scans the files for fenced code blocks immediately preceded by an HTML comment with embedded @labels.
If one of the block labels matches the label argument to the command line, the associated block is extracted. Extracted blocks are emitted to stdout
, or, if --subshell
is specified, concatenated to run as a subprocess.
This is a markdown-based instance of language-independent literate programming (for perspective, see the latex-based noweb). It's language independent because shell scripts can make, build and run programs in any programming language, via here documents and what not.
Assuming Go installed:
export MDRIP=~/mdrip GOPATH=$MDRIP/go go get github.com/monopole/mdrip GOPATH=$MDRIP/go go test github.com/monopole/mdrip/util $MDRIP/go/bin/mdrip # Shows usage.
This markdown coding tutorial (raw markdown here) has bash code blocks that write, compile and run a Go program.
Send code from that file to stdout
:
$MDRIP/go/bin/mdrip lesson1 \ $MDRIP/go/src/github.com/monopole/mdrip/example_tutorial.md
Alternatively, run it's code in a subshell:
$MDRIP/go/bin/mdrip --subshell lesson1 \ $MDRIP/go/src/github.com/monopole/mdrip/example_tutorial.md
The above command has no output and exits with status zero if all the scripts labelled @lesson1
in the given markdown succeed. On any failure, however, the command dumps a report and exits with non-zero status.
This is one way to cover documentation with feature tests. Keeping code and documentation describing the code in the same file makes it much easier to keep them in sync.
A script is a sequence of code blocks with a common label. If a block has multiple labels, it can be incorporated into multiple scripts. If a block has no label, it's ignored. The number of scripts that can be extracted from a set of markdown files equals the number of unique labels.
If code blocks are in bash syntax, and the tool is itself running in a bash shell, then piping mdrip
output to source /dev/stdin
is equivalent to a human copy/pasting code blocks to their own shell prompt. In this scenario, an error in block N will not stop execution of block N+1. To instead stop on error, pipe the output to bash -e
.
Alternatively, the tool can itself run extracted code in a bash subshell like this
mdrip --subshell someLabel file1.md file2.md ...
If that command fails, so did something in a command block. mdrip
reports which block failed and what it's stdout
and stderr
saw, while otherwise capturing and discarding subshell output.
There‘s no notion of encapsulation. Also, there’s no automatic cleanup. A block that does cleanup can be added to the markdown.
The first label on a block is slightly special, in that it's reported as the block name for logging. But like any label it can be used for selection too.
The @sleep label causes mdrip to insert a sleep 2
command after the block. Appropriate if one is starting a server in the background in that block.