{"id":13595515,"url":"https://github.com/OceanSprint/tesh","last_synced_at":"2025-04-09T13:32:20.100Z","repository":{"id":63667517,"uuid":"568929414","full_name":"OceanSprint/tesh","owner":"OceanSprint","description":"TEstable SHell sessions in Markdown","archived":false,"fork":false,"pushed_at":"2023-11-15T18:11:58.000Z","size":181,"stargazers_count":133,"open_issues_count":11,"forks_count":8,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-09-10T10:22:23.129Z","etag":null,"topics":["markdown","shell","testing"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/tesh","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OceanSprint.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-11-21T18:01:21.000Z","updated_at":"2024-07-05T03:25:38.000Z","dependencies_parsed_at":"2023-10-24T21:24:15.322Z","dependency_job_id":"c31221c5-e699-4852-b128-fe043bb642ec","html_url":"https://github.com/OceanSprint/tesh","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OceanSprint%2Ftesh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OceanSprint%2Ftesh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OceanSprint%2Ftesh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OceanSprint%2Ftesh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OceanSprint","download_url":"https://codeload.github.com/OceanSprint/tesh/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223394531,"owners_count":17138561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["markdown","shell","testing"],"created_at":"2024-08-01T16:01:51.607Z","updated_at":"2024-11-06T18:31:02.749Z","avatar_url":"https://github.com/OceanSprint.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/OceanSprint/tesh/actions/workflows/ci.yml\"\u003e\n    \u003cimg alt=\"CI for tesh (main branch)\"\n         src=\"https://github.com/oceansprint/tesh/actions/workflows/ci.yml/badge.svg\"\u003e\n  \u003c/a\u003e\n  \u003cimg alt=\"Test coverage (main branch)\"\n       src=\"https://img.shields.io/badge/tests_coverage-100%25-brightgreen.svg\"\u003e\n  \u003cimg alt=\"Test coverage (main branch)\"\n       src=\"https://img.shields.io/badge/types_coverage-100%25-brightgreen.svg\"\u003e\n  \u003ca href=\"https://pypi.org/project/tesh/\"\u003e\n    \u003cimg alt=\"latest version of tesh on PyPI\"\n         src=\"https://img.shields.io/pypi/v/tesh.svg\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://pypi.org/project/tesh/\"\u003e\n    \u003cimg alt=\"Supported Python versions\"\n         src=\"https://img.shields.io/pypi/pyversions/tesh.svg\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/OceanSprint/tesh/blob/main/LICENSE\"\u003e\n    \u003cimg alt=\"License: MIT\"\n         src=\"https://img.shields.io/badge/License-MIT-yellow.svg\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/OceanSprint/tesh/graphs/contributors\"\u003e\n    \u003cimg alt=\"Built by these great folks!\"\n         src=\"https://img.shields.io/github/contributors/OceanSprint/tesh.svg\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# tesh [[tɛʃ]](http://ipa-reader.xyz/?text=t%C9%9B%CA%83\u0026voice=Joanna) - TEstable SHell sessions in Markdown\n\nShowing shell interactions how to run a tool is useful for teaching and explaining.\n\nMaking sure that example still works over the years is painfully hard.\n\nNot anymore.\n\n```console\n$ tesh demo/\n📄 Checking demo/happy.md\n  ✨ Running foo  ✅ Passed\n  ✨ Running bar  ✅ Passed\n📄 Checking demo/sad.md\n  ✨ Running foo  ❌ Failed\n         Command: echo \"foo\"\n\n         Expected:\nsad panda\n         Got:\nfoo\n\nTaking you into the shell ...\n\nEnter `!!` to rerun the last command.\n\n$\n```\n\n## Syntax\n\nTo mark a code block as testable, append `tesh-session=\"NAME\"` to the header line.\n\nYou can use any syntax highlighting directive, such as `bash`, `shell`, `shell-session`, `console` or others.\n\n~~~\n```console tesh-session=\"hello\"\n$ echo \"Hello World!\"\nHello World!\n```\n~~~\n\n### Linking multiple code blocks into a single shell session\n\nBesides marking a code block as testable, `tesh-session` is a unique identifier that allows for multiple code blocks to share the same session.\n\n~~~\n```console tesh-session=\"multiple_blocks\"\n$ export NAME=Earth\n\n```\n~~~\n\n~~~\n```console tesh-session=\"multiple_blocks\"\n$ echo \"Hello $NAME!\"\nHello Earth!\n```\n~~~\n\n### Ignoring parts of the output\n\nParts of the inline output can be ignored with `...`:\n\n~~~\n```console tesh-session=\"ignore\"\n$ echo \"Hello from Space!\"\nHello ... Space!\n```\n~~~\n\n### Multiline support\n\nThe same can be done for multiple lines of output. Note that trailing whitespace in every line is trimmed.\n\n~~~\n```console tesh-session=\"ignore\"\n$ printf \"Hello \\nthere \\nfrom \\nSpace!\"\nHello\n...\nSpace!\n```\n~~~\n\nCommands can continue across multiple lines by prefixing lines with `\u003e `.\n\n~~~\n```console tesh-session=\"multiline\"\n$ echo \"Hello from\" \\\n\u003e   \"another\" \\\n\u003e   \"line!\"\nHello from another line!\n```\n~~~\n\n## Advanced directives\n\nYou can set a few other optional directives in the header line:\n\n- `tesh-exitcodes`: a list of exit codes in the order of commands executed inside the code block,\n- `tesh-setup`: a filename of a script to run before running the commands in the code block,\n- `tesh-ps1`: allow an additional PS1 prompt besides the default `$`,\n- `tesh-platform`: specify on which platforms this session block should be tested (`linux`, `darwin`, `windows`),\n- `tesh-fixture`: a filename to save the current snippet,\n- `tesh-timeout`: number of seconds before a command timeouts (defaults to 30s),\n- `tesh-long-running`: set to `true` to showcase long-running commands such as `docker compose up`.\n\nLet's look at all of these through examples!\n\n### Testing exit codes\n\n`tesh-exitcodes` accepts a list of integers, which represent the exit code for every command in the block.\n\n~~~\n```console tesh-session=\"exitcodes\" tesh-exitcodes=\"1 0\"\n$ false\n\n$ true\n\n```\n~~~\n\n\n### Test setup\n\nSometimes 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.\n\n~~~\n```console tesh-session=\"setup\" tesh-setup=\"readme.sh\"\n$ echo \"Hello $NAME!\"\nHello Gaea!\n```\n~~~\n\n\n### Custom prompts\n\nEvery so often you need to drop into a virtualenv or similar shell that changes the prompt. `tesh` supports this via `test-ps1` directive.\n\n~~~\n```console tesh-session=\"prompt\" tesh-ps1=\"(foo) $\"\n$ PS1=\"(foo) $ \"\n\n\n(foo) $ echo \"hello\"\nhello\n```\n~~~\n\n### Only run on certain platforms\n\nSome examples should only run on certain platforms, use `tesh-platform` to declare them as such.\n\n~~~\n```console tesh-session=\"platform\" tesh-platform=\"linux\"\n$ uname\n...Linux...\n```\n~~~\n\n~~~\n```console tesh-session=\"platform\" tesh-platform=\"darwin\"\n$ uname\n...Darwin...\n```\n~~~\n\n### Dump file to disk\n\nOccasionally 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.\n\n~~~\n```bash tesh-session=\"fixture\" tesh-fixture=\"foo.sh\"\necho \"foo\"\n```\n~~~\n\n~~~\n```console tesh-session=\"fixture\"\n$ chmod +x foo.sh\n\n$ ./foo.sh\nfoo\n```\n~~~\n\n### Custom timeout\n\nBy default, `tesh` will fail if an example command does not finish in 30 seconds. This number can be modified using the `tesh-timeout` directive.\n\n~~~\n```console tesh-session=\"timeout\" tesh-timeout=\"3\"\n$ sleep 1\n\n```\n~~~\n\n\n### Long running processes\n\nSome 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.\n\n\n~~~\n```console tesh-session=\"long-running\" tesh-timeout=\"1\" tesh-long-running=\"true\"\n$ nmap 1.1.1.1\nStarting Nmap ...\n```\n~~~\n\n## Installation\n\nThe best way to install `tesh` is with your favorite Python package manager.\n\n```bash\n$ pip install tesh\n```\n\n## Design decisions\n\n- Supports Linux / macOS.\n- Not tied to a specific markdown flavor or tooling.\n- Renders reasonably well on GitHub.\n\n\n## Comparison with other tools\n\n| | tesh | [mdsh](https://github.com/zimbatm/mdsh) | [pandoc filters](http://www.chriswarbo.net/projects/activecode/index.html) |\n|------------------------------------------|---|---|---|\n| Execute shell session                    | ✔️ | ✔️ | ✔️ |\n| Modify markdown file with the new output | 🚧[\u003csub\u003e[1]\u003c/sub\u003e](https://github.com/OceanSprint/tesh/issues/6) | ✔️ | ✔️ |\n| Shared session between code blocks       | ✔️ | ✖️ | ✖️ |\n| Custom PS1 prompts                       | ✔️ | ✖️ | ✖️ |\n| Assert non-zero exit codes               | ✔️ | ✖️ | ✖️ |\n| Setup the shell environment              | ✔️ | ✖️ | ✖️ |\n| Reference fixtures from other snippets   | ✔️ | ✖️ | ✖️ |\n| Wildcard matching of the command output  | ✔️ | ✖️ | ✖️ |\n| Starts the shell in debugging mode       | ✔️ | ✖️ | ✖️ |\n| Specify timeout                          | ✔️ | ✖️ | ✖️ |\n| Support long-running commands            | ✔️ | ✖️ | ✖️ |\n\n* ✔️: Supported\n* C: Possible but you have to write some code yourself\n* 🚧: Under development\n* ✖️: Not supported\n* ?: I don't know.\n\n\n## Developing `tesh`\n\nWe 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/).\n\nFor Nix, run `nix develop` to enter the development environment, where everything is ready for use.\n\nFor Docker, run `docker build -t tesh . \u0026\u0026 docker run --rm -v .:/tesh -it tesh` to enter the development environment, where everything is ready for use.\n\nThen you can run `make tests` to run all tests \u0026 checks.\n\nAdditional `make` commands are available to run just a subset of tests or checks.\n\n```\n# run tesh on all Markdown files\n$ make tesh\n\n# run flake8 linters on changed files only\n$ make lint\n\n# run flake8 linters on all files\n$ make lint all=true\n\n# run mypy type checker\n$ make types\n\n# run unit tests\n$ make unit\n\n# run a subset of unit tests (regex find)\n$ make unit filter=foo\n```\n\n### Multiple Python versions\n\nBy default, the development environment uses the latest supported Python version. This is how you drop into an environment with an older Python\n\nOn Linux:\n```\n$ nix develop .#devShells.x86_64-linux.default-python39\n```\n\nOn macOS:\n\n```\n$ nix develop .#devShells.aarch64-darwin.default-python39\n```\n\nOn CI, all supported versions of Python are tested.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOceanSprint%2Ftesh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FOceanSprint%2Ftesh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOceanSprint%2Ftesh/lists"}