Code Monkey home page Code Monkey logo

mdsh's Introduction

$ mdsh - a markdown shell pre-processor

Build Status crates.io

The mdsh project describes a Markdown language extension that can be used to automate some common tasks in README.md files. Quite often I find myself needing to embed a snippet of code or markdown from a different file. Or I want to show the output of a command. In both cases this can be done manually, but what all you had to do was run mdsh and have the file updated automatically?

So the goal of this tool is first to extend the syntax of Markdown in a natural way. Something that you might type. And if the mdsh tool is run, the related blocks get updated in place. Most other tools would produce a new file but we really want a sort of idempotent operation here.

In the end this gives a tool that is a bit akin to literate programming or jupyer notebooks but for shell commands. It adds a bit of verbosity to the file and in exchange it allows to automate the refresh of those outputs.

Usage

$ mdsh --help

mdsh 0.9.0
Markdown shell pre-processor. Never let your READMEs and tutorials get out of sync again.

Exits non-zero if a sub-command failed.

USAGE:
    mdsh [FLAGS] [OPTIONS]

FLAGS:
        --clean      
            Remove all generated blocks

        --frozen     
            Fail if the output is different from the input. Useful for CI.
            
            Using `--frozen`, you can guarantee that developers update documentation when they make a change. Just add
            `mdsh --frozen` as a check to your continuous integration setup.
    -h, --help       
            Prints help information

    -V, --version    
            Prints version information


OPTIONS:
    -i, --inputs <inputs>...     
            Path to the markdown files. `-` for stdin [default: ./README.md]

    -o, --output <output>        
            Path to the output file, `-` for stdout [defaults to updating the input file in-place]

        --work_dir <work-dir>    
            Directory to execute the scripts under [defaults to the input file’s directory]

Syntax Extensions

Inline Shell Code

Syntax regexp:

^`[$>] ([^`]+)`\s*$

Inline Shell Code are normal inline code that:

  • start at the beginning of a line
  • include either $ or > at the beginning of their content
  • contain a shell command

When those are enountered, the command is executed by mdsh and output as either a fenced code block ($) or markdown code (>).

  • $ runs the command and outputs a code block
  • > runs the command and outputs markdown

Examples:

`$ seq 4 | sort -r`

```
4
3
2
1
```
`> echo 'I *can* include markdown. <code>Hehe</code>.'`

<!-- BEGIN mdsh -->
I *can* include markdown. <code>Hehe</code>.
<!-- END mdsh -->

Multiline Shell Code

Syntax regexp:

^```[$^]\n.*\n```$

Multiline Shell Code are normal multiline code that:

  • start at the beginning of a line
  • include $ or ^ as "language"
  • contain a shell command

When those are enountered, the command is executed by mdsh and output as either a fenced code block ($) or markdown code (>).

  • $ runs the command and outputs a code block
  • > runs the command and outputs markdown

Examples:

```$ as bash
seq 3 | sort -r
seq 2 | sort -r
```

```bash
3
2
1
2
1
```
```>
echo 'I *can* include markdown. <code>Hehe</code>.'
```

<!-- BEGIN mdsh -->
I *can* include markdown. <code>Hehe</code>.
<!-- END mdsh -->

Variables

Syntax regexp:

^`! ([\w_]+)=([^`]+)`\s*$

Variables allow you to set new variables in the environment and reachable by the next blocks that are being executed.

The value part is being evaluated by bash and can thus spawn sub-shells.

Examples:

! user=bob

Now the $user environment variable is available:

$ echo hello $user

hello bob

Now capitalize the user

! USER=$(echo $user | tr '[[:lower:]]' '[[:upper:]]')

$ echo hello $USER

hello BOB

Link Includes

Syntax regexp:

^\[[$>] ([^\]]+)]\([^\)]+\)\s*$

Link Includes work similarily to code blocks but with the link syntax.

  • $ loads the file and embeds it as a code block
  • > loads the file and embeds it as markdown

Examples:

[$ code.rb](samples/code.rb) as ruby

```ruby
require "pp"

pp ({ foo: 3 })
```
[> example.md](samples/example.md)

<!-- BEGIN mdsh -->
*this is part of the example.md file*
<!-- END mdsh -->

ANSI escapes

ANSI escape sequences are filtered from command outputs:

$ echo $'\e[33m'yellow

yellow

Commented-out commands

Sometimes it's useful not to render the command that is being shown. All the commands support being hidden inside of a HTML comment like so:

<!-- `$ echo example` -->

```
example
```

Fenced code type

If you want GitHub to highlight the outputted code fences, it's possible to postfix the line with as <type>. For example:

`$ echo '{ key: "value" }'` as json

```json
{ key: "value" }
```

Installation

The best way to install mdsh is with the rust tool cargo.

cargo install mdsh

If you are lucky enough to be a nix user:

nix-env -f https://github.com/NixOS/nixpkgs/archive/master.tar.gz -iA mdsh

If you are a nix + flakes user:

nix profile install github:zimbatm/mdsh

Running without installation

If you are a nix + flakes user:

nix run github:zimbatm/mdsh -- --help

Pre-commit hook

This project can also be installed as a pre-commit hook.

Add to your project's .pre-commit-config.yaml:

-   repo: https://github.com/zimbatm/mdsh.git
    rev: main
    hooks:
    -   id: mdsh

Make sure to have rust available in your environment.

Then run pre-commit install-hooks

Known issues

The tool currently lacks in precision as it doesn't parse the Markdown file, it just looks for the desired blocks by regexp. It means that in some cases it might misintepret some of the commands. Most existing Markdown parsers are used to generate HTML in the end and are thus not position-preserving. Eg: pulldown-cmark

The block removal algorithm doesn't support output that contains triple backtick or <!-- END mdsh -->.

Related projects

  • http://chriswarbo.net/essays/activecode/ is the closest to this project. It has some interesting Pandoc filters that capture code blocks into outputs. The transformation is not in-place like mdsh.
  • Literate Programming is the practice of interspesing executable code into documents. There are many language-specific implementations out there. mdsh is a bit like a bash literate programming language.
  • Jupyter Notebooks is a whole other universe of documentation and code. It's great but stores the notebooks as JSON files. A special viewer program is required to render them to HTML or text.

User Feedback

Issues

If you have any problems with or questions about this project, please contact use through a GitHub issue.

Contributing

You are invited to contribute new features, fixes or updates, large or small; we are always thrilled to receive pull requests, and do our best to process them as fast as we can.

License

> LICENSE

MIT License

Copyright (c) 2019 zimbatm and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

mdsh's People

Contributors

deemp avatar dependabot[bot] avatar domenkozar avatar moredread avatar profpatsch avatar renovate[bot] avatar zimbatm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

mdsh's Issues

add asciinema output

for long-running processes, it might be nice to record the output, and have a "player" to replay it, instead of having it inline in the document.

something like asciinema

support expanding code blocks

Just found this interesting project, thanks.
It seems close to what I need.

I think it would be really nice if mdsh could also support a code block format like this:

```shellsession
$ echo hello; echo there
hello
there
```

Instead of having to write:
`$ echo hello` as shellsession
```shellsession
hello
```

Particularly since the as ... will be rendered in the README, right? which is really not desirable.

handle ansi escape sequences

I just tried to use mdsh on a command that outputs some color and that currently has the undesired effect of yielding the escape codes verbatim in the output:

�[32m[please]:�[0m Running sanity checks:

- Nix installed :  �[32mOK�[0m
- contrail channel: �[32mOK�[0m
- contrail cache: �[32mOK�[0m
- kvm support: �[32mOK�[0m

All essential tests passed.

It would be great if there could be an option to filter those.

More related tools (and another one named mdsh)

Hi there! I stumbled across your project while searching for something related to this other mdsh project, and just thought I should mention the possible naming conflict. The mdsh I wrote is a tool for doing both data processing and execution of blocks within markdown, implemented as a shell script. It has third-party extensions such mdshi, which is actually kind of close to some of what your mdsh is for.

Anyway, I can't really lay claim to the name even if I used it first, but it might be nice if you named yours something more about its function (md-live? mdexec? mdgen?) and less about its shellness, since yours isn't actually written in shell and so the shell aspect of it is kind of incidental. Whereas mine is written in shell and compiles markdown into shell scripts, so the shellness is part of its essential nature. ;-)

Also, as an FYI, there's a tool you might be interested in called cram that does something very similar to your mdsh as well, but it's for testing anything that can be tested by comparing a file's content with the output of shell commands, . It processes indented blocks like this:

$ ls
foo
bar

...by running the commands that begin with $ (optionally continued with > ), and verifying the output matches the input. If you set its indentation width to 4, then it can process markdown indented code blocks, and it can also be used to generate the data in the first place, because if the output doesn't match the input file, you can tell it to update the input file with the new output.

I actually use cram for testing mdsh (my mdsh, that is) and other shell tools, as it's nice to be able to have the documentation examples tested, as well as being able to document the tests. :-)

So, these all may or may not be projects you might or might not want to mention in the "Related Projects" part of your readme. Hope you find some or any of this illuminating and/or useful.

Unfenced code with backticks being interpreted as command?

Thank you for your work and I apologize if this is an obvious mistake on my part.
I was expecting mdsh to leave unfenced code alone.

For example, in a file containing

`leave-me-alone`

`$ echo 1`

I would expect the output to be

`leave-me-alone`
`$ echo 1`

```
1
```

Instead, I get

Using input=File("input.md") output=StdHandle work_dir=Parent("/tmp") clean=false frozen=false
`leave-me-alone`
thread 'main' panicked at src/main.rs:414:25:
WTF, not supported
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

This panic!("WTF, not supported") is there becasue mdsh expects leave-me-alone to be a RE_MATCH_FENCE_COMMAND, a RE_MATCH_MD_COMMAND, or a RE_MATCH_VAR_COMMAND, which of course it isn't. Instead of panicking, shouldn't the code simply leave the original_line.to_string() in place undisturbed? After all, there was no fence there.

`mdsh` clobbers code block immediately following command substitution

Take the following in.md file as an example:

`$ echo hi`

\```
bye
\```

After running mdsh --input in.md --output out.md, out.md looks like the following:

`$ echo hi`
\```
hi
\```

Notice how the "bye" code block is now missing.

This can be worked around by adding a dummy code block or HTML comment after command substitutions, e.g. <!-- --> or

\```
\```

add configurable work_dir

By default the work_dir should be the folder containing the input file. That way, calling mdsh path/to/README.md will give the same output as cd path/to && mdsh.

If the input is - then the work_dir shouldn't change.

add syntax for using existing snippet as input

As far as I understand mdsh, it is currently used to (re-)generate markdown snippets. It would be nice if a command could update an existing snippet, so we don't have to generate the entire thing in code but rather modify the tiny bit we are interested in.

This could be implemented by injecting the target block as stdin:

& sed -e 's/foo/bar/'

loooooooooooooooooooooooong snippet foo

Run mdsh. foo is fed as stdin to sed. Result:

& sed -e 's/foo/bar/'

loooooooooooooooooooooooong snippet bar

This could for example be used to update things like versions in more complex markdown blocks.

allow switching between modes

It should be possible to switch modes from > to $ or reversely, re-run mdsh and have the new output.

This means expanding the clean regexp to support both modes at the same time.

Invisible command, for docs generation

When generating documentation, the command itself is usually not very interesting and it may distract the reader.

This can replace for example terraform-docs-updater.

Proposed doc:


Invisible Includes

When generating documentation, the command itself is usually not very interesting and it may distract the reader.

Syntax regexp:
TBD

Invisible Includes work similarily to code blocks but with the comment syntax.

  • $ loads the file and embeds it as a code block
  • > loads the file and embeds it as markdown

Examples:

<!-- $ wc -l <sponsors.txt -->
```
42
```
<!-- > terraform-docs markdown ./my-module -->
<!-- BEGIN mdsh -->
# Inputs
[... actual generated docs ...]
<!-- END mdsh -->

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

cargo
Cargo.toml
  • difference 2.0.0
  • lazy_static 1.4.0
  • regex 1.9.5
  • structopt 0.3.26
github-actions
.github/workflows/ci.yaml
  • actions/checkout v4@692973e3d937129bcbf40652eb9f2f61becf3332
  • cachix/install-nix-action v27@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b

  • Check this box to trigger a request for Renovate to run again on this repository

include stderr in output

I think stderr should be included in the output - or at least there should be an option to include it.

Build instructions

Nice tool

Here's how I built it

nix-shell -p cargo --command "cargo build"

Maybe add instructions along those lines?

Block insert whitespace issue

README.md:

`$ echo foo`



## Heading

mdsh

`$ echo foo`


```
foo
```
## Heading

mdsh

`$ echo foo`

```
foo
```
## Heading

mdsh

`$ echo foo`
```
foo
```
## Heading

Then it reaches a fixpoint.

block formatting syntax

It would be nice if the outputted blocks could also include the highlighting format.

Where in the following block do we give the syntax?

`[$ code.rb](code.rb)`

So that it outputs:

```ruby
require "pp"

pp ({ foo: 3 })
```

add support for variables

the proposed syntax to set a variable would be:

`! foo=bar`

once the variable is set in the document, all further programs will have it in their environment variables.

It should also be possible to set a variable with the output of a program with:

`! foo=$(cat /etc/passwd)`

add mdsh header

when running mdsh on a file, it might make sense to insert a comment header to the file that explains how to run mdsh on the file again. If the header would be to contain a version number it would also allow upgrade scenarios in case the format has changed between version.

`--frozen` to expect no changes in README

This is another one for CI:

mdsh --frozen should check whether the new README is exactly the same as the old one, and fail if they are not the same.

We can achieve something similar with a trivial:

git diff --exit-code

after running mdsh, but that picks up all files that have been modified by the commands in the md file, which might or might not be what the user wants.

Alternatively, a wrapper script like

INPUT="$1"
tmp=$(mktemp)
cp "$INPUT" "$tmp"
mdsh "$INPUT"
diff "$tmp" "$INPUT"

might also be enough for most use cases.

I’m not sure whether --frozen is necessary in the end, but if it isn’t the best practices should be documented.

Fail when a command returns ≠ 0

mdsh should run all commands, fail if one command returned non-success and then output all commands that failed (with their stdout and stderr), in a way that is nice to integrate with CI.

Reasoning: I want to be able to use mdsh as a kind of doctest for README.md files, to make sure the examples always work.

I will prepare a PR.

add image output

Technically this is doable with > ./somecommand that writes the image and outputs the markdown link to it. Maybe there is a shorter way to do that...

`cabal-rpm builddep && cabal install || stack install` panics

I tried mdsh on the following readme.md:

Using input=File("./README.md") output=File("./README.md") work_dir=Parent(".") clean=false frozen=false
`$ koji-tool --version`
`$ koji-tool --help`
`$ koji-tool builds --help`
`$ koji-tool tasks --help`
`$ koji-tool install --help`
`$ koji-tool find`
` cabal-rpm builddep && cabal install || stack install`
thread 'main' panicked at /var/home/petersen/.cargo/registry/src/index.crates.io-6f17d22bba15001f/mdsh-0.7.0/src/main.rs:387:25:
WTF, not supported
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

panic!("WTF, not supported")

Is there an easy way to ignore some backquotes?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.