feat(standards): add versioning + changelog standard, release templates, and Drone version guard

Add lib page 11 (SemVer contract, pre-1.0 rule, tag==version invariant, Keep a Changelog), the releasing wiki runbook, and canonical release templates (CHANGELOG.md, VERSION). Vendor them into the MCP bundle as templates/release + release.json and expose registry://release with list/get tools. Add a changelog component to the app template. Enforce the tag<->version contract in both Drone shapes: Type A check-version and the backfill loop verify pyproject version; Type B adds a VERSION guard step.
This commit is contained in:
2026-10-15 01:24:25 +05:30
parent a754366901
commit 8cc9631fe7
25 changed files with 625 additions and 10 deletions

View File

@@ -0,0 +1,107 @@
# Releasing & Versioning
How to cut a release of any Aetos repository. The rule lives in the reference
page [Versioning & Changelog](../lib/11_versioning_and_changelog/); this is the
runbook you follow.
Every repository is one of two Drone shapes — a **PyPI package** or a **Docker
deployment** ([Drone Pipelines](../lib/10_drone_pipelines/)). Both are
released the same way: bump the version file, promote the changelog, tag.
---
## 🧾 Before you start
- `main` is green (the Drone quality gate passed on the last run).
- You know whether this is a **breaking**, **feature**, or **fix** release.
- The version file exists:
- package → `version` in `pyproject.toml`
- service / site → `VERSION` at the repo root
---
## 🚦 Pick the next version
The version file and the tag must be identical. For `0.y.z`, `0.y` is the major
line (see the reference page); once at `1.0.0`, normal SemVer applies.
| Change | `0.y.z` → | `1.0.0+` → |
|--------|-----------|------------|
| Breaking | `0.(y+1).0` | `(x+1).0.0` |
| Feature | `0.y.(z+1)` | `x.(y+1).0` |
| Fix | `0.y.(z+1)` | `x.y.(z+1)` |
---
## ✍️ Promote the changelog
In `CHANGELOG.md`:
1. Rename the content under `[Unreleased]` into a new
`## [x.y.z] - YYYY-MM-DD` heading (keep the sections that apply).
2. Leave a fresh, empty `## [Unreleased]` on top.
3. Add the compare links at the bottom:
```markdown
[Unreleased]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.z...HEAD
[x.y.z]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.(z-1)...x.y.z
```
---
## 🔢 Bump the version file
- **Package:** edit `version = "x.y.z"` in `pyproject.toml`.
- **Service / site:** edit the `VERSION` file.
Then commit and tag:
```bash
git add CHANGELOG.md pyproject.toml # or VERSION
git commit -m "chore(release): x.y.z"
git tag x.y.z
git push origin HEAD
git push origin x.y.z
```
---
## 🤖 What Drone does
- **Type A (package):** asserts `tag == pyproject version`, then skips if that
version already exists on the index, runs the quality gate, builds, and
uploads the wheel + sdist.
- **Type B (service):** asserts `tag == VERSION`, resolves the tag, skips if the
image already exists, runs the quality gate, builds, pushes, and restarts the
container.
- **Type B (site):** triggered on push, ships `:latest` and has no tag guard.
If the guard fails, nothing is published — fix the version file, amend the
commit, and move the tag **only** if it was never pushed.
---
## 🛟 Backfill / recovery
If an older tag never published (a past version-file mismatch, an index
outage):
- **Package:** run the `backfill-pypi-from-tags` pipeline (custom event). It
checks out every tag, verifies `tag == pyproject version` at that commit,
and uploads the ones missing from the index. Already-fixed history is never
rewritten.
- **Service:** rebuild the image for the missing tag with a fresh commit and a
new patch version; do not move a published tag.
---
## 🔁 If a release is wrong
- Never reuse or force-move a pushed tag.
- Fix forward: bump to the next patch, promote the changelog, tag again.
---
## Related
- [Testing & Quality Gates](01_testing.md) · [Versioning & Changelog](../lib/11_versioning_and_changelog/) · [Drone Pipelines](../lib/10_drone_pipelines/)