Partial Documentation
Partial documentation is a framework that allows writers of MkDocs-based documentation to deliver parts of documentation as Python packages, rather than placing all documentation in a single codebase.
Scenarios:
- Keep documentation close to the code: When a project has multiple repositories, each with its own documentation, the documentation site can be assembled from all repository docs.
- Share a documentation subset across multiple sites: For projects that share some code but maintain independent documentation, the shared documentation part can be distributed as a package and linked to each project site.
- Synchronize the look and feel of multiple sites: When several documentation sites need a unified look and feel, the shared site configuration and UI customizations can be distributed, even though the content differs.
- Bypass the MkDocs requirement to keep all content in the
docs_dir
: If thedocs_dir
constraint is limiting, documentation content from outsidedocs_dir
can be linked into the site.
Installation
PyPI
To install mkdocs-partial
package, run the following command from the command line:
pip install mkdocs-partial
mkdocs-partial
command through a console script entry point and includes the MkDocs plugins described below.
Docker
Pull:
docker pull exordis/mkdocs-partial
Execute:
docker run exordis/mkdocs-partial
Entrypoint is mkdocs-partial. See Creating Packages for args documentation
GitHub
pip install git+https://github.com/exordis/[email protected]
pip install git+https://github.com/exordis/mkdocs-partial@master
Plugins
docs_package
- Injects content from a specified directory into the MkDocs documentation site.partial_docs
- Automatically loads all installed plugins that inherit fromdocs_package
for CI/CD scenarios.
Docs Package
The docs_package
plugin takes the contents of a specified directory and injects them into a target directory on the documentation site. There are two main options for content injection:
-
Target directory injection: takes the contents of a specified directory and injects them into a target directory within
docs_dir
. -
Inheritance-based extension: users can inherit from
DocsPackagePlugin
to create a package that serves documentation for specific resources.
When injecting content the docs_package
plugin does not create any files directly on the filesystem; instead, it uses MkDocs' on_files
event to manage content injection. Only exception is blogs plugin from Material for MkDocs (see below)
Configuration
enabled
- boolean setting that allows the plugin to be disabled while keeping the rest of the configuration intact.docs_path
- path specifying the location of content files to be included.directory
- directory of the site documentation to inject the content to. Should be relative path todocs_dir
. Use empty string to inject content to the root of the site.name
- name used to reference the package. By default, MkDocs assigns the plugin's entry point name, with #N added if there are multiple instances. For example ifdocs_package
is registered twice, the default names would bedocs_package #1
anddocs_package #2
.edit_url_template
- template for the edit URL. Each injected page will have anedit_url
based on this template, which can be used to showedit
links (e.g., for editing the original file on GitHub or GitLab). This must be a string with{path}
as a placeholder, replaced by the path relative todocs_path
.
For example, for GitLab, it could be"${CI_PROJECT_URL}/-/edit/${CI_COMMIT_BRANCH}/{path}?ref_type=heads"
.title
- title override for package rootindex.md
.
Note
package root index.md
title applied also to directory where package is injected.
Basic usage
To inject the content from directories outside of docs_path
, following configuration can be used:
site_name: "Basic Usage"
plugins:
- docs_package:
directory: injected1
docs_path: ~/my-docs/injected_dir1/ # path to the directory containing files to be injected
name: injected1
- docs_package:
directory: injected2
docs_path: ~/my-docs/injected_dir2/ # path to the directory containing files to be injected
name: injected2
Integrations
Macros
If mkdocs-macros plugin is detected docs_package
will register macros
package_link
Constructs link to the content injected by referenced package.
Arguments
path
-
path within the package.
name
-
name of the package. default: package managing current page. Fails if current page is not managed with
docs_package
Samples
[Injected page]({{ "getting-started/faq.md" | package_link("injected2") }} )
injected_dir2/getting-started/faq.md
link
[Injected page from the same package]({{ "getting-started/faq.md" | package_link }} )
getting-started/faq.md
within current package
Note
mkdocs recommends having only relative to docs_dir
URIs. With package_link
macro changing inject directory of plugin does not require any changes in content
package_version
Renders version of the package.
Arguments
name
-
name of the package. default: package managing current page. Fails if current page is not managed with
docs_package
Samples
{{ package_version() }}
generates version of the packages managing current page. Within current page it is mkdocs-material
and generated version will be 1.34.1
{{ package_version("my-package") }}
{{ "my-package" | package_version }}
generates version of the specific packages.
Redirects
If mkdocs-redirects plugin is detected docs_package
will
- handle
redirects
tag in front matter as list of alternative URIs for the page - each redirect would be registered with mkdocs-redirects as redirect from the specified path (must be relative to package directory) to current page
It is needed to handle cases where docs_package
page referenced in other packages moves to new uri. Common practice is to build mkdocs site with --strict
to treat warnings as errors while move of the page referenced in other packages produces warning about missing link target page missing.
Having redirects allows to avoid complex flows and communication between package maintainers to handle page moves though keeps documentation consistent.
Sample:
---
title: FAQ
redirects:
- getting-started/faq.md
- guides/faq.md
---
Spellcheck
If mkdocs-spellcheck plugin is detected docs_package
will
- Add each line from
known_words.txt
(if found) from injected folder to spellcheck dictionary. - Disable spellcheck for pages that have
spellcheck: false
set in front matter. - Disable spellcheck for for sections of a page following
<!-- spellcheck: disable -->
until<!-- spellcheck: enable -->
or the end of the page ifenable
is missing. - Include
docs_package
plugin name to spellcheck warnings.
Mkdocs Material Blogs
If blogs plugin from Material for MkDocs is detected docs_package
will inject blog posts from directory having path matching post_dir
of blogs plugin within injected directory (by default - blog/posts
)
Note
blogs plugin manipulates with filesystem, so to inject blog posts docs_package
creates partial
directory in post_dir
and creates files there. It is recommended to add [post_dir]/partial
to .gitignore
Markdown Extensions
Mkdocs supports configuring markdown_extensions to use paths relative docs_dir.
It does not work for docs_package
handled pages as they are generated from mkdocs
perspective thus and not have path on file system path relative to docs_dir
.
To have same logic as for files statically by mkdocs, if docs_package
is used with mkdocs.yml
it extends config with !docs_package_relative
tag, that expands to path of current page package root (if page is not handled by docs_package
it resolves to non-existing directory).
markdown_extensions:
- pymdownx.snippets:
base_path:
# Lookup snippet files path as relative to mkdocs.yml
- !relative $config_dir
# Lookup snippet files path as relative to docs_package root
- !docs_package_relative
Snippet files must reside in directory passed as source directory when creating docs package
Note
At the moment such files are served over http. In later releases CLI would be extended to let exclusion of some packaged files from being served with http
Partial Docs
partial_docs
plugin is used to load all installed plugins that inherit from docs_package
(excluding docs_package
itself). It is designed for scenarios where multiple docs_package
plugins are discovered and installed in a site with CI/CD. This allows new documentation packages to be automatically added to the site upon publishing, without requiring changes to the MkDocs configuration.
Configuration
enabled
- boolean setting that allows the plugin to be disabled while keeping the rest of the configuration intact.packages
- dictionary where the key is the name of the plugin that inherits fromdocs_package
, and the value is the configuration override for that plugin.
Creating Packages
Docs Package
Docs package may be created from directory with mkdocs-partial package
CLI command:
usage: mkdocs-partial package [-h] [--source-dir SOURCE_DIR]
[--package-name PACKAGE_NAME] --package-version
PACKAGE_VERSION
[--package-description PACKAGE_DESCRIPTION]
[--output-dir OUTPUT_DIR] [--exclude EXCLUDE]
[--freeze] [--directory DIRECTORY]
[--title TITLE]
[--blog-categories BLOG_CATEGORIES]
[--edit-url-template EDIT_URL_TEMPLATE]
options:
-h, --help show this help message and exit
--source-dir SOURCE_DIR
Directory to be packaged. Default - current directory
--package-name PACKAGE_NAME
Name of the package to build. Default - normalized
`--directory` value directory name.
--package-version PACKAGE_VERSION
Version of the package to build
--package-description PACKAGE_DESCRIPTION
Description of the package to build
--output-dir OUTPUT_DIR
Directory to write generated package file. Default -
`--source-dir` value directory name.
--exclude EXCLUDE Exclude glob (should be relative to directory provided
with `--source-dir`
--freeze Pin doc package versions in requirements.txt to
currently installed. (if there is no requirements.txt
in `--source-dir` directory, has no effect)
--directory DIRECTORY
Path in target documentation to inject documentation,
relative to mkdocs `doc_dir`. Pass empty string to
inject files directly to mkdocs `docs_dir`Default -
`--source-dir` value directory name
--title TITLE Title override for package root index.md
--blog-categories BLOG_CATEGORIES
`/` separated list of categories to be prepended to
defined in blog posts of the package. Empty by default
--edit-url-template EDIT_URL_TEMPLATE
f-string template for page edit url with {path} as
placeholder for markdown file path relative to
directory from --docs-dir
The result of executing this command is a Python wheel package that contains:
- All the content from the directory specified by the
--source-dir
option, included as resources. - A plugin that inherits from
DocsPackagePluginConfig
, with the default value for thedirectory
configuration option set to the value provided by--directory
. - An entry point for MkDocs plugin discovery, with a name matching the
--package-name
option.
Sample
mkdocs-partial package --package-name my-docs-package --package-version 0.1.0 --source-dir ".\docs" --output-dir ~/packages --directory my-docs
docker run -v ./docs:/docs -v .:/packages exordis/mkdocs-partial package --package-name my-docs-package --package-version 0.1.0 --output-dir /packages --directory my-docs
will create package ~/packages/my_docs_package-0.1.0-py3-none-any.whl
with package named my-docs-package
that may be added to mkdocs config with
site_name: "Docs Package Demo"
plugins:
- my-docs-package
and inject content to /my-docs
of the site (unless overridden within mkdocs.yml
)
If packaged directory contains requirements.txt
, built package will have dependencies it defines.
Site Package
Site package is package with mkdocs config and overrides that is to be shared or accumulate all docs packages for deployment.
It may be built with mkdocs-partial site-package
CLI command
usage: mkdocs-partial site-package [-h] [--source-dir SOURCE_DIR]
[--package-name PACKAGE_NAME]
--package-version PACKAGE_VERSION
[--package-description PACKAGE_DESCRIPTION]
[--output-dir OUTPUT_DIR]
[--exclude EXCLUDE] [--freeze]
options:
-h, --help show this help message and exit
--source-dir SOURCE_DIR
Directory to be packaged. Default - current directory
--package-name PACKAGE_NAME
Name of the package to build. Default - `--source-dir`
value directory name.
--package-version PACKAGE_VERSION
Version of the package to build
--package-description PACKAGE_DESCRIPTION
Description of the package to build
--output-dir OUTPUT_DIR
Directory to write generated package file. Default -
`--source-dir` value directory name.
--exclude EXCLUDE Exclude glob (should be relative to directory provided
with `--source-dir`
--freeze Pin doc package versions in requirements.txt to
currently installed. (if there is no requirements.txt
in `--source-dir` directory, has no effect)
The built package will:
- Include all content from the directory specified by
--source-dir
as resources. - Include dependencies listed in
requirements.txt
. - Provide a CLI entry point named after
--package-name
, which can be used to launch MkDocs.
Site Package CLI
Serve Documentation Locally
The serve
command launches mkdocs serve
with options to specify:
-
Override for any installed docs package resource directory:
The option--local-docs my-docs-package=./my-package/docs::my-package
instructs themy-docs-package
to inject files from./my-package/docs
instead of its default resources and usemy-package
as inject site directory instead of the one configured for plugin.Parts for docs path and directory are optional:
--local-docs my-docs-package=./my-package/docs
will keep configured directory.--local-docs my-docs-package
will inject files from/docs
path keep configured directory. -
Inject path which does not have plugin configuration in site resources If plugin referenced by
--local-docs
is not configures, configuration with provided path (fallback to/docs
) and directory (fallback to root of the site) will be created -
Override the site root directory:
The option--site-root ./site
directs the site package to load the MkDocs configuration and overrides from./site
rather than its default resources.
These overrides are particularly useful for documentation editing. When the site package is installed with all its associated docs packages, one of the docs packages can be pointed to a local directory, such as a Git repository, allowing real-time editing. As documentation changes are made, the results are immediately available at https://127.0.0.1:8000
in the full site context. Similarly, with the --site-root
option, the site configuration can be adjusted locally to observe its effects on the site in real time with all docs packages installed.
usage: [package-name] serve [-h] [--local-docs LOCAL_DOCS] [--site-root SITE_ROOT]
options:
-h, --help show this help message and exit
--local-docs LOCAL_DOCS
loads local directory as `docs_package` plugin content. Format <plugin name>[=<docs_path>[::<directory>]]. If `docs_path` is not provided `/docs` is
used as default. If plugin is configured within site mkdocs.yml `directory` overrides corresponding plugin config option. If plugin not configured
within site mkdocs.yml, it is added to config
--site-root SITE_ROOT
loads local directory as site `docs_dir` instead of the content packed with site package
All standard arguments for mkdocs serve
can be passed as well. For example, the server’s port and address can be changed using --dev-addr
, and --strict
can be used to trigger a failure on any warning.
Build Static Documentation
The build
command launches mkdocs build
, with the same overrides available as for the serve
command.
usage: [package-name] build [-h] [--local-docs LOCAL_DOCS] [--site-root SITE_ROOT]
options:
-h, --help show this help message and exit
--local-docs LOCAL_DOCS
loads local directory as `docs_package` plugin content. Format <plugin name>[=<docs_path>[::<directory>]]. If `docs_path` is not provided `/docs` is
used as default. If plugin is configured within site mkdocs.yml `directory` overrides corresponding plugin config option. If plugin not configured
within site mkdocs.yml, it is added to config
--site-root SITE_ROOT
loads local directory as site `docs_dir` instead of the content packed with site package
Export Site Resources
The dump
command exports the site's resources to a specified directory. For example, the content can be dumped to ~/site
, and the site can then be served using serve --site-root ~/site
to test configuration changes locally.
usage: [package-name] dump [-h] [--output OUTPUT]
options:
-h, --help show this help message and exit
--output OUTPUT Output directory. Default - Current directory
Real World Use Cases
Consider a scenario where an organization needs to maintain the documentation for two products. The setup for repositories on GitLab or GitHub might look like this:
site-company-documentation
- holds
mkdocs.yml
andrequirements.txt
listing all docs packages (in advanced scenariorequirements.txt
is generated during CI with iterating all repositories with gitlab/github API ). - has
partial_docs
plugin loaded withmkdocs.yml
to automatically load all documentation packages - CI builds this repos with
mkdocs-partial site-package
, installs built package and publishes results ofsite-company-documentation build
- holds
docs-company-documentation
- Holds documentation related to company context - home page of the site, contacts etc.
- Docs package is referenced in
requirements.txt
ofsite-company-documentation
without version constraint to grab the latest. - CI builds this repository with
mkdocs-partial package --directory ""
to inject docs to the root of the site - CI publishes docs package to company pypi registry and triggers
site-company-documentation
rebuild
product-a
- holds code for product A and has
docs
directory with documentation. - CI builds product and documentation for it with
mkdocs-partial package --package-name docs-project-a --directory ProdcutA
(docs are injected to directoryProdcutA
of the site) docs-project-a
package is referenced inrequirements.txt
ofsite-company-documentation
without version constraint to grab the latest.- CI publishes
docs-project-a
package to company pypi registry and triggerssite-company-documentation
rebuild
- holds code for product A and has
product-b
- holds code for product A and has
docs
directory with documentation. - CI builds product and documentation for it with
mkdocs-partial package --package-name docs-project-b --directory ProdcutB
(docs are injected to directoryProdcutB
of the site) docs-project-b
package is referenced inrequirements.txt
ofsite-company-documentation
without version constraint to grab the latest.- CI publishes
docs-project-b
package to company pypi registry and triggerssite-company-documentation
rebuild
- holds code for product A and has
Note
Injecting only release versions of packages to the site, verification of documentation with --strict
and similar aspects are skipped for clarity
As a result:
- The site configuration is separated from the documentation and can have a different maintainer.
- Updating any documentation package will automatically update the site.
- Each documentation package has its own maintainer.
- Product documentation is updated alongside the code, ensuring consistency.
- Documentation package maintainers can write and test documentation in the context of the full company site.
For example maintainer of ProductA documentation may do the following to start editing documentation
# Clone ProductA repository
git clone [repo with ProductA]
# Create and activate virtual environment
python3 -m pip install virtualenv
python3 -m venv env
source env/bin/activate
# Install latest version of company documentation site
python3 -m pip install site-company-documentation
# Launch it with `docs-project-a` package taking content from local folder
site-company-documentation serve --local-docs docs-project-a=./docs
it will start serving company documentation site on http://127.0.0.0:8000 with content for ProductA taken from local folder ./docs
.
Tip
site-company-documentation
CI may have additional step to build docker image from site, it would make things even simpler for documentation writer as all she'd need is launching docker with something like
docker run --pull always --rm \
-p 8000:8000 \
-v ./docs:/docs \
[docker image] serve --local-docs docs-project-a=./docs