Skip to content

Versioning & Changelog

Summary

Every Aetos artifact — library, service, or site — is versioned with Semantic Versioning and documents its releases in a Keep a Changelog file. The version file is the source of truth, the git tag must equal it, and a Drone guard enforces that equality before anything is published. Releases are cut manually; a tag is what triggers the pipeline.

This page pairs with 10 – Drone Pipelines: the version and tag contract is exactly what those two CI shapes consume.


The version contract

Artifact Source of truth Notes
Library (PyPI package) version in pyproject.toml check-version also reads it to skip duplicates
Service / site (Docker) VERSION file at the repo root one line, x.y.z

Rules:

  1. One version per repository. No per-module versions.
  2. Tag = version. The git tag is bare SemVer (0.0.6), no v prefix, and it MUST match the version file byte-for-byte. The Drone guard fails the build otherwise — this is what stops a tag from silently publishing nothing.
  3. A release is a tag. Bump the version file, promote the changelog, commit, tag, push. Everything else is automated by Drone.
  4. Never reuse a tag. If a release is wrong, cut a new patch version.

Pre-1.0 (0.y.z)

While a package is 0.y.z, 0.y is treated as the major line:

  • breaking change → 0.(y+1).0
  • new feature → 0.y.(z+1)
  • bug fix → 0.y.(z+1)

Once a package reaches 1.0.0, normal SemVer applies. Do not ship 1.0.0 until the public API is considered stable.


The changelog

CHANGELOG.md at the repo root, in Keep a Changelog form. It always has exactly one open [Unreleased] section with the change categories that apply:

1
2
3
4
5
6
7
8
## [Unreleased]

### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security

On release you promote it — never delete history:

1
2
3
4
## [0.0.6] - 2026-03-07

### Added
- ...

and add a link reference at the bottom:

[Unreleased]: https://git.aetoskia.com/aetos/<repo>/compare/0.0.6...HEAD
[0.0.6]: https://git.aetoskia.com/aetos/<repo>/compare/0.0.5...0.0.6

Conventions:

  • Write entries for humans: one line each, imperative, referencing the user impact, not the commit hash.
  • Categories: Added, Changed, Deprecated, Removed, Fixed, Security. Omit empty categories.
  • Security fixes get their own entry even if the change is otherwise a bug fix.
  • The [Unreleased] heading is never renamed; copy its content into the new version heading and clear it.

Release checklist

  1. Confirm the quality gate is green on main.
  2. Move every [Unreleased] entry under the new version heading with today's date (## [x.y.z] - YYYY-MM-DD); restore an empty [Unreleased] on top.
  3. Add the two compare-link references at the bottom of the changelog.
  4. Set the version file: pyproject.toml (version = "x.y.z") or VERSION.
  5. Commit: chore(release): x.y.z.
  6. Tag: git tag x.y.z and push the tag (git push origin x.y.z).
  7. Watch Drone: Type A publishes the wheel/sdist, Type B builds and deploys the image. Both first assert tag == version.
  8. Do not reuse the tag if the run fails — fix and cut x.y.(z+1).

Why the guard matters

The version file and the tag are two facts that must agree; nothing in git enforces that by itself. A mismatch is not a theoretical risk — a package can be tagged 0.0.2 while its pyproject.toml still says 0.0.1, and the publish step (which compares the file against the index) then skips the build entirely, so 0.0.2 never reaches the index while looking like a release. The Drone guard turns that silent no-op into a hard failure.