Executable documentation smoke tests for Markdown.
docsmoke runs the shell and Python examples you mark in README.md,
docs/, and onboarding guides, then fails CI when those examples stop
matching reality. It is intentionally small: opt-in fenced blocks,
inline expectations, deterministic timeouts, and reports that fit pull
requests.
- Docs fail like code — quickstarts, install commands, and CLI examples run in CI instead of quietly rotting.
- Markdown-native — authors keep examples in ordinary fenced blocks.
- Opt-in by default — only snippets marked for
docsmokeexecute unless you explicitly choose--all-supported. - Assert behavior — expectations, regexes, timeouts, working directories, environment overrides, skips, and shell overrides live next to the example.
- CI-native — console, JSON, and Markdown reports work in local terminals, GitHub Actions, and release gates.
- Supply-chain aware — releases publish to PyPI, GitHub Releases, GHCR, and the reusable GitHub Action with SBOMs and Sigstore bundles.
- your README contains copy-paste commands that users rely on
- your docs include shell or Python examples that should keep working
- you want a narrow documentation gate in CI
- you want to verify examples without adopting a full documentation platform
- you need prose style linting or grammar checks
- you need full notebook execution
- you need browser-based end-to-end tests
- you want to run arbitrary untrusted snippets without sandboxing
pipx install docsmoke
docsmoke --helpDetailed installation: docs/INSTALL.md.
Put docsmoke after the language in the fenced block's info string.
The first word, bash, still controls Markdown syntax highlighting; the
second word, docsmoke, is the opt-in marker that tells the scanner to
execute the block.
```bash docsmoke
# docsmoke: name=hello; expect-contains=hello
printf 'hello\n'
```Run a scan:
# docsmoke: name=scan-examples
docsmoke scan examples --quietList what would run:
# docsmoke: expect-contains=example-snippet
docsmoke list-snippets examples --jsonDirectives live in the first lines of a runnable fenced block:
```bash docsmoke
# docsmoke: name=install-check
# docsmoke: cwd=examples
# docsmoke: expect-contains=hello
printf 'hello\n'
```Supported directives:
name=<value>: human-friendly snippet labelcwd=<path>: working directory relative to the project roottimeout=<seconds>: positive per-snippet timeoutexpect-contains=<text>: required stdout or stderr substringexpect-regex=<pattern>: required regex match against stdout or stderrenv.NAME=<value>: environment variable overrideshell=<binary>: shell override for shell snippetsskip[=true|false]: skip the snippet without removing it
Use the moving @v1 tag to receive compatible 1.x fixes automatically, or
pin an exact release such as @v1.0.0 for fully reproducible workflow inputs.
- uses: dev-ugurkontel/docsmoke@v1
with:
paths: README.md docs examples- PyPI — best for
pipx, virtualenvs, and Python-based tooling. - GHCR — best when CI prefers a pinned container image.
- GitHub Action — best when docs validation already lives in Actions.
docker run --rm -v "$PWD:/work" -w /work \
ghcr.io/dev-ugurkontel/docsmoke:latest scan README.md docs examplesUse :latest for convenience, :1 for the moving stable major line, or
:1.0.0 for a fully pinned container.
docsmoke
passed examples/README.md:5 bash 0.004s ok
Summary: total=1 passed=1 failed=0 skipped=0 errors=0
- docs/INSTALL.md: installation options
- docs/USAGE.md: CLI usage and workflows
- docs/CONFIG.md: configuration file reference
- docs/ARCHITECTURE.md: internal architecture
- docs/RECIPES.md: CI, Docker, and migration recipes
- docs/REPORT_SCHEMA.md: JSON report contract
- docs/RELEASE.md: release and tag-management process
- docs/REPOSITORY.md: repository settings checklist
- CONTRIBUTING.md: contribution workflow
- SECURITY.md: private vulnerability disclosure
- SUPPORT.md: usage help and issue routing
make allApache 2.0 licensed. See LICENSE.