Custom generators
Write a changelog generator as a small Python package, register it as a molt.changelog entry point, and point your config at it by name.
This is the hands-on companion to Changelog plugins, which covers the contract and the plugin seam in the abstract. Here we build one from an empty directory to a working molt version. The example produces categorized, emoji-prefixed release lines rendered with a small Jinja2 template.
When you need a generator (and when you do not)
A generator controls the text of each changelog line -- how a single changeset's summary becomes a bullet, and how a dependency bump is phrased. You need one when you want to change that text: link to a different forge, add attribution, prefix a category emoji, or reshape the bullet.
If you only want to change the structure around the lines -- the version heading, section titles, dates, ordering -- you do not need a generator at all. That is a changelog template, a Jinja2 file with no code. Reach for a generator only when the per-line text is what you want to change.
1. Lay out the package
A generator is an ordinary distribution. The smallest useful layout:
2. Implement the contract
A generator provides get_release_line and get_dependency_release_line. molt owns the sections and spacing, so each function returns just the text for one bullet. This example buckets by a category key in the changeset front matter and renders each line through a Jinja2 template.
The template is plain Jinja2:
A few things this example demonstrates:
- molt injects the forge.
forgeis molt's cached, rate-limited GitHub adapter when GitHub is active, andNoneotherwise -- so the same generator degrades gracefully off a forge instead of crashing or opening its own client. - The forge hands back links, not ids.
forge.commit_info(sha)returnsNonewhen the forge has nothing for that commit, and otherwise an object with three parts:info.commit,info.authorandinfo.pull. Each carries.urland a ready-made.markdown_link, so a generator never builds a URL itself -- that is what keeps the same generator working against a forge other than GitHub.info.commitis always present;info.authorandinfo.pullcan beNone. - You never touch the file. No reading
CHANGELOG.md, no regex insertion, no blank-line juggling. You return one bullet's text; molt places it in the right### Major/Minor/Patch Changessection, keeps dependency bumps last in the patch section, and clamps the spacing. molt emits correct Markdown directly -- there is no formatter pass to repair it afterward. - Summaries are passed through literally. molt does not run your changeset summary through a template engine or strip Markdown from it, so a
#heading or a$1in a summary survives intact. (Both are upstream bugs molt refuses to port -- see Design decisions.)
3. Register the entry point
Declare the molt.changelog entry point in the generator's pyproject.toml. The name on the left is what config will reference; the value on the right is module:object.
4. Install it and point config at it
Install the generator into the same environment as molt, then reference it by name:
The options table ({ repo = "acme/widgets" }) arrives as the options argument, verbatim. The next molt version uses your generator with no further wiring.
Entry points, not shell hooks
molt extends only through named entry points like this one -- never through an arbitrary shell command in config. That is a deliberate refusal, not a missing feature:
- A shell
postversionhook makes dependency-bump and changelog semantics undefined -- molt cannot reason about what the hook did to the tree. - Executable config (a
changelog.pythat runs on parse) means no tool can read your settings without running your code -- it breaks static analysis, editor tooling, and any bot that inspects the repo.
A typed, importable generator gives you the same power with none of those costs: it is discoverable, testable in isolation, versioned as a dependency, and inert until molt calls it. See Why molt for the full list of what molt refuses and why.
Where to go next
- Changelog plugins -- the contract and resolution rules in full.
- Changelog templates -- change entry structure without writing code.
- GitHub -- the forge adapter your generator receives.
- Options reference -- the
changelogoption.