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
2 changes: 1 addition & 1 deletion Languages/Python-Wheels.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ 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.
Comment thread
Copilot marked this conversation as resolved.
Outdated
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