Skip to content

Latest commit

 

History

History
243 lines (172 loc) · 7.65 KB

README.md

File metadata and controls

243 lines (172 loc) · 7.65 KB

Mechanical markdown by example

Prerequisites

Be sure you have mechanical markdown installed and that the mm.py utility is in your $PATH. These examples were written with basic bash commands in mind, but any bash like shell should work. See Shells below for alternatives.

Using this guide

All markdown files in examples/ (including this one!) are annotetated and can be executed and validated with:

mm.py filename.md

This guide automatically executed as part of this project's continuous integration pipeline. It serves as both user guide and integration test suite for this package. If you use it run through it, hopefully you will see what a powerful concept self executing documentation can be.

How mechanical-markdown works

One of the beautiful features of markdown is that it is both human and machine readable. A human can read a user guide and copy paste steps into their terminal. A machine can do the same and do some extra validation on the steps to make sure they executed correctly. Also, most markdown engines support embedded HTML comments <!-- -->. We can use these HTML comments to embed extra information to tell our validation program what output we expect from a command. The mm.py utility will automatically extract this information, allong with the commands to execute from our markdown files.

Annotation format

To tell mechanical-markdown what parts of your document need to be executed as code, you must add an HTML comment that begins with the token STEP. After this token, mechanical-markdown will interpret the rest of the comment as a yaml document with instructions for how the code blocks should be executed and verified. All fields in this yaml document are optional. Finish the comment to denote then end of the yaml document and the beginning of your exectuable markdown code. Finally, finish with an html comment like this: <!-- END_STEP --> to denote the end of a step. Let's look at a basic example:

<!-- STEP 
name: Hello World
expected_stdout_lines:
  - "Hello World!"
-->

You can use regular markdown anywhere during a step. It will be ignored. Only denoted as bash or sh will be executed.

```bash
echo "Hello World!"
```

This python code block will not be executed:

```python
print("This python script will not be run")
```
    
<!-- END_STEP -->

This unannotated command will not be run:

```bash
echo "A command that will not be run"
```

Here's how the above will render in your markdown interpreter.


You can use regular markdown anywhere during a step. It will be ignored. Only code blocks denoted as bash or sh will be executed.

echo "Hello World!"

This python code block will not be executed:

print("This python script will not be run")

This unannotated command will not be run:

echo "A command that will not be run"

Let's breakdown what this embedded yaml annotation is doing. There are two fields in our yaml document name and expected_stdout_lines. The name field simply provides a name for the step that will be printed to the report that mm.py generates. The expected_stdout_lines field is actually telling mm.py what it should be looking for from stdout when it executes our code block(s). For more on this, checkout io.md.

CLI

Help

For a list of options:

mm.py --help
usage: mm.py [-h] [--dry-run] [--manual] [--shell SHELL_CMD] [--version]
             [MARKDOWN_FILE]

Auto validate markdown documentation

optional arguments:
  -h, --help            show this help message and exit
  --version             Print version and exit

  MARKDOWN_FILE         The annotated markdown file to run/execute
  --dry-run, -d         Print out the commands we would run based on
                        markdown_file
  --manual, -m          If your markdown_file contains manual validation
                        steps, pause for user input
  --shell SHELL_CMD, -s SHELL_CMD
                        Specify a different shell to use

Version

mm.py --version

Dry Run

You can do a dry run to print out exactly what commands will be run using the '-d' flag.

mm.py -d README.md

This will print out all the steps that would be run, without actually running them. Output looks something like this

Would run the following validation steps:
Step: Hello World
	commands to run with 'bash -c':
		`echo "Hello World!"`
	Expected stdout:
		Hello World!
	Expected stderr:

...

Run and Validate

Now you can run the steps and verify the output:

mm.py start.md

The script will parse the markdown, execute the annotated commands, and then print a report like this:

Running shell 'bash -c' with command: `echo "Hello World!"`
Running shell 'bash -c' with command: `mm.py --help`
Running shell 'bash -c' with command: `mm.py -d README.md`

Step: Hello World
	command: `echo "Hello World!"`
	return_code: 0
	Expected stdout:
		Hello World!
	Actual stdout:
		Hello World!
		
	Expected stderr:
	Actual stderr:
...

If anything unexpected happens, you will get report of what went wrong, and mm.py will return non-zero.

Shells

The default shell used to execute scripts is bash -c. You can use a different shell interpreter by specifying one via the cli:

mm.py -s 'zsh -c' README.md

Manual validation

You can add manual validation steps to your document. A manual validation step is just a pause message to allow the user to take some manual step like opening a browser. These steps normally get ignored, as mm.py is designed to do automated validation by default. If you run the following, it will enable mm.py to pause for user input. (View raw markdown for an example of what a manual_pause_message looks like):

mm.py -m README.md 

More examples: