Bridge project and docs builds¶
The starter pack can be used as a standalone docs repository, or embedded inside a parent project. This guide demonstrates how to bridge the docs build with a Python project’s main build. Once bridged, project contributors can install, build, and check the docs from the root of the project with the main build system.
Parent projects and the build describes the full benefits of bridging the build in a larger project.
Plan and requirements¶
The bridge is built by making up to three changes to the build.
The bridge shims the docs build targets in the main build. Any build system is
capable of adding targets that call other systems. When shimmed, the docs targets like
html and clean pass through to docs/Makefile, with arguments.
The bridge also merges the virtual environments, removing the need for a separate docs environment. This change is optional but recommended. To combine environments, your project must provide Python 3.11 or higher to the starter pack. Any Python dependency manager will do, and this guide illustrates with three:
pip 25.1 and higher
uv 0.4.27 and higher
Poetry 1.2.0a2 and higher
After adding the bridge, it’s also possible to adjust the documentation workflows to use your project’s main build. The workflows were designed with Make, so they only work if you use it for your build system.
Example project layout¶
This guide illustrates the bridge through an example project. In the example project, the file tree contains:
Project
│
├── ...
├── docs
│ └── Makefile
├── .venv
├── Makefile
└── pyproject.toml
The example project’s root Makefile has two conventional targets that need
adjustment:
setupfor building the virtual environment and syncing dependenciescleanfor cleaning up the virtual environment and temporary files
Set the build paths¶
Where your main build sets environment variables, redeclare the docs environment variables that specify the build paths:
BUILDDIRis the destination for the docs. If you have special distribution needs you can override this, but for most builds this can be left as-is.VENVDIRis the virtual environment of the docs. If you’re merging the virtual environments, set this as a relative path from the docs directory to your project’s virtual environment.VALEDIRis the path to the Vale binary. The full path depends on the location of your virtual environment, so it’s best to copy this as-is.
In the example project, this looks like:
# Env vars for the docs build
export BUILDDIR ?= _build
export VENVDIR ?= ../.venv
export VALEDIR ?= $(VENVDIR)/lib/python*/site-packages/vale
Integrate the docs setup¶
The next step is to incorporate the docs installation target, and optionally the dependencies, into the main build.
Separate virtual environments¶
If you don’t plan to merge the virtual environments, override the installation target by calling all three doc installation targets in a row.
In the example project, this is written as:
# Override the
.PHONY: docs-install
docs-install:
$(MAKE) -C docs install --no-print-directory
$(MAKE) -C docs vale-install --no-print-directory
$(MAKE) -C docs pymarkdownlnt-install --no-print-directory
Merged virtual environments¶
To merge virtual environments, you make the main setup target handle both
development and docs packages, and enumerate all docs packages in pyproject.toml.
By adding dependency groups, the docs packages, plus any custom Sphinx extensions, can
be managed by the main build and stored in one virtual environment. The result will be
three dependency groups in pyproject.toml:
devfor development buildsdocsfor extra docs packages that your project needsdocs-starter-packfor the core docs packages set by the starter pack
First, add these dependency groups, and make the docs dependencies include the starter pack packages:
[dependency-groups]
dev = [
# Packages for main development and testing
]
docs = [
# Packages for extra docs features
{include-group = "docs-starter-pack"},
]
docs-starter-pack = [
# Core docs packages
]
If you don’t already have a dev dependency group, review the packages listed in the
file’s dependencies key, then move any non-runtime dependencies to the dev
dependency group.
If your project needs extra docs features, like the Mermaid or LaTeX Sphinx extensions,
add their packages to the docs group.
Copy the contents of docs/requirements.txt into the docs-starter-pack group.
In the main build, override the docs installation target and make the project’s
setup target depend on it. In the example project, it is written like this:
.PHONY: setup
setup: docs-install
pip install --group dev
# ...
# Override for `install` target in docs project
.PHONY: docs-install
docs-install:
pip install --group docs
$(MAKE) -C docs vale-install --no-print-directory
$(MAKE) -C docs pymarkdownlnt-install --no-print-directory
.PHONY: setup
setup: docs-install
uv sync --group dev
# ...
# Override for `install` target in docs project
.PHONY: docs-install
docs-install:
uv sync --no-dev --group docs
$(MAKE) -C docs vale-install --no-print-directory
$(MAKE) -C docs pymarkdownlnt-install --no-print-directory
.PHONY: setup
setup: docs-install
poetry install --only dev
# ...
# Override for `install` target in docs project
.PHONY: docs-install
docs-install:
poetry install --only docs
$(MAKE) -C docs vale-install --no-print-directory
$(MAKE) -C docs pymarkdownlnt-install --no-print-directory
If your docs aren’t written in Markdown, remove the command that runs the
pymarkdownlnt-install target.
Shim the remaining targets¶
The docs build has many targets, but only a handful of them overlap or collide with most project builds, so we only need to override two more. The rest can pass straight through to the docs build.
In the example project, the main build calls the targets like this:
# Override for `clean` target in docs project. We don't want to touch `.venv`,
# so we pass a null dir instead.
.PHONY: docs-clean
docs-clean:
VENVDIR=null $(MAKE) -C docs clean --no-print-directory
# Override for `help` target
.PHONY: docs-help
docs-help:
@echo "Commands in the documentation subproject:"
$(MAKE) -C docs help --no-print-directory
@echo "Run these commands with 'make docs-<command>' in the project root."
# Shim for the rest of the targets in docs Makefile
.PHONY: docs-%
docs-%: docs-install
$(MAKE) -C docs $(@:docs-%=%) --no-print-directory
Adjust the Read the Docs build¶
With the Makefile in a different location than usual, and its being a separate process,
it’s simplest to override the Read The Docs build in .readthedocs.yaml to call the
same build targets that developers use locally.
If you use an uncommon system, you might need to install it during the workflow’s
create_environment job.
If you merged the virtual environments, make sure to set VENVDIR=${READTHEDOCS_VIRTUALENV_PATH} in all commands.
Here’s what it looks like in the example project:
build:
os: ubuntu-24.04
tools:
python: "3.12"
jobs:
post_checkout:
- git fetch --tags --unshallow # Also fetch tags
create_environment:
- python3 -m venv "${READTHEDOCS_VIRTUALENV_PATH}"
install:
- make docs-install VENVDIR="${READTHEDOCS_VIRTUALENV_PATH}"
build:
html:
- make docs VENVDIR="${READTHEDOCS_VIRTUALENV_PATH}" BUILDDIR="$READTHEDOCS_OUTPUT/html/"
Adjust the doc workflows¶
If your project uses the starter pack’s docs workflows and Make, adjust the workflows to use the bridged targets.
For the main checks, override the target names and paths through the workflow inputs:
jobs:
documentation-checks:
uses: canonical/documentation-workflows/.github/workflows/documentation-checks.yaml@main
with:
working-directory: "."
fetch-depth: 0
install-target: docs-install
spelling-target: docs-spelling
woke-target: docs-woke
linkcheck-target: docs-linkcheck
pa11y-target: docs-pa11y
If your docs are written in Markdown, override the path and command inputs in the Markdown linter workflow:
- name: Create venv
working-directory: "."
run: make docs-install
- name: Lint markdown
working-directory: "."
run: make docs-lint-md