Forges overview
Molt talks to your code host through a forge backend -- a seam that hides how a given host creates pull requests, publishes release notes, and attributes commits, so release automation is not welded to any one platform.
The reason for the seam
The parts of the release loop that touch a code host go through a backend protocol rather than calling GitHub directly:
- Pull request creation -- opening and updating the "Version Packages" release PR.
- Release notes -- turning a package's changelog entry into a host-native release.
- Commit and author attribution -- resolving which PR and which author a change came from, so changelogs can credit contributors.
GitHub is the backend molt ships. Because the protocol sits between the engine and the host, adding another one is writing a backend rather than changing the engine, and the core release loop already runs on any CI without a backend at all.
What a forge backend provides
A forge backend answers a small, host-agnostic set of questions:
- Attribution. Given a commit, which pull request introduced it, and who authored it? Given a pull request number, its author and merge commit? This is what lets changelog templates render
Thanks @author!and link back to the PR. - Release publication. Given an existing tag, a name and a changelog body, create a release on the host -- one per released package, with that package's changelog entry as the body.
- Pull request lifecycle. Find the open release PR for a branch and update it in place, or open a new one.
- Commit creation. Given a branch, a base commit, a message and a set of file additions and deletions, make that branch carry one commit holding those changes on top of that base.
The engine, the changelog generators, and the CI loop call these; they never assume the host is GitHub.
Two details of release publication worth knowing
A release the host already carries is not a failure. Creating a release for a tag that already has one reports "nothing created" instead of raising, so re-running a publish that half-failed completes the releases that are missing rather than failing on the ones that already went out.
The prerelease flag follows PEP 440. 1.0.0rc1, 1.0.0a1, 1.0.0b2, 1.0.0.dev1 and every molt version --snapshot version are published as prereleases. 1.0.0 and a post-release such as 1.0.0.post1 are not, and neither is a local version such as 1.0.0+local.build. If you are migrating from changesets, note that it reads the flag off a hyphen in the version string, and no PEP 440 prerelease contains one.
Two details of commit creation worth knowing
The seam says "commit", never "sign". A commit the host authors on its own server can be signed by that host, which is what makes commit-mode: api work on GitHub. Signing is a property of a particular backend's implementation, not a promise the seam makes -- a member named for it would be a GitHub-shaped hole in a host-neutral protocol. Every host molt names as a backend candidate has a multi-file commit endpoint that fits behind this shape.
File contents cross as bytes, keyed by path. Additions are a mapping from a repository-relative path to that file's raw bytes, and the backend applies whatever encoding its host wants on the wire -- base64 for GitHub, raw text behind an encoding flag for GitLab. Decoding to text at the seam would corrupt a file that is not UTF-8 and would silently translate line endings on a Windows checkout. A file the host's commit API cannot represent -- a symbolic link, an executable, a submodule -- is refused by name rather than committed as the wrong kind of file.
Two details of the pull-request lifecycle worth knowing
The release PR is looked up, not remembered. Molt keeps no state between runs, so the branch name is the whole identity: the lookup asks the host for the open pull request from changeset-release/<base> onto <base>, and the first match is the one updated. That is why the branch name is fixed rather than configurable.
Updating a PR also re-opens it. A force-push onto the release branch can close the pull request, and a closed one is not found by an "open" query. Molt therefore sets the state back to open on every update, so the next run keeps the same pull request -- with its number, its comments and its subscribers -- instead of opening a second one.
The CI action's release loop calls all of these: it finds or opens the release pull request during the version phase, and creates one release per package during the publish phase.
Caching and backoff
Both behaviors below live in the shared client, so every backend inherits them.
- A cache keyed by identity of the thing, not the object. The cache key is
(host, kind, repository, id), so a commit or a pull request is fetched once per release no matter how many changelog lines reference it. - Rate limits, retries, and backoff. Molt honors
Retry-After, retries transient5xxresponses with jittered backoff, sets an explicit timeout, and fails with an actionable message when the rate limit is exhausted, including when it resets.
On a large release that generates many changelog lines, this is the difference between one batch of API calls and a storm of throttled refetches.
See also
- GitHub -- the forge backend molt ships, in detail.
- GitLab, Gitea & others -- releasing from another host.
- CI: GitHub Action -- the release loop the forge backend drives.
- Changelog templates -- what author and PR attribution feed into.