Releases¶
Every ModernLeft repository releases the same way. Versioning is driven
entirely by Conventional Commits and
commitizen, following the
release architecture commitizen-tools uses for commitizen itself. There is no
release button and no release branch. Landing an eligible commit on main is
the release decision.
What cuts a release¶
cz bump runs after every push to main and inspects the commits since the
last tag. A release happens only when at least one bump-eligible commit
landed.
| Commit type | Effect on the version |
|---|---|
BREAKING CHANGE footer or ! |
major bump |
feat |
minor bump |
fix, refactor, perf |
patch bump |
docs, chore, ci, build, test, style |
none |
Inert commits accumulate silently and ship as part of the next eligible
release. Renovate commits its dependency updates as ci(deps) (workflow and
action updates) or build(deps) (everything else), so automated dependency
merges never cut a release on their own.
Developer workflow¶
- Branch off
mainand work normally. Commit messages follow Conventional Commits. Nothing you do locally can cut a release. - Open a pull request. CI runs the quality checks and tests, and the bump preview bot posts a sticky comment stating exactly which release a merge would produce. The comment updates as you push.
- The commit types are the release lever. If the PR should ship immediately,
make sure an eligible type (
feat,fix,refactor,perf) lands with it. If it should stay silent, keep everythingdocs/chore/ci/build/test. - Merge (or push an eligible commit straight to
main). That is the entire release ceremony: tests run and the bump commit and tag are pushed. The publish workflow fires from the tag. There is no second approval step. - If nothing eligible landed, the bump is a no-op and your changes ride along with the next eligible release.
- If a publish run fails, re-run it for that tag via
workflow_dispatch. Do not create a new version to retry a publish.
The pipeline¶
Three workflows per repository, chained by events rather than by jobs:
- Test & Version (
build-version.yaml) runs on every push tomain. The test job is the post-merge CI check. When it is green, the reusable bump workflow from commitizen-ci runscz bump. If a release is due, it writes the changelog and creates a signed bump commit plus an annotated tag, then pushes both atomically withgit push --follow-tags. If nothing is eligible, the run ends as a no-op. - Publish (
publish.yaml) triggers on the tag push. Container image repos build and push the image with the version tag andlatest. Library repos upload the package to the Forgejo package registry (PyPI or npm). - PR bump preview (
pr-bump-preview.yaml) keeps one sticky comment on every pull request stating exactly which release a merge would produce, so the consequence is visible before the merge button is clicked.
The bump commit itself starts with bump: and is skipped by every workflow,
which prevents release loops.
Versioning mechanics¶
- Versions are semver. Tags are the bare version (
1.4.0, novprefix). - The version source of truth is the repo's native metadata:
pyproject.tomlfor Python (version_provider = "uv") orpackage.jsonfor TypeScript ("npm"). Repos without package metadata use git tags alone ("scm"). - Bump commits and tags are SSH-signed by the
commitizen-botaccount. The signing key is pulled from Infisical at run time, so no per-repo secret setup is needed for org repositories. - The bump workflow pushes the commit and the created tag in one atomic
push, so a partial release (commit without tag) cannot happen. Tags are
annotated (
annotated_tag = truein the cz config templates) by convention, but the workflow does not require any particular tag settings.
Republishing a failed release¶
Publish workflows accept a workflow_dispatch with a tag name. This rebuilds
and republishes that exact tag without creating a new version and without
moving latest. Use it when the original tag-triggered run failed for
environmental reasons.
Adopting the system in a repository¶
The repo needs the workflow callers and a commitizen config with
annotated_tag = true. Conventional commit discipline covers the rest. The
commitizen-ci README
documents the reusable workflows, the caller snippets, and the setup for
repositories outside the org (bring your own signing key). Org repositories
get the Infisical credentials automatically.