Skip to content

Package version improvements - #102

Draft
Christopher Obbard (obbardc) wants to merge 9 commits into
mainfrom
wip/obbardc/version-improvements
Draft

Package version improvements#102
Christopher Obbard (obbardc) wants to merge 9 commits into
mainfrom
wip/obbardc/version-improvements

Conversation

@obbardc

@obbardc Christopher Obbard (obbardc) commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Improve the package versioning to allow for future expansion.

Remaining:

  • Clean up commits a bit.
  • Test branch-tip builds.
  • Check package names are OK still.
  • Check respin tag works fine.
  • Check output of uname -r and systemd-boot menu generation.
  • Check PR build works fine.
  • Test build for all variants in staging.
  • Document versioning scheme & why it it's the best approach.
  • Pull all versioning information into the document and place references in their place.

systemd-boot sorts boot entries on the BLS 'version' key, which
kernel-install writes verbatim from uname -r. Under systemd's version
comparison:

  7.2.0-rc7-qcom-next-20260821  >  7.2.0-qcom-next-20260826

so every release candidate outranked the final release that followed it,
and an rc daily stayed the default boot entry after the release was
installed. The deciding comparison is a plain strcmp of "rc" against
"qcom", where 'r' > 'q'; there is no notion of a pre-release involved.

debian/rules already rewrites -rcN to ~rcN, but only for the Debian
version, because a package name may not contain '~' and the binary
packages are named linux-image-<KVER>. So dpkg ordered these correctly
all along and only the boot menu was wrong.

Joining the suffix with '+' fixes it: systemd compares the separator
before the chunk behind it, and '-' < '+'. This is not a local invention
-- it is why Debian's own kernels sort correctly
(linux-image-7.1.10+deb14-amd64). Upstream bindeb-pkg has the identical
~rc/-rc split and gets away with it for a different reason: it puts a
numeric commit count in that slot, and systemd ranks digits above
letters. qcom-next was the only scheme putting a lowercase word there.

The Debian version is unchanged, byte for byte: DATE_PART reads the date
off the end of LOCALVERSION, which still matches. Only uname -r and the
binary package names change.

Note this renames every binary package. Already-installed kernels under
the old names are not upgraded or removed by it -- they keep their
existing sort position until purged, so the fix applies to newly
installed kernels only.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The stub was a bare '0qli', which left a packaging-only rebuild -- same
kernel snapshot, changed debian/ -- with no field to bump. Neither of the
other two components can absorb it: suite_suffix_mapping is a per-suite
constant shared by every row, and the delivery suffix is the Daily/Release
marker. Bumping either to force a rebuild through would corrupt what they
mean.

Make the trailing digit part of the stub, so a rebuild is 0qli1 -> 0qli2 and
lands uniformly across every suite:

  0qli1~bpo13+1~  <  0qli1~bpo13+1  <  0qli2~bpo13+1

derive-debian-revision.sh now rejects a stub that does not end in a digit,
alongside the existing "must not end in ~" check. Both guard the same thing:
a stub that quietly loses a field it is supposed to carry.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
debian/rules recovered the snapshot date by matching the tail of
LOCALVERSION, a string it did not build and cannot safely parse. The variant
name in the middle is caller-supplied and unbounded, and a branch-tip suffix
ends in a 12-hex SHA that can be eight digits:

  $ echo '+qcom-next-g123456789012' | grep -oE '[0-9]{8}$'
  56789012

Such a build was versioned 7.2.0~rc7+56789012-0qli1, naming a snapshot date
that never existed and sorting it against real ones. Roughly one branch-tip
SHA in ten ends that way, so this is a live bug, not a theoretical one.

Anchoring the match would have fixed those refs while leaving the guess in
place. Carry the field instead. derive-localversion.sh already knows the
snapshot -- it matched the tag to build LOCALVERSION in the first place -- so
it now emits both:

  LOCALVERSION=+qcom-next-20260722
  SNAPSHOT=20260722

KEY=VALUE because the workflow appends them straight to GITHUB_ENV.
prepare-source.sh gains --snapshot and forwards it, and its tag auto-detect
derives both fields in one match. debian/rules takes SNAPSHOT as an input and
validates its shape rather than discovering it.

Neither caller reconstructs the field when it is absent. An explicit
--localversion replaces the derived suffix precisely because the ref no
longer describes the build, so parsing it back would reinstate the guess this
commit removes. Both paths warn that the Debian version will carry no dated
component, which is visible and correctable, unlike a date invented from a
SHA.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
A second tag cut on the same day (qcom-next-7.2-20260826.1) collapsed onto
the same version as the first, because every derivation matched only a bare
YYYYMMDD. derive-localversion.sh dropped the ordinal; prepare-source.sh
matched trailing digits with -([0-9]+)$, which a ".1" suffix cannot satisfy
at all, so the tag fell through to the +<whole-tag> fallback.

Match the date width explicitly with an optional ordinal after it, so the
component stays whole:

  qcom-next-7.2-20260826    -> +qcom-next-20260826    / 20260826
  qcom-next-7.2-20260826.1  -> +qcom-next-20260826.1  / 20260826.1

The first tag of a day deliberately keeps the plain form rather than
normalising to ".0". Both comparators read an absent ordinal as lower than a
present one, so the respin already sorts above it and every other build is
spared a ".0" it never needed.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The dated component was appended bare, as +20260722, which reads as part of
the upstream version rather than as a snapshot marker. Debian spells a
VCS snapshot +git<date>, so use that:

  7.2.0~rc3+git20260722-0qcom1
  7.2.0~rc3+git20260722.1-0qcom1

Ordering is unchanged -- the 'git' is a constant prefix on both sides of
every comparison, and the date behind it still decides.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The snapshot date identifies the tag, not the commit the tag pointed at when
the build was cut. A moved tag then produces two different kernels under one
version, indistinguishable in the archive and on a running system. Append the
commit to both strings:

  uname -r         7.2.0-rc7+qcom-next-20260826.1-gabcdef123456
  Debian version   7.2.0~rc7+git20260826.1~gabcdef123456-0qli1~bpo13+1

The separators differ, and the difference is load-bearing rather than
sloppy. systemd weighs the separator before the chunk behind it, so '-g<sha>'
leaves an absent respin ordinal sorting below a present one. dpkg instead
alternates digit and non-digit runs and reads an exhausted run as lower than
a letter, so the same '.' or '-' join would decide before reaching the
ordinal:

  7.2.0+git20260826.g3f2f3ca1a81e  >  7.2.0+git20260826.1.gabcdef123456

putting a respin below the build it respins. '~' sorts below everything
including the empty string, so the ordinal is always compared first and the
SHA only ever breaks a tie between builds sharing a snapshot. The
alternative -- spelling ".0" on every non-respin build -- buys the same
ordering at the cost of a redundant ordinal in every version forever.

What '~' costs is one reading: the version sorts below the same snapshot
without a SHA, as though it preceded it. Nothing occupies that slot, because
every snapshot build carries a SHA, so the claim is never tested against a
real version.

The SHA cannot order anything -- two SHAs have no relation -- so a moved tag
still needs its respin ordinal or the packaging revision bumped before users
can upgrade onto it. What this buys is that the two builds are never confused
for each other.

--sha becomes required for every build rather than just branch tips, and is
validated as hex, since it is now part of every kernel release rather than a
fallback identity for refs that had nothing else.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
(cherry picked from commit d6c478d, reworked)

A branch-tip build had no snapshot, so debian/rules emitted a version with no
+git<date> at all -- 7.2.0~rc7-0qli1~bpo13+1 -- which sorts below every dated
build rather than among them. Take the date from the HEAD commit instead, so
these builds land in the same sequence:

  7.2.0~rc7+git20260821~g07f50dc44edd-…    tag build
  7.2.0~rc7+git20260904~g07f50dc44edd-…    branch tip, dated by commit

The date is the committer date of HEAD, not the build date:

  - rebuilding a commit reproduces its version instead of inventing a higher
    one on each run
  - the date describes the source rather than when CI happened to run
  - it lands in the same space as upstream's tag dates, which track the
    commit each tag is cut from

Committer date rather than author date, because an author date can be months
old on a backported patch. Normalised to UTC, since --date=short would
otherwise render in each committer's timezone and make one commit yield
different dates.

The trade-off is that a commit date need not advance: rewinding the branch to
an older commit produces a lower version that apt will not offer as an
upgrade. Documented in derive-localversion.sh rather than left to be found.

The original replaced --sha with --date, because at the time the SHA was not
in the version and dating a build meant giving up identifying it. Both are
kept here: --sha is required for every build and --date only supplies what a
branch tip's ref cannot, so a branch-tip release now has the same shape as a
tag build rather than a second shape of its own.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
(cherry picked from commit d6c478d, reworked)

@gitclone@ and @Branch@ were substituted with empty strings, so every
changelog carried a bare "* Source:" naming nothing. Thread the clone URL,
ref and full SHA through prepare-source.sh so the entry says where the build
came from:

  * Source: https://github.com/qualcomm-linux/kernel qcom-next
  * Commit: 07f50dc44edd…

The original justified this as the only place the exact source was pinned,
since LOCALVERSION identified a build by date alone. That is no longer true
-- the version strings carry the commit -- but the repository and ref are
still nowhere else, and a 12-character abbreviation is not the commit itself.
The comments asserting otherwise are corrected rather than carried over.

prepare-source.sh gains one --git-sha taking the full SHA, not two. The
original added a second option under that name for the changelog, alongside
the existing one for the version field; bash takes the first matching case
arm, so the changelog copy would have been silently unset and every entry
would have read "Commit: unknown". They are the same commit at two widths, so
the short form is derived from the long one in a single place.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
Records why the kernel release and the Debian version are two strings rather
than one, and why each spells the same four fields differently: the '+' join
that keeps a release candidate below its final release under systemd, and the
'~' before the SHA that keeps a respin above the build it respins under dpkg.

Both separators look arbitrary and both are load-bearing, so the reasoning is
written down where someone tempted to normalise them will find it. Also
covers what the version deliberately does not carry -- the repository and ref
live in the changelog -- and the one case where a branch-tip version can go
backwards, when the branch is rewound to an older commit.

README.md loses its copy of the same reasoning, which had grown into a second
account that would drift, and keeps the format table plus a pointer here.

Scoped to qcom-next; the derivation is not variant-agnostic.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant