# 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 release the same way: the version bump and changelog promotion go **in the merge request**, and the tag is created on the merged commit. --- ## ๐Ÿงพ Before you start - You have an open **merge request** holding the change, and `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)` | --- ## ๐ŸŒฟ Release in the MR The version is known while the change is still a merge request, so the bump rides along with it. Do both edits **in the MR**: - **Promote the changelog** in `CHANGELOG.md`: move the `[Unreleased]` content under a new `## [x.y.z] - YYYY-MM-DD` heading, leave a fresh empty `[Unreleased]` on top, and add the compare links โ€” naming the **version/tag**, never a commit id: ```markdown [Unreleased]: https://git.aetoskia.com/aetos//compare/x.y.z...HEAD [x.y.z]: https://git.aetoskia.com/aetos//compare/x.y.(z-1)...x.y.z ``` - **Bump the version file** to the intended release version: `version = "x.y.z"` in `pyproject.toml` (packages) or the `VERSION` file (services/sites). --- ## ๐Ÿท๏ธ Tag the merged commit 1. **Merge or squash** the MR into `main`. 2. **Create the tag** on the resulting commit: `git tag x.y.z` (lightweight). 3. **Push the tag** (`git push origin x.y.z`) โ€” Drone builds the release. ### ๐Ÿค– Agent-assisted tagging The agent does not author or merge the MR and never tags before the merge. After you confirm the MR is merged/squashed and tell it to proceed, the agent: 1. Waits for that explicit go-ahead. 2. Finds the commit that introduced the bump: - package: `git log -S 'version = "x.y.z"' -n 1 --format='%H %s' -- pyproject.toml` - service / site: `git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION` 3. Verifies that commit's version file equals `x.y.z`, then tags **only** that commit: `git tag x.y.z `. 4. Does **not** push โ€” it asks you to run `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/)