There is a certain level of misunderstanding between GNOME maintainers as to how to adapt to new versioning schemes, in light of the versioning scheme introduced two years ago with GNOME 40.
I am going to try and address the misunderstanding and offer a few options for maintainers; if you are a maintainer of a GNOME module, you’re kindly encouraged to follow the best practices, instead of making your own, in the interest of consistency and predictability.
Remember: version tuples don’t mean anything by themselves. You have to decide what kind of ordering and meaning you impose on them. The recommendations in this topic are, of course, advisory, but they should be taken in the same stride as the keywords in RFC 2119: conformance is beneficial for all parties involved, and any deviation should be weighted against all possible downsides.
Rule 0: versions are not numbers
The fact that version tuples can be composed by numbers does not imply that versions are, or behave like natural numbers. Don’t treat them as numbers, and don’t assume that just because some of their components look like numbers they have the same properties as numbers.
Version numbers alone don’t tell you that a release is newer or older than another; version numbers alone don’t tell you that a release is stable or unstable; version numbers alone don’t tell you if it’s safe to upgrade from one release to another.
Do not make the mistake of thinking that version numbers are, by themselves, self-explanatory.
If you follow the tenets of Rule 0 you’ll avoid leading yourself, the people packaging your project, and the people using your project, into a deep, dark, rabbit hole of pain.
Versions
Let us assume we have a library, called libexample
; this library follows the GNOME development cycle:
- six months development followed by an arbitrary number of stable releases
- the API is grouped under a single namespace
- the API’s namespace has a “major” version
- stable releases can only fix issues without introducing new API
- new symbols can only be added during a development cycle
- old symbols can only be deprecated during a development cycle
- no symbol can be removed or changed within the same API version
See also: API stability.
Given the nature of the cycle, a version identifier will typically have three components:
- a “major” component, indicating the API version
- a “minor” component, indicating the current cycle within the same API version
- a “micro”, or “patch” component, indicating the release within the same cycle
The maintainer of libexample now has the option of choosing a versioning scheme, roughly based on the “semantic versioning” rules:
Option 1a: Classic even/odd versioning scheme
Releases made during the development cycle have an odd number for the “minor” component; releases made during a stable cycle have an even number for the “minor” component.
Examples
- 1.1.2: development release
- 1.2.0: stable release
- 1.3.3: development release
- 1.2.5: stable release
Option 1b: Cairo-style even/odd versioning scheme
An elegant versioning scheme, for a more… civilized age.
Works just like the even/odd scheme of Option 1a, with these additional rules
- the “micro” component in source revision is always odd
- the “micro” component for releases is always even
- a value of “0” for the “micro” component is always reserved for the first stable release
Example
- 1.1.0: does not exist
- 1.1.1: development version in the code repository
- 1.2.0: first release of the 1.2 stable cycle
- 1.2.1: stable version in the code repository
- 1.3.2: first development snapshot of the 1.3 development cycle
Option 2: Monotonic version with initial target update
The minor version number is increased at the beginning of the development cycle.
Development snapshots use a different identifier in the “micro” component.
Examples
- 1.1.0: first stable release of the 1.1 cycle
- 1.2.alpha: first developers snapshot of the 1.2 cycle
- 1.2.beta: second developers snapshot of the 1.2 cycle
- 1.2.rc: release candidate of the 1.2 cycle
- 1.2.0: first stable release of the 1.2 cycle
- 1.3.alpha: first developers snapshot of the 1.3 cycle
- 1.3.0.rc: does not exist
Note: The alpha/beta/rc identifiers are the micro component. DO NOT use them as suffixes.
Option 3: Monotonic version with final target update
The minor version number is increased at the end of the development cycle.
Development snapshot use a different identifier in the “micro” component.
Examples
- 1.1.0: first stable release of the 1.1 cycle
- 1.1.90: first developers snapshot of the 1.2 cycle
- 1.1.91: second developers snapshot of the 1.2 cycle
- 1.1.99: release candidate of the 1.2 cycle
- 1.2.0: first stable candidate of the 1.2 cycle
- 1.2.90: first developers snapshot of the 1.3 cycle
Further considerations
Additional snapshots
If you are doing additional releases, you can either choose to increase the micro component—typically reserved for stable cycles; or you can append an additional “nano” component to the version tuple, e.g.
- 1.1.alpha.1: second alpha snapshot of the 1.1 development cycle
- 1.2.90.1: second alpha snapshot of the 1.3 development cycle
Version bumps
You are strongly encouraged to follow a policy of post-release version bump. In other words: the micro component of your project inside the source code repository should always be higher than the micro component of the archive you just released.
You have the option of using the next version, or—if you’re using a Cairo-like versioning scheme—you can use a separate identifier for the code in the source repository. The important part is that other projects can choose whether to depend on released or unreleased code.
Ancillary versioned files
Always remember that your pkg-config file and your inclusion path must be versioned to allow for parallel installability. You SHOULD follow the instructions in the maintainers guide.