Documentation publishing¶
The documentation pipeline treats the Markdown files under docs/ as the canonical
source. One command produces a searchable site, standalone handbook, PDF, man pages,
checksums, and a deterministic archive.
Quick commands¶
Build every format:
Build and validate every format:
Build only the static site, as used by the Pages workflow:
Preview the static site while editing:
Remove generated documentation:
Output layout¶
A successful build creates:
dist/docs/
├── site/ # searchable MkDocs site
├── man/
│ ├── certify-reverse.1
│ ├── certify-reverse.1.gz
│ ├── caddy-docker.1
│ └── caddy-docker.1.gz
├── certify-reverse-handbook.html # standalone embedded-resource HTML
├── certify-reverse-handbook.pdf # A4 PDF
├── build-manifest.json # tool versions, sizes, SHA-256 hashes
└── certify-reverse-docs.tar.gz # deterministic archive, including manifest
The static site can be copied to any ordinary web server. It has no server-side runtime requirement.
Toolchain model¶
The pipeline has two layers.
Pinned Python layer¶
MkDocs and MkDocs Material dependencies are compiled into:
The lock contains exact package versions and hashes. The build script creates a
dedicated environment below .cache/docs/venv and synchronizes it with the pinned
uv version. It recreates the environment automatically when its Python major/minor
version no longer matches toolchain.toml.
Refresh the lock only as an intentional maintenance change:
Review the complete lock diff and rebuild every output before committing it.
Pinned renderer layer¶
Expected renderer versions are recorded in:
The current pipeline pins:
- Python and
uvfor the isolated MkDocs environment; - Pandoc to assemble the handbook and render man pages;
- WeasyPrint to print the standalone handbook HTML to A4 PDF;
- groff as the local man-page inspection tool.
The build fails when uv or renderer versions drift. For an intentional local experiment,
you may set:
Do not use that override for a release build. Update toolchain.toml, inspect the
rendered changes, and commit the version change instead.
How the formats are built¶
Markdown sources
|
+-- mkdocs.yml + MkDocs Material
| -> multi-page static site + search index
|
+-- tools/docs/handbook.toml + Pandoc
| -> one standalone HTML handbook
| -> internal Markdown links rewritten to chapter anchors
|
+-- handbook HTML + WeasyPrint
| -> A4 PDF with page numbers and print typography
|
+-- docs/reference/*.1.md + Pandoc man writer
-> roff man pages + reproducible gzip files
tools/docs/handbook.toml defines chapter order. A page can be part of the static
site but excluded from the printed handbook, or vice versa, by changing this list.
Add a new documentation page¶
- Create the Markdown file below
docs/. - Add it to
navinmkdocs.yml. - Add it to
tools/docs/handbook.tomlwhen it belongs in the PDF handbook. - Link it from the nearest overview page.
- Build with strict validation:
MkDocs strict mode rejects broken internal links, missing snippets, and many configuration mistakes.
Write beginner-friendly pages¶
Each task-oriented page should answer these questions in order:
- What will I achieve?
- What do I need before starting?
- What exact command do I run?
- What should successful output look like?
- What does the command change?
- How do I verify the result independently?
- How do I undo or clean it up?
- Where do I go when it fails?
Prefer complete examples over fragments. When a command contains placeholders, list every placeholder immediately below the block.
Use admonitions consistently:
!!! note
Additional context that is useful but not urgent.
!!! tip
A simpler or safer workflow.
!!! warning
A mistake that can break setup or expose data.
!!! danger
A destructive action such as deleting volumes.
Avoid hiding required information in an admonition.
Code example rules¶
- Examples must use fake tokens and non-sensitive domains.
- Commands should run from the repository root unless the page says otherwise.
- Shell snippets should use
set -euwhen saved as scripts. - Compose examples should validate with
docker compose config --quiet. - YAML examples should use spaces, never tabs.
- Show cleanup commands for every tutorial stack.
- Do not recommend
curl -kas a normal final state. - Explain whether an HTTP error means unreachable, unhealthy, or merely unauthorized.
Man page sources¶
The source files are:
They contain @VERSION@ and @DATE@ placeholders. The build script replaces those
from pyproject.toml and the source date before invoking Pandoc.
Preview generated pages:
Inspect raw roff when debugging:
PDF review checklist¶
After a documentation release, inspect at least:
- cover/title and table of contents;
- first and last page numbers;
- code blocks near page boundaries;
- wide tables;
- long URLs and file paths;
- admonitions and block quotes;
- chapter page breaks;
- non-ASCII characters such as arrows and en dashes.
The build validates the PDF signature and file presence, but visual review remains necessary for typography and pagination.
Reproducibility¶
Set SOURCE_DATE_EPOCH to control the handbook date and archive metadata:
The generated archive uses zeroed timestamps, numeric ownership, and stable path
ordering. build-manifest.json records renderer versions and SHA-256 hashes for all
other outputs and is itself included in the archive.
Release integration¶
The main release gate calls the documentation check:
A release is not ready when:
- the site cannot build in strict mode;
- the search index is missing;
- the PDF is absent or malformed;
- either man page lacks its roff header;
- the dependency lock and environment differ;
- the pinned Python,
uv, or renderer versions differ fromtoolchain.toml; - the documentation archive omits
build-manifest.json; - documentation examples fail their syntax or Compose checks.