The Sphinx extension#
moneta_sphinx renders the changelog into a Sphinx site. It runs
moneta render --format json at build time and turns the result into docutils
nodes, so the page is rebuilt from the tags on every build rather than from a
file somebody remembered to update.
# conf.py
extensions = ["myst_parser", "moneta_sphinx"]
```{moneta-changelog}
```
That is the whole setup for a docs/ directory inside the repository being
documented: with no moneta_repo set, the extension walks up from the source
directory to the enclosing checkout.
Directive options#
Option |
|
|---|---|
|
|
|
render only these kinds, comma- or space-separated |
|
render only entries carrying one of these scopes |
|
|
|
leave out the Unreleased section |
|
read a different repository, relative to the source directory |
Filtering happens after moneta has run, not as flags to it, so a site with several changelog pages — one per scope, say — is still one subprocess.
:heading-depth: is a depth rather than a level because the level the headings
come out at is decided by where the directive sits in the page, which docutils
resolves and the extension cannot. What it controls is how many of the two
generated levels are real headings:
2
: A section per version and a section per kind. Everything is linkable and
everything is in the sidebar.
1
: A section per version; the kinds become rubrics. This is what a project with
fifty releases wants, rather than three hundred sidebar entries.
0
: No headings at all, for embedding the changelog under a page’s own. The
version anchors still exist, so cross-references keep working.
Cross-references#
Every rendered version is an object in the moneta domain:
The fix shipped in {moneta:version}`v1.2.0`.
The tag prefix is optional — {moneta:version}`1.2.0` resolves too — since
which form is the version’s name is a property of the project’s tag-prefix
and prose should not have to know it. With nitpicky = True, a reference to a
release the site no longer renders fails the build.
Configuration#
Value |
Default |
|
|---|---|---|
|
|
the binary to run |
|
|
the repository to read; relative paths resolve against the source directory |
|
|
read this file instead of running moneta |
|
|
extra arguments for |
|
|
|
|
|
the default for |
Entry bodies are Markdown, so the default parses them with MyST; each body is
parsed into a document of its own rather than through the host page’s state
machine, which on a Markdown page would leave anything generated as literal
text. A project whose fragments are reStructuredText sets
moneta_body_parser = "rst".
moneta_json is the escape hatch for a build that cannot run git — a hermetic
package build, or a shallow CI checkout, where moneta would correctly refuse.
Render the document in a job that has the full history and carry the file into
the one that does not.
Incremental builds#
Rebuilding the changelog page on every build is wasteful; not rebuilding it when it has changed publishes a stale one. Two mechanisms, because neither is enough alone:
Every fragment file and
.moneta.yamlis a dependency of the page, so editing an entry rereads it the way editing any source file does.Creating a tag changes which release an entry belongs to without touching any file, which no dependency can catch. So the extension also hashes the document on
env-get-outdatedand compares it with what the page last rendered.
Watching .git/HEAD and .git/packed-refs as dependencies was the obvious
alternative, and it is wrong often enough to matter: a ref may be loose or
packed, and .git is a file rather than a directory inside a worktree.
The hash deliberately leaves out repo.head, so an ordinary commit — one that
adds no entry and no tag — rebuilds nothing.
Failing loudly#
A changelog that renders empty, or short by a release, looks exactly like a correct one. Nobody reading the page can tell. So each of these stops the build rather than producing a page:
monetaexiting non-zero, including its refusal to read a shallow clone. The child’s stderr is reported verbatim.a
schema_versionthe extension was not written for.:versions:naming a release that is not there.moneta_body_parser = "myst"withmyst_parsernot loaded.