Version string format¶
Choosing the appropriate version can be complex since there are numerous conditions to consider. The following paragraphs first provide links to essential foundational information, shared with Debian.
Subsequently, each paragraph addresses specific scenarios that may arise when uploading to Ubuntu.
The Version field¶
Since Ubuntu’s versioning specifics are derived from Debian, it is beneficial to begin by thoroughly reviewing the Debian control field “Version” to understand the foundational principles. The key takeaway is that Debian versions are formatted as:
[upstream_version]-[debian_revision]
using the -
to split the upstream version from the Debian packaging revision.
When we modify a package compared to what’s shipped by Debian, we indicate the change by adding ‘ubuntu’ to the Debian revision.
One can think of an Ubuntu version number as consisting of three components:
[upstream_version]-[debian_revision_component]ubuntu[ubuntu_revision_component]
The ubuntu
string then marks that whatever follows it is related to changes
added in Ubuntu (ubuntu
isn’t the only string used here, but more on that
shortly).
This distinction allows each party involved in providing a package to the users – Upstream, Debian and Ubuntu – to modify and iterate on their section of the version number without interfering with others.
When everyone abides by these conventions we can guarantee package upgradability, but also allow that one can understand some of the package history by just looking at the package version.
Version: Adding a change in the current Ubuntu development release¶
As shown above, [upstream_version]-[debian_revision]ubuntu[ubuntu_revision]
is
the basic pattern. Changes within the development release require adding or
incrementing the number immediately following the ubuntu
string.
Consequently, the first Ubuntu modification would have ubuntu1
appended to the
Debian revision. Subsequent Ubuntu changes would increment this suffix, i.e.
ubuntu1 --> ubuntu2
.
Example one: Adding a change to
2.0-2
in the Ubuntu development release will use2.0-2ubuntu1
Example two: Adding another change to
2.0-2ubuntu1
in the Ubuntu development release will use2.0-2ubuntu2
As alluded to earlier, there are a number of special cases, and situations where
we use a string other than ubuntu
in Ubuntu version numbers.
One such case is build
, typically used for
no-change rebuilds that will be discussed
in more depth below. The key thing to know when incrementing from a buildN
is
that the package is considered unchanged from the Debian revision component, and
incremented as a new Ubuntu change.
Here’s a listing of examples of the application of these rules:
Previous version |
Modification type |
Recommended version |
---|---|---|
|
Ubuntu change |
|
|
Ubuntu change |
|
|
Ubuntu change |
|
|
Re-Sync w/ Debian |
|
|
No-change rebuild |
|
|
No-change rebuild |
|
|
Ubuntu change |
|
While these examples cover common scenarios, it’s important to note that they apply specifically to the current Ubuntu development release. Stable releases have different policies, but first we’ll explore some special cases for development releases.
Version: Syncing from Debian¶
Sometimes the desired change has already been packaged by Debian in a newer version of the package. During the Ubuntu development cycle, the Archive tooling automatically copies or “syncs” new package versions from Debian.
Only packages with no Ubuntu modifications or those with no-change updates
(i.e., versions with build
rather than ubuntu
in the revision), are
automatically synced.
If these updates pass Ubuntu’s automated testing, they do not require manual intervention. However, there are scenarios where the automation cannot handle the process.
First, once the development release reaches Debian import freeze, the sync automation is disabled.
Therefore, if an update from Debian is required, it must be manually synced. Version numbering in this case is trivial: It’s the exact same version as in Debian.
A second case is when the package does have Ubuntu changes, but our modifications no longer need to be carried as Ubuntu delta with the new Debian package.
For example, perhaps the Ubuntu changes were cherry-picked patches from upstream that are now available in a newer release now available from Debian, or perhaps Debian has adopted all of the Ubuntu changes into their own packaging. These situations will also require a manual re-sync with Debian. The version number also remains identical to Debian (see the ‘Re-Sync w/ Debian’ example in the previous section).
Version: No-change Rebuilds¶
Sometimes a package has previously successfully synced into Ubuntu, and still does not require changes to the packaging or source code, but the generated binaries do require rebuilding against newer versions of dependencies.
A common example of this is a library being updated to a newer ABI, such that a package depending on it needs to be re-linked to continue functioning properly. The process of updating packages dependent on a changed library like this is called a transition.
Rebuilding is not considered a change that should prevent future syncing, so in this special case one would:
Not add
ubuntuX
and instead add abuildX
suffixIf there is already one such
buildX
suffix present, increment itIf there is already an
ubuntuX
suffix there would be no auto-sync anyway, so increment the existing suffix
So far it is weakly defined if a no change rebuild for
native packages shall be a version increment
or adding a buildX
suffix.
Both styles are present in the Archive and both will work just fine. Until this is properly defined, one should try to follow what the particular package used so far.
Example: A Debian package
2.0-2
needs a rebuild in Ubuntu which would use2.0-2build1
List of this and further related examples:
Previous version |
No-change rebuild in devel |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It is unlikely, but possible that one needs a no-change rebuild as part of a Stable release update, but in this situation auto-syncing isn’t active anyway and the need for upgradability applies. So a no-change-rebuild in regard to picking a version number in this case is identical to any other change (see the Version: Adding a change in Ubuntu as a stable release update section).
Version: Merging from Debian¶
If Ubuntu has delta to the package from Debian, it can not be automatically synced otherwise that delta would be lost. In that case an Ubuntu developer regularly (usually at least once per Ubuntu release) merges the delta in Ubuntu with the content from Debian and indirectly also with the changes by upstream.
The counter for Ubuntu increments resets to 1, therefore the version for the new
upload to the Ubuntu development is the version from Debian with a suffix of
ubuntu1
.
Example: Uploading
3.1
to Ubuntu-devel
based on Debian3.1-2
retaining former Ubuntu delta of2.1-1ubuntu2
would become3.1-2ubuntu1
List of this and further related examples:
Old-Debian |
Old-Ubuntu |
New-Debian |
Ubuntu Devel |
---|---|---|---|
|
|
|
|
|
|
|
|
Version: Merging from Upstream¶
The mentioned special case of an upstream merge is triggered when Ubuntu merges directly from upstream ahead of Debian upstream. In this case the new version shall:
Reflect that it was not yet packaged in Debian (at this point in time) and thereby is not really based on a Debian version by using
-0
as the Debian version suffix.Represent that this is the first devel upload via
ubuntu1
which also serves as a place increment for further uploads to the development release.
Example: Uploading
3.1
to Ubuntu-devel
while Debian is not yet at3.1
, so we pick3.1
followed by-0
representing it is not based on Debian andubuntu1
to index and iterate changes to Ubuntu to an overall3.1-0ubuntu1
.
List of this and further related examples:
Old-Debian |
Old-Ubuntu |
New-Debian |
New-Upstream |
Ubuntu Devel |
---|---|---|---|---|
|
|
unchanged |
|
|
|
|
unchanged |
|
|
|
|
unchanged |
|
|
Version: Adding a change in Ubuntu as a stable release update¶
After a version of Ubuntu is released, changes to packages follow a slightly different versioning scheme which ensures upgradability to later releases and allows the history of changes to be recognized from just looking at the version.
The only change from the normal Ubuntu version scheme is to the
ubuntu revision section of [upstream_version]-[debian_revision]ubuntu[ubuntu_revision]
.
Increment Y in the numeric
ubuntuX.Y
suffix, like:ubuntu3.1 -> ubuntu3.2
.Never increment X in the numeric
ubuntuX.Y
suffix. An Ubuntu delta is always captured in Y.If this is the first change via the SRU process, add the
.1
suffix, like:ubuntu3 -> ubuntu3.1
.If no
ubuntuX
was present before, then setubuntu0.1
which will represent that there was no Ubuntu delta (0
) before this upload which is the first to add a change (.1
).If two releases have the same version, then this would make the package non-upgradable and cause a version conflict (two builds, but not the same). To resolve this, add the numeric
YY.MM
release version in betweenubuntuX
and the increment.1
. For Jammy that suffix might look likeubuntu3.22.04.1
Compare these examples with the adding a change in the current Ubuntu development release section above to better see the subtle difference.
Example one: Adding a change to
2.0-2
in an Ubuntu stable release update will use2.0-2ubuntu0.1
.
Example two: Adding an Ubuntu delta to
2.0-2ubuntu2.4
in an Ubuntu stable release update will use2.0-2ubuntu2.5
.
List of these and further related examples:
Previous version |
Recommended version |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Version: native packages¶
There exist packages in which Debian or Ubuntu are themselves the upstream.
This is due to the classification as native or non-native package as
outlined by the referred
Debian control field version
and in more detail Debian source packages.
To quote: “Presence of the debian_revision
part indicates this package is a
non-native package. Absence indicates the package is a native package.”
Due to that, in a Debian native package there is no -debian_revision
.
Example: A package native to Debian
2.0
(no-
), getting an Ubuntu change in the-devel
release would use2.0ubuntu1
.
This continues into native Ubuntu packages, which also do not have a
-debian_revision
. But remember that the package namespace is shared between
Debian and Ubuntu, therefore a native Ubuntu package foo
of version 1.0
could be overwritten if Debian ever adds foo
> 1.0
(if not blocked the newer
according to dpkg --compare-versions
will be synced).
That would even happen automatically via the
auto-sync.
To avoid this, it is recommended to add a ubuntu0
suffix to the package.
We’d also like to be able to differentiate between “a native Debian package that
got an Ubuntu Delta added” which could be 2.0ubuntu1
, and “a native Ubuntu
package”. Therefore the marker suffix for a native package shall be ubuntu0
and not itself be incremented.
Furthermore, native package versioning is package-dependent; whether it uses only major, or a major.minor, or any other version pattern is the maintainer’s choice. Just as upstream can and will version software the way they consider the best. Due to that, selecting the right subsequent version requires you to check the package history or confer with its maintainer.
Example: A package native to Ubuntu
2.0ubuntu0
(no-
andubuntu0
) getting an Ubuntu change in the-devel
release could use2.1ubuntu0
or3.0ubuntu0
_
Lists of these and further related examples:
Native in Debian:
Previous version |
Devel upload |
SRU upload |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Native in Ubuntu:
Previous version |
Devel upload |
SRU upload |
---|---|---|
|
|
|
|
|
|
Note
The rule of multiple releases with the same version needing to also add a
per-release YY.MM
to differentiate and ensure upgradability might apply here as
well.
Note
There might be reasons that the maintainer wants the native Ubuntu package to
not have a ubuntu0
suffix, for example if overwriting via auto-sync is desired.
An example might be coordinated uploads to both Distributions in freeze times when the auto-sync is disabled.
In any such case that deviates from the recommendation to have a ubuntu0
suffix, the package should have an entry in debian/README.source
that explains
the reasoning, to allow fellow packagers to understand.
If the deviation is expected to not last very long, for example to be resolved
in the next Ubuntu development cycle, then a note in debian/changelog
is
sufficient.
Version: Backport from upstream¶
There are more special cases, especially related to backports (as in backports of new versions via SRU, not as in backports.
In the rare case of a new upstream release being pushed to all stable releases it will lose all its former version suffixes. This is a common practice on some minor release exception processes, where we pick up the most recent upstream content, but avoid regressions/changes due to packaging changes like moving files, splitting or removing binary packages etc. To represent this the upload should:
Signal that it is not based on a Debian packaging version by using
-0
as the Debian packaging revision.Signal that it was not yet packaged in this Ubuntu release before via
ubuntu0.
.Add a suffix per Ubuntu release like
.22.04
.Add an increment that can be used in subsequent individual per-release SRU uploads like
.1
.
Example: Uploading
3.1
but packaging-wise using what is already in LTS and LTS+1 – not with all the Ubuntu changes that might be in3.1-1ubuntu2
.
List of this and further related examples:
Old |
New |
|
---|---|---|
LTS |
|
|
LTS+1 |
|
|
LTS+2 |
|
|
The new version is independent of the former version of the target release. Here is a list of examples targeting 22.04:
Previous |
New upstream |
Upload for LTS |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Version: Backport from Ubuntu devel¶
But if instead the backport is actually without meaningful differences to what is in the current Ubuntu development release (a common practice for some packages that keep the same version everywhere) with only minimal backporting adaptations it would instead:
Signal that is it basically identical but backported by starting with the same version as the one in the development release.
Add a
~
to ensure it sorts earlier than the version in-devel
.Add a suffix per Ubuntu release like
22.04
.Add an increment that can be used in subsequent individual per release SRU uploads like
.1
.
Example: Uploading
3.1
to LTS and LTS+1 being more or less identical to what is in -devel in3.1-1ubuntu2
.
List of this and further related examples:
Old |
New |
|
---|---|---|
LTS |
|
|
LTS+1 |
|
|
LTS+2 |
|
|
The new version is again independent of the former version that was present in the target release. Here are examples targeting 22.04:
Previous |
New development |
Upload for LTS |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Note
If in this case the package in the Ubuntu development release is a native package one would still use the same version as in the development release.
Example: Uploading
3.1
to LTS and LTS+1 being more or less identical to what is in-devel
in3.1
.
List of this and further related examples:
Old |
New |
|
---|---|---|
LTS |
|
|
LTS+1 |
|
|
LTS+2 |
|
|
Version: almost native packages¶
Let us be clear, almost native packages is not a clear definition, but some have evolved that way.
An example is snapd
, which is packaged like a native package in Ubuntu with
versions like 2.67+25.04
, the content matches 1:1 what is upstream (including
the debian/*
being there), and being packaged as 3.0 (native)
.
But the package versions are nowadays no more defined by these native uploads
to Ubuntu. There actually is a
separate upstream project with proper releases.
Furthermore the Debian packaging switched over to proper upstream/downstream
packaging with orig tarball, Debian additions and a version like 2.67-1
and
format being 3.0 (quilt)
.
Due to that, some of the normal rules no more apply. Changing the example
2.67
to 2.67.1
is no more just an Ubuntu-native version choice. It
represents a new upstream version in the upstream project which might not exist
or not match.
Furthermore the example of snapd
is also usually backported with the same
content to all active releases. Combined that makes this an uncommon mix of the
rules usually applied to:
Native package
Backport from upstream
Let us define how such a case could be handled for the example of snapd
:
The first element of the version matches the upstream version it represents, like
2.67
Since this is backported to multiple releases at the same version, but being native can’t have an
-
like in the usual-X
or-XubuntuY
– but it can have the suffix is added via+ubuntuYY.MM
Note: former versions just added
+YY.MM
but for symmetry and to avoid e.g. conflicts with the auto-sync process+ubuntuYY.MM
should be the way to go
If iterations on the same upstream version and for the same target Ubuntu release are needed add an increment counter
.x
as suffix
Previous |
New upstream 2.67 |
Changes inside of 2.66 |
---|---|---|
LTS: |
|
|
Devel: |
|
|
LTS: |
|
|
Devel: |
|
|
Note
Yes, no more being a real native package means this could also become
2.68-0ubuntu1
and thereby the more “normal” rules would apply, but
until the release process can handle it the above is an example how we can
apply the same rules outlined here to almost any case and still ensure
upgradability and some reasonable instant insight looking at only the
version number.
Version: Undoing mistakes¶
In a very rare case where an upgrade to a new upstream version of a package
caused major regression and the only way out is rolling back, the convention is
to insert +really
followed by the actual version number.
To be clear, both should be used sparingly and only if really needed. Please also have a look at +really in control fields.
Example: Uploading
3.1
caused a regression, so rolling back to the previous version2.0-2ubuntu2
would use3.1+really2.0-2ubuntu2
List of this and further related examples:
Before |
Current |
Upload |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Further reading¶
Right at the beginning we had the link to the underlying concepts in Debian referring to the Debian control field “Version” for overall topic awareness.
But of course there are more special cases, one that you might encounter is package uploads based on repository content. This is most commonly done:
If upstreams do not yet have a new release, but we’d like to have all the content up to a certain point in time/git
In cases where the upstream release is imminent, but to meet freeze deadlines one uploads the state from git (but going to final before the release)
If upstream is only using nightly builds and you need to pick one of them
At a high level you’d either go before the next version
{upcoming_version}~git...
or, if this seems more appropriate to the case,
ahead of the current {current_version}+git...
.
The former is more common, the latter a special case, for example considered if nobody knows if/when/what the new revision will be.
The underlying principles are outlined in more detail in the Debian Wiki.