Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/OceanSprint/tesh

TEstable SHell sessions in Markdown
https://github.com/OceanSprint/tesh

markdown shell testing

Last synced: about 1 month ago
JSON representation

TEstable SHell sessions in Markdown

Awesome Lists containing this project

README

        



CI for tesh (main branch)

Test coverage (main branch)
Test coverage (main branch)

latest version of tesh on PyPI


Supported Python versions


License: MIT


Built by these great folks!

# tesh [[tɛʃ]](http://ipa-reader.xyz/?text=t%C9%9B%CA%83&voice=Joanna) - TEstable SHell sessions in Markdown

Showing shell interactions how to run a tool is useful for teaching and explaining.

Making sure that example still works over the years is painfully hard.

Not anymore.

```console
$ tesh demo/
📄 Checking demo/happy.md
✨ Running foo ✅ Passed
✨ Running bar ✅ Passed
📄 Checking demo/sad.md
✨ Running foo ❌ Failed
Command: echo "foo"

Expected:
sad panda
Got:
foo

Taking you into the shell ...

Enter `!!` to rerun the last command.

$
```

## Syntax

To mark a code block as testable, append `tesh-session="NAME"` to the header line.

You can use any syntax highlighting directive, such as `bash`, `shell`, `shell-session`, `console` or others.

~~~
```console tesh-session="hello"
$ echo "Hello World!"
Hello World!
```
~~~

### Linking multiple code blocks into a single shell session

Besides marking a code block as testable, `tesh-session` is a unique identifier that allows for multiple code blocks to share the same session.

~~~
```console tesh-session="multiple_blocks"
$ export NAME=Earth

```
~~~

~~~
```console tesh-session="multiple_blocks"
$ echo "Hello $NAME!"
Hello Earth!
```
~~~

### Ignoring parts of the output

Parts of the inline output can be ignored with `...`:

~~~
```console tesh-session="ignore"
$ echo "Hello from Space!"
Hello ... Space!
```
~~~

### Multiline support

The same can be done for multiple lines of output. Note that trailing whitespace in every line is trimmed.

~~~
```console tesh-session="ignore"
$ printf "Hello \nthere \nfrom \nSpace!"
Hello
...
Space!
```
~~~

Commands can continue across multiple lines by prefixing lines with `> `.

~~~
```console tesh-session="multiline"
$ echo "Hello from" \
> "another" \
> "line!"
Hello from another line!
```
~~~

## Advanced directives

You can set a few other optional directives in the header line:

- `tesh-exitcodes`: a list of exit codes in the order of commands executed inside the code block,
- `tesh-setup`: a filename of a script to run before running the commands in the code block,
- `tesh-ps1`: allow an additional PS1 prompt besides the default `$`,
- `tesh-platform`: specify on which platforms this session block should be tested (`linux`, `darwin`, `windows`),
- `tesh-fixture`: a filename to save the current snippet,
- `tesh-timeout`: number of seconds before a command timeouts (defaults to 30s),
- `tesh-long-running`: set to `true` to showcase long-running commands such as `docker compose up`.

Let's look at all of these through examples!

### Testing exit codes

`tesh-exitcodes` accepts a list of integers, which represent the exit code for every command in the block.

~~~
```console tesh-session="exitcodes" tesh-exitcodes="1 0"
$ false

$ true

```
~~~

### Test setup

Sometimes you need to do some test setup before running the examples in your code blocks. Put those [in a file](./readme.sh) and point to it with the `tesh-setup` directive.

~~~
```console tesh-session="setup" tesh-setup="readme.sh"
$ echo "Hello $NAME!"
Hello Gaea!
```
~~~

### Custom prompts

Every so often you need to drop into a virtualenv or similar shell that changes the prompt. `tesh` supports this via `test-ps1` directive.

~~~
```console tesh-session="prompt" tesh-ps1="(foo) $"
$ PS1="(foo) $ "

(foo) $ echo "hello"
hello
```
~~~

### Only run on certain platforms

Some examples should only run on certain platforms, use `tesh-platform` to declare them as such.

~~~
```console tesh-session="platform" tesh-platform="linux"
$ uname
...Linux...
```
~~~

~~~
```console tesh-session="platform" tesh-platform="darwin"
$ uname
...Darwin...
```
~~~

### Dump file to disk

Occasionally your examples consist of first showing contents of a file, then executing a command that uses said file. This is supported, use the `tesh-fixture` directive.

~~~
```bash tesh-session="fixture" tesh-fixture="foo.sh"
echo "foo"
```
~~~

~~~
```console tesh-session="fixture"
$ chmod +x foo.sh

$ ./foo.sh
foo
```
~~~

### Custom timeout

By default, `tesh` will fail if an example command does not finish in 30 seconds. This number can be modified using the `tesh-timeout` directive.

~~~
```console tesh-session="timeout" tesh-timeout="3"
$ sleep 1

```
~~~

### Long running processes

Some processes that you want to show examples for are long-running processes, like `docker compose up`. They are supported in `tesh` blocks using the `tesh-long-running` directive. Note that they need to be the last command in the block.

~~~
```console tesh-session="long-running" tesh-timeout="1" tesh-long-running="true"
$ nmap 1.1.1.1
Starting Nmap ...
```
~~~

## Installation

The best way to install `tesh` is with your favorite Python package manager.

```bash
$ pip install tesh
```

## Design decisions

- Supports Linux / macOS.
- Not tied to a specific markdown flavor or tooling.
- Renders reasonably well on GitHub.

## Comparison with other tools

| | tesh | [mdsh](https://github.com/zimbatm/mdsh) | [pandoc filters](http://www.chriswarbo.net/projects/activecode/index.html) |
|------------------------------------------|---|---|---|
| Execute shell session | ✔️ | ✔️ | ✔️ |
| Modify markdown file with the new output | 🚧[[1]](https://github.com/OceanSprint/tesh/issues/6) | ✔️ | ✔️ |
| Shared session between code blocks | ✔️ | ✖️ | ✖️ |
| Custom PS1 prompts | ✔️ | ✖️ | ✖️ |
| Assert non-zero exit codes | ✔️ | ✖️ | ✖️ |
| Setup the shell environment | ✔️ | ✖️ | ✖️ |
| Reference fixtures from other snippets | ✔️ | ✖️ | ✖️ |
| Wildcard matching of the command output | ✔️ | ✖️ | ✖️ |
| Starts the shell in debugging mode | ✔️ | ✖️ | ✖️ |
| Specify timeout | ✔️ | ✖️ | ✖️ |
| Support long-running commands | ✔️ | ✖️ | ✖️ |

* ✔️: Supported
* C: Possible but you have to write some code yourself
* 🚧: Under development
* ✖️: Not supported
* ?: I don't know.

## Developing `tesh`

We provide two development environments for people working on this project, one based on [Nix](https://nixos.org/) and one based on [Docker](https://www.docker.com/).

For Nix, run `nix develop` to enter the development environment, where everything is ready for use.

For Docker, run `docker build -t tesh . && docker run --rm -v .:/tesh -it tesh` to enter the development environment, where everything is ready for use.

Then you can run `make tests` to run all tests & checks.

Additional `make` commands are available to run just a subset of tests or checks.

```
# run tesh on all Markdown files
$ make tesh

# run flake8 linters on changed files only
$ make lint

# run flake8 linters on all files
$ make lint all=true

# run mypy type checker
$ make types

# run unit tests
$ make unit

# run a subset of unit tests (regex find)
$ make unit filter=foo
```

### Multiple Python versions

By default, the development environment uses the latest supported Python version. This is how you drop into an environment with an older Python

On Linux:
```
$ nix develop .#devShells.x86_64-linux.default-python39
```

On macOS:

```
$ nix develop .#devShells.aarch64-darwin.default-python39
```

On CI, all supported versions of Python are tested.