Skip to content

rats.docs

Create the best documentation solution for your project.

This module provides a rats-docs command that helps manage documentation in monorepos by combining generated api docs with markdown files into a single site generated with MkDocs.

We don't plan on enhancing the features of the documentation tooling outside of using standard mkdocs plugins. Teams working in single-component repositories should be able to author documentation using the standard mkdocs build and mkdocs serve commands. In monorepos, our tools help enable cross-referencing and generate a single site that allows us to search across all components instead of publishing an isolated documentation site for each component. We try to accomplish this as a small command proxy that inserts default arguments to the underlying mkdocs command.

Note

Previously, this module would copy original markdown files, and symlink directories during the build process to unify the docs across components. However, over time, we've found solutions that eliminate these custom operations, making the rats-docs commands much simpler, and making it easy to get the same output when using mkdocs commands directly.

Structure

We structure our documentation site such that each component is as self contained as possible, each containing a dedicated docs directory. These component docs are then merged with a docs folder found at the root of the repository, where authors can create tutorials and other types of content that might combine component features to build comprehensive examples. The root docs directory uses symlinks to the individual component docs, and mkdocs is only made aware of the root docs.

We can look at the structure of the rats repo as an example:

.
├── bin
   ├── rats-docs
   ├── ├── docs # (1)
   ├── css
      └── extra.css
   ├── images
      ├── favicon.ico
      └── logo.png
   └── index.md
├── rats
   ├── docs
      └── index.md
   ├── README.md
   └── src
       └── rats
├── rats-apps
   ├── docs # (2)
      ├── index.md
      ├──       └── rats.runtime.md
   ├── README.md
   ├── src
      ├── rats
      ├── rats_e2e
      └── rats_resources
   └── test
       └── rats_test
├── rats.code-workspace
├── rats-devtools
   ├── docs
      ├── index.md
      ├──       └── rats.docs.md # (3)
   ├── README.md
   ├── src
      ├── rats
      ├── rats_e2e
      └── rats_resources
   └── test
       ├── rats_test
       └── rats_test_resources
└── README.md -> docs/index.md
  1. 🙋‍♂️ The root documentation can contain an introduction to the project and provide links to component pages.
  2. 🙋‍♂️ Components contain their own docs directory. We symlink this directory such that the rats-apps documentation will be at /rats-apps/.
  3. 🙋‍♂️ This page is part of rats-devtools and will be built as if it was found at docs/rats-devtools/rats.docs.md.

CLI

In CI Pipelines, the rats-docs build command will generate the documentation site and place it in the dist/site directory of your devtools component—the component in your repo that contains your mkdocs.yaml, and has rats-devtools installed. The generated docs site can be deployed to Github Pages.

$ rats-docs build
INFO    -  Cleaning site directory
INFO    -  Building documentation to directory: /…/rats/rats-devtools/dist/site
INFO    -  Documentation built in 2.03 seconds

Locally, run rats-docs serve to make the documentation available at http://127.0.0.1:8000/. Just like mkdocs serve, the site will automatically rebuild after any detected changes.

$ rats-docs serve
INFO    -  Building documentation...
INFO    -  Cleaning site directory
INFO    -  Documentation built in 1.92 seconds
INFO    -  [22:58:02] Watching paths for changes: 'dist/docs', 'dist/mkdocs.yaml',
            '/…/rats/rats-apps/src', 'src', 'mkdocs.yaml'
INFO    -  [22:58:02] Serving on http://127.0.0.1:8000/
INFO    -  [22:58:07] Browser connected: http://127.0.0.1:8000/
INFO    -  [23:06:03] Detected file changes
INFO    -  Building documentation...
INFO    -  Documentation built in 2.08 seconds
INFO    -  [23:06:05] Reloading browsers
INFO    -  [23:06:24] Browser connected: http://127.0.0.1:8000/

Note

rats-docs serve and rats-docs build are simply using the mkdocs cli after, and you can provide arguments to the underlying command separated by a double dash --. You can see the entire list of options by running rats-docs serve -- --help, which should be equivalent to running mkdocs serve --help.

$ rats-docs serve -- --dev-addr "127.0.0.1:8181"
INFO    -  Building documentation...
INFO    -  Cleaning site directory
INFO    -  Documentation built in 2.73 seconds
INFO    -  [14:42:00] Serving on http://127.0.0.1:8181/

Configuration

rats.docs.AppConfigs contains any configurable aspects of the rats-docs application. Register a rats.apps.ContainerPlugin to the rats.docs python entry-point, then define providers for the configuration values needing updates.

[project.entry-points."rats.docs"]
"rats.example" = "rats.example:PluginContainer"
from rats import apps, docs


class PluginContainer(apps.Container, apps.PluginMixin):
    @apps.service(docs.AppConfigs.DOCS_COMPONENT_NAME)  # (1)
    def _docs_component(self) -> str:
        return "my-docs-component"  # (2)
  1. By default, we assume your repo has a component with devtools in the name, and we assume this is the component that should run mkdocs. The default can be replaced if it doesn't meet your needs.
  2. We set the "my-docs-example" component as the one where mkdocs is installed and configured.

__all__ = ['AppConfigs', 'Application', 'main'] module-attribute

AppConfigs

DOCS_COMPONENT_NAME = apps.ServiceId[str]('docs-component-name.config') class-attribute instance-attribute

The name of the component in the repo that contains the mkdocs packages.

By default, we assume there is a component in the repo named *devtools*; but this behavior can be replaced by registering a rats.apps.ContainerPlugin to the rats.docs python entry-point in pyproject.toml.

The final built documentation, created by rats-docs build, will be placed in the dist/site directory within this component.

MKDOCS_YAML = apps.ServiceId[str]('mkdocs-yaml.config') class-attribute instance-attribute

The path to the mkdocs.yaml config file, relative to the root of the repo.

By default, we assume the mkdocs.yaml file is in the root directory, but it's sometimes preferred to keep it in the docs component, defined by rats.docs.AppConfigs.DOCS_COMPONENT_NAME.

Application(app)

Bases: apps.AppContainer, cli.Container, apps.PluginMixin

Source code in rats/apps/_app_containers.py
def __init__(self, app: Container) -> None:
    self._app = app

execute()

Source code in src/rats/docs/_app.py
def execute(self) -> None:
    cli.create_group(click.Group("rats-docs"), self).main()

build(mkdocs_args)

Build the mkdocs site for every component in the project.

Source code in src/rats/docs/_app.py
@cli.command()
@click.argument("mkdocs-args", nargs=-1)
def build(self, mkdocs_args: tuple[str, ...]) -> None:
    """Build the mkdocs site for every component in the project."""
    ptools = self._app.get(projects.PluginServices.PROJECT_TOOLS)
    docs_component = ptools.get_component(self._app.get(AppConfigs.DOCS_COMPONENT_NAME))
    site_dir_path = docs_component.find_path("dist/site")
    config_path = ptools.repo_root() / self._app.get(AppConfigs.MKDOCS_YAML)
    docs_component.run(
        "mkdocs",
        "build",
        "--config-file",
        config_path.as_posix(),
        "--site-dir",
        site_dir_path.as_posix(),
        *mkdocs_args,
    )

serve(mkdocs_args)

Serve the mkdocs site for the project and monitor files for changes.

Source code in src/rats/docs/_app.py
@cli.command()
@click.argument("mkdocs-args", nargs=-1)
def serve(self, mkdocs_args: tuple[str, ...]) -> None:
    """Serve the mkdocs site for the project and monitor files for changes."""
    ptools = self._app.get(projects.PluginServices.PROJECT_TOOLS)
    docs_component = ptools.get_component(self._app.get(AppConfigs.DOCS_COMPONENT_NAME))
    config_path = ptools.repo_root() / self._app.get(AppConfigs.MKDOCS_YAML)
    docs_component.run(
        "mkdocs",
        "serve",
        "--config-file",
        config_path.as_posix(),
        *mkdocs_args,
    )

main()

Source code in src/rats/docs/_app.py
def main() -> None:
    apps.run_plugin(logs.ConfigureApplication)
    apps.run_plugin(Application)