Versioned documentation: design and rollout#
Added in version v0.2: The per-version switcher.json scaffolding lands with v0.2. The
companion changes to MICROROBOTICA’s umbrella conf.py + docs CI
are required to make the switcher live on the published site —
that work has since landed in MICROROBOTICA’s umbrella docs.
Settled design decisions#
These are the choices made for the v0.2 rollout. Captured here so that the rationale survives turnover; revisit them when the v0.3 docs cycle starts.
# |
Decision |
Choice |
Reason |
|---|---|---|---|
1 |
What does |
Most recent git tag (not main tip) |
New visitors land on stable; bleeding-edge work moves to |
2 |
Where does |
In each project’s repo ( |
Projects progress somewhat independently; each owns its release cadence. |
3 |
Version label convention |
|
Pins to semver; the |
4 |
|
Always the latest stable tag |
Standard PyData theme convention; what new readers should see by default. |
5 |
Does MICROROBOTICA + MIME get versioned too? |
Yes — all three |
MICROROBOTICA ships a C++ API that breaks compatibility on minor bumps; MIME’s nodes evolve alongside MADDENING. Each release tag freezes its own docs. |
6 |
CI build strategy |
Cache + only rebuild changed versions (option C from the brainstorm) |
Old tagged builds are immutable artefacts; rebuild only on tag-cut, cache forever. |
7 |
Old version’s incompatible |
For now, build all versions with the current umbrella |
Cheap until incompatibility actually bites; the pin-file pattern is a known fallback. |
8 |
Glossary across versions |
Per-version (frozen at release) |
Each version stays accurate to its source; readers searching old docs see the terms they actually shipped with. |
9 |
Switcher fallback when JSON unreachable |
Show a debug banner + console warning |
Hidden silent failures are the worst kind; make broken deploys visible immediately. |
10 |
URL shape for older releases |
Subdir ( |
Single TLS cert, single GitHub Pages host, fits the multiproject build pipeline. |
These shape the rollout described below. The
multiversion_preview.md
guide in MICROROBOTICA covers the local-iteration workflow that
implements them.
The three MSF projects (MADDENING, MIME, MICROROBOTICA) are
published from a single sphinx-multiproject build at
https://microrobotica.org/. Today that build emits a single
“latest” version of each project — there’s no v0.1 URL you can
deep-link to. When v0.2 lands as the new stable release, v0.1
documentation effectively disappears.
This page is the rollout plan for keeping every supported version addressable at a stable URL with a UI version picker.
The shape of the live site (target)#
https://microrobotica.org/ — MICROROBOTICA latest
https://microrobotica.org/v0.1/ — MICROROBOTICA v0.1
https://microrobotica.org/maddening/ — MADDENING latest (== current main)
https://microrobotica.org/maddening/v0.2/ — MADDENING v0.2 (stable)
https://microrobotica.org/maddening/v0.1/ — MADDENING v0.1
https://microrobotica.org/mime/ — MIME latest
https://microrobotica.org/mime/v0.2/ — MIME v0.2
/<project>/always points atlatest(= the most recent tagged release, notmain).maincontent lives at/<project>/dev/if someone wants pre-release docs.Each version is a frozen build of the docs in that release’s
docs/tree against the Sphinx config at that tag. Switching versions doesn’t switch just the content — the navbar, theme, and inventory all freeze together.
How a user sees it: PyData theme’s version switcher#
PyData Sphinx Theme ships a built-in version
switcher
that reads a JSON file at build time and renders a dropdown in the
navbar. We use it because the umbrella site already runs PyData
theme (MICROROBOTICA/docs/conf.py line ~70).
Each project ships its own switcher.json at
docs/_static/switcher.json, e.g. MADDENING's:
[
{"version": "latest", "name": "latest (main)",
"url": "https://microrobotica.org/maddening/"},
{"version": "v0.2", "name": "v0.2 (stable)",
"url": "https://microrobotica.org/maddening/v0.2/", "preferred": true},
{"version": "v0.1", "name": "v0.1",
"url": "https://microrobotica.org/maddening/v0.1/"}
]
The umbrella conf.py wires it into the theme:
html_theme_options.update({
"switcher": {
"json_url": f"{_BASEURLS[project]}_static/switcher.json",
"version_match": os.environ.get("DOCS_VERSION", "latest"),
},
"navbar_end": ["theme-switcher", "version-switcher", "navbar-icon-links"],
})
DOCS_VERSION is set by the CI build per tag — latest for the
main build, v0.2 for the v0.2.0 tag, etc.
How a build produces all the versions#
flowchart LR
Tags["git tags<br/>v0.1.0 · v0.2.0 · ..."] --> Matrix
Main["main branch"] --> Matrix
Matrix["docs.yml matrix:<br/>{ tag in tags, 'main' }"] --> Build
Build["checkout @ tag/main<br/>DOCS_VERSION=tag make all<br/>OUT=_build/html/{project}/{tag}/"] --> Merge
Merge["merge into root _build/<br/>aliases: latest → newest tag"] --> Deploy
Deploy["github-pages deploy"]
The umbrella Makefile already builds each project sequentially
(make all → _build/html/{maddening,mime,microrobotica}/). The
multi-version extension is:
Loop over
git tag --list 'v*'.For each tag,
git worktree add /tmp/wt-$tag $tag, thencd docs && DOCS_VERSION=$tag make all OUTDIR=_build/html-$tag/.For
main, the build is the same but withDOCS_VERSION=latest.After all builds, symlink
_build/html/latest → _build/html-$latest_tag/and copy each_build/html-$tag/into_build/html/{project}/$tag/.Each project’s root
switcher.jsonlives at_build/html/{project}/_static/switcher.jsonand is the same file in every version’s output (so the dropdown lists every version regardless of which one the user is currently on).
Why this design over alternatives#
Option |
Pros |
Cons |
Verdict |
|---|---|---|---|
sphinx-multiversion |
Automatic git-tag enumeration; single command. |
Each historical tag must build with the current |
Rejected — we expect |
Read-the-Docs |
Hosted, automatic. |
Not the umbrella architecture; would need a separate domain per project. |
Rejected — keep the unified microrobotica.org URL. |
Manual switcher.json + CI matrix (chosen) |
Each version builds against its own |
Switcher JSON has to be updated for every release. |
Chosen. |
Single-version + 404 page |
Cheapest. |
Loses backwards-discoverable docs. |
Rejected — v0.1 users still need their docs. |
Rollout sequence#
Note
The first three steps are MADDENING-side; the rest require coordinated changes on MICROROBOTICA’s umbrella repo.
(this commit) —
docs/_static/switcher.jsonin each project listslatest+ every released tag. Today only the v0.2 row has a target (we haven’t built v0.1 with this infra yet); v0.1 resolves to a 404 until step 4.(this commit) — Document the design in this page so the handoff is in version control, not in someone’s head.
(MICROROBOTICA umbrella PR) — add
switcher+navbar_endtohtml_theme_optionsinconf.py. SetDOCS_VERSIONfrom env var withlatestdefault.(MICROROBOTICA umbrella PR) — extend
Makefilewith aversionstarget that loops overgit tagper project and builds each into the right subdirectory.(MICROROBOTICA docs.yml) — switch the workflow from “build once” to “matrix of (project, tag), then merge into a single artifact”. Cache builds aggressively because most tags won’t change between deploys.
(release process) — every release adds a new entry to its project’s
switcher.jsonand a new git tag. The next CI run picks it up automatically.
How a developer triggers a version-specific local build#
Until the CI changes land, the per-version path is manual:
# Build v0.2 docs from a checkout at the v0.2.0 tag
git worktree add /tmp/maddening-v0.2 v0.2.0
cd ../MICROROBOTICA/docs
DOCS_VERSION=v0.2 make maddening # → _build/html/maddening/
# Same for main → _build/html-main/
git worktree add /tmp/maddening-main main
DOCS_VERSION=latest make maddening
The switcher will appear in the navbar; clicking the v0.1 entry 404s until v0.1 is built into the right subdir.
What goes in a new release’s switcher.json#
When cutting a new release N:
Update
docs/_static/switcher.json:Add a new entry for version N marked
"preferred": true.Flip the previous stable’s
"preferred"tofalse.latestalways points at/<project>/(the unversioned URL, which the CI maps to the newest tag).
Tag the release.
Push.
CI picks up the new tag, builds it into the right subdir, and the switcher picks it up automatically on the next umbrella build.
Open questions#
MIME’s switcher.json lives at
MIME/docs/_static/switcher.jsonand is authored as of the v0.2 cycle (singlelatestentry today; populates per-version rows as MIME’s own release cadence kicks in).MICROROBOTICA’s switcher is also authored at
MICROROBOTICA/docs/_static/switcher.json— same scaffolding, same per-project release lifecycle.Do we want a
dev/alias? Probably useful for users tracking unstable APIs. Easy add:/dev/always maps to the most recentmainbuild alongside the tag-pinned versions.