Skip to content
2 changes: 1 addition & 1 deletion Agent Skills/open-source-audit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ publication. Removing the file in a new commit is not sufficient.
best proxy for "publishable".
- [ ] Version tags follow SemVer if the project has releases: ECMWF production
tags use the clean `x.y.z` form (no `v` prefix)
[Codex: Guidelines/External-Contributions.md].
[Codex: Languages/Versioning.md; Guidelines/External-Contributions.md].
- [ ] A `CHANGELOG.md` is not required — but if one exists, check it is
sane: entries match actual tags, no placeholder sections, no internal
references.
Expand Down
2 changes: 1 addition & 1 deletion Guidelines/Branching.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Branching Patterns

This document describes the branching patterns used across ECMWF repositories. The choice of pattern for a given repository is governed by [ADR-001: Git Branching Model](../ADR/ADR-001-Git-Branching-Model.md).
This document describes the branching patterns used across ECMWF repositories. The choice of pattern for a given repository is governed by [ADR-001: Git Branching Model](../ADR/ADR-001-Git-Branching-Model.md). Release version numbers and tags follow the [Versioning](../Languages/Versioning.md) policy (Semantic Versioning, clean `x.y.z`, no `v` prefix).

## Contents

Expand Down
2 changes: 2 additions & 0 deletions Guidelines/External-Contributions.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ Contractors may work in one of two ways:

### Tagging Rules

These tags follow ECMWF's [Versioning](../Languages/Versioning.md) policy (Semantic Versioning, clean `x.y.z`, no `v` prefix); the rules below cover the contractor prerelease and production tags specifically.

Contractors using this model may create prerelease tags for internal testing and staging:

```
Expand Down
6 changes: 3 additions & 3 deletions Languages/Python-Wheels.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Say we release multio with version A, depending on mir B and eckit C, with exact
Later on, a new version C+1 of eckit appears, and then we'd like to release multio A+1.
However, if there is no mir of version B+1, we are stuck -- the mirlib-B.wheel already exists, and it depends on eckit-A.wheel, not eckit-A+1.wheel.

To get around that limitation, we switch to 4-dimensional versioning, x.y.z.C, where C is a monotonic counter shared across all packages.
To get around that limitation, we extend ECMWF's base [Semantic Versioning](./Versioning.md) scheme to 4-dimensional versioning, x.y.z.C, where C is a monotonic counter shared across all packages (this is a wheel build identifier and does not change the software's `x.y.z` release version).
For the example above, we would first release eckit.A.1, mir.B.1 and multio.C.1.
And in the second run, we would release eckit.A+1.2, mir.B.2 and multio.C2, all with exact pins.
The mir.B.1 and mir.B.2 are seemingly the same, from the point of view of the compiled mir code -- but they actually differ in which version of eckit they were build against, and thus justify being separate wheels.
And in the second run, we would release eckit.A+1.2, mir.B.2 and multio.C.2, all with exact pins.
The mir.B.1 and mir.B.2 are seemingly the same, from the point of view of the compiled mir code -- but they actually differ in which version of eckit they were built against, and thus justify being separate wheels.
Comment thread
tlmquintino marked this conversation as resolved.
Outdated
Comment thread
tlmquintino marked this conversation as resolved.
Outdated

This has an additional benefit of allowing simple check of ABI compatibility of a given python environment.
List all installed wheels and check that their fourth versioning number is _exactly_ equal.
Expand Down
2 changes: 1 addition & 1 deletion Languages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ Language-specific guidelines and tooling for ECMWF software projects.

- [C++](./C++/README.md) — coding standards, clang-format and clang-tidy configuration
- [Python Wheels](./Python-Wheels.md) — building and using Python wheels with compiled libraries
- [Versioning](./Versioning.md) — versioning guidance for multi-language repositories
- [Versioning](./Versioning.md) — ECMWF's Semantic Versioning policy: tag format (`x.y.z`, no `v` prefix), MAJOR/MINOR/PATCH bump rules, production vs prerelease tags, and single-version guidance for multi-language / multi-package repositories
117 changes: 115 additions & 2 deletions Languages/Versioning.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,116 @@
# TODO
# Versioning

How do we manage multi-language repositories with a single version number?
ECMWF software is versioned with **Semantic Versioning (SemVer)**. This document
is the authoritative policy for how to version an ECMWF repository — the tag
format, when to bump each component, prerelease and production tags, and how to
version a repository that contains multiple languages or packages with a single
version number.

It consolidates the version-tag rules used in
[Branching](../Guidelines/Branching.md) and
[External Contributions](../Guidelines/External-Contributions.md); those
documents govern *how* releases are branched and delivered, while this document
defines *how the version number itself is chosen and applied*.

## Semantic Versioning

Versions have the form:

```
MAJOR.MINOR.PATCH e.g. 2.4.1
```

Given a public interface (a library API, a CLI, a data/format contract, a wire
protocol), increment the:

- **MAJOR** version for **incompatible / breaking changes** — anything that
requires downstream users to change their code, commands, configuration, or
data handling.
- **MINOR** version for **backwards-compatible new functionality** (and for
marking features as deprecated).
- **PATCH** version for **backwards-compatible bug fixes** only.

See [semver.org](https://semver.org) for the full specification.

### No `v` prefix

ECMWF version tags use the **clean `x.y.z` form with no `v` prefix** (`2.4.1`,
not `v2.4.1`). This is checked by the `open-source-audit` skill. Note that this
applies to *ECMWF release tags*; third-party GitHub Actions and other external
artefacts keep their own tag conventions (e.g. `actions/checkout@v4`).

### Initial development (`0.y.z`)

A project below `1.0.0` is in initial development: **anything may change at any
time** and the public interface is not considered stable. Reach `1.0.0` when the
interface is stable and the software is used in anger. New public repositories
usually start at a low [Project Maturity](../Project%20Maturity/README.md) level,
which is a better signal of stability for users than the version number alone.
Comment thread
Copilot marked this conversation as resolved.

### Deprecation and compatibility

- Prefer to **deprecate before removing**: introduce the replacement, mark the
old interface deprecated (a MINOR release), keep it working for at least one
MINOR cycle, then remove it in a MAJOR release.
- Document breaking changes prominently (release notes / `CHANGELOG`), and a
`CHANGELOG` is recommended so users can see what changed between versions.

## Production and prerelease tags

- **Production tags** are clean `x.y.z` tags on `main`/`master`. Only such tags
represent production-ready software.
- **Prerelease tags** use the form `x.y.z-upstream.N` (`N` = a sequential
prerelease number), for contractor-side testing, staging, and CI. They are
**non-production**, must not appear on `main`/`master`, and must never be
deployed beyond development or test environments.

The full rules, and the delivery/acceptance workflow they belong to, are in
[External Contributions → Tagging Rules](../Guidelines/External-Contributions.md).
The mechanics of creating and pushing release tags are in
[Branching](../Guidelines/Branching.md).

## Single source of truth

A repository has **one** version number. Do not maintain the version in several
places that can drift. The **git tag is authoritative**; build tooling should
derive the package/library version from it rather than hard-coding it — for
example `setuptools_scm` for Python, or a single top-level `VERSION` file (or the
`project.version` in `pyproject.toml` / the CMake `project(... VERSION ...)`)
that the build reads. A generated version file (e.g. `_version.py`) is a build
artefact and must not be committed (see [Repository Structure](../Repository%20Structure/README.md)).
Comment thread
tlmquintino marked this conversation as resolved.
Outdated

## Multi-language and multi-package repositories

A repository that contains more than one language or produces more than one
artefact (for example a C/C++ library with Python bindings, or a package plus
its CLI) is versioned as **a single unit under one version number**:

- **One version for the whole repository.** All components and artefacts built
from a commit share the same `x.y.z`, are tagged once, and are released
together. Do not version the Python bindings independently of the C++ library
they wrap within the same repository.
- **Bump for the repository as a whole.** A breaking change in *any* public
component is a MAJOR bump for the repository; new backwards-compatible
functionality in any component is a MINOR bump; and so on. This keeps a single,
unambiguous compatibility signal for downstream users.
- **If components genuinely need to evolve on independent version lines**, that
is a signal they should live in **separate repositories**, each with its own
single version, rather than being force-fitted into one.

### Compiled Python wheels

Where a repository publishes compiled Python wheels split across interdependent
packages (e.g. a library wheel and its Python interface wheel), the base `x.y.z`
scheme is extended to a **four-component `x.y.z.C`** form, where `C` is a
monotonic counter shared across the packages so that matching builds can be
pinned together. This mechanism, and its current limitations, are described in
[Python Wheels](./Python-Wheels.md); it does not change the `x.y.z` release
version of the software, only the wheel build identifier.
Comment thread
tlmquintino marked this conversation as resolved.
Outdated

## Summary

- Use SemVer `MAJOR.MINOR.PATCH` (`x.y.z`), **no `v` prefix**.
- MAJOR = breaking, MINOR = backwards-compatible feature, PATCH = backwards-compatible fix; `0.y.z` = unstable.
- Production tags are clean `x.y.z` on `main`/`master`; prereleases are `x.y.z-upstream.N` (non-production).
- One version per repository; the git tag is the single source of truth; all languages/artefacts release together.
- Compiled Python wheels may use `x.y.z.C` for build pinning (see [Python Wheels](./Python-Wheels.md)).
2 changes: 1 addition & 1 deletion Software Management Plan/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Include:
- Branching model
- Merge criteria, e.g. pull requests with mandatory X reviews, all CI/CD test pass
- **Release strategy**
- Versioning system, e.g. *"Semantic versioning (MAJOR.MINOR.PATCH)"*
- Versioning system — ECMWF uses Semantic Versioning (MAJOR.MINOR.PATCH); see the [Versioning](../Languages/Versioning.md) policy
- **Maintenance**
- How will future developments, and user requests, be managed?
- **Maturity timeline**
Expand Down
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Contributions addressing any of these items are welcome via pull request.
## High Priority

- [ ] **Guidelines/Testing.md** — File opens with an explicit `TODO`. No actual testing guidelines exist beyond test data hosting via `sites.ecmwf.int`. Needs unit, integration, and regression testing standards.
- [ ] **Languages/Versioning.md** — Entire file is a single-line placeholder: *"How do we manage multi-language repositories with a single version number?"*. Needs a versioning policy.
- [ ] **LICENSE** (root) — File is empty. The licence text exists in `Legal/Apache-Licence` but the root `LICENSE` file that GitHub uses for badge detection and display contains no content.

## Medium Priority
Expand Down