Release process
A cerebro release is a git tag and a Codeberg release page. Nothing is published to
crates.io: all four crates are publish = false, they depend on each other by path,
and cargo package cannot resolve a path dependency from a registry. That is the
whole story in ADR-0012.
What a release looks like
main ──▶ just release ──▶ release PR ──▶ merge ──▶ CI publishes
bump + changelog reviewed tag + release page
+ lockfile
One version covers the whole workspace. All four crates inherit
[workspace.package] version, so one tag names one release: cerebro-v0.3.0.
Cutting one
just release-status # what Cargo.toml says vs. what the commits propose
just release # branch, changelog section, version bump, one commit
just release needs git-cliff (cargo install git-cliff --locked) and cargo. It writes
nothing to the forge and prints the two commands that follow:
git push -u codeberg release/cerebro-v0.3.0
fj pr create 'chore(release): cerebro-v0.3.0' --base main
One commit goes on that branch, carrying three files: CHANGELOG.md with the new
section, Cargo.toml with the version, and Cargo.lock re-resolved to match. All
four crates inherit [workspace.package] version, so the lock moves with the
manifest or it quietly disagrees with every release tag until someone’s build
rewrites it.
Read the changelog section in that diff. It is generated from commit subjects, and a subject that needs rewording is cheaper to fix in the release PR than in a published release page.
Once the PR merges, CI publishes on its own. The push to main leaves the manifest
ahead of the last tag, so scripts/release.sh publish posts one release with that
version’s changelog section as the body, and Codeberg creates the tag pointing at the
commit CI built. On any other push to main it prints “already released” and exits 0.
To see what CI would do without doing it:
just release-preview
Version numbers
Commit types decide the bump, since the last cerebro-v* tag:
| Commits since the last tag | Bump | Example |
|---|---|---|
fix: | patch | 0.2.2 → 0.2.3 |
feat: | minor | 0.2.2 → 0.3.0 |
feat!: or a BREAKING CHANGE: footer | major | 0.2.2 → 1.0.0 |
build:, ci:, chore:, style:, test: only | none | 0.2.2 → nothing to release |
Commit subjects follow conventional commits because they are the release notes. The
commit-msg hook runs cog verify, which enforces the format.
Releasing by hand
If CI is unavailable, the same API call works from a laptop. It is the exact request
publish makes:
version=$(bash scripts/release.sh version)
notes=$(bash scripts/release.sh notes "$version")
Do not git push a tag by hand. The forge cuts it when the release is created, which
keeps the tag, the release page, and the commit CI tested in agreement.
Tests
bash scripts/release-test.sh
Scratch git repositories, a stubbed curl, and assertions on every decision: which
line bump rewrites, what next proposes for each commit type, whether notes stops
at the next section, and the publish branches (no-op, publish once, stand down after
the tag exists, refuse without notes or a token). It runs in CI’s test step and inside
just test, so a broken release script cannot merge quietly.