Skip to content

fix: compare Debian-style versions correctly and protect active sysext version#113

Merged
bketelsen merged 1 commit into
mainfrom
fix/debian-version-compare
May 21, 2026
Merged

fix: compare Debian-style versions correctly and protect active sysext version#113
bketelsen merged 1 commit into
mainfrom
fix/debian-version-compare

Conversation

@bketelsen

Copy link
Copy Markdown
Contributor

Problem

The docker.raw sysext symlink ended up dangling: it pointed at docker_5+29.4.2-2~debian.13~trixie... but no such file existed on disk, so systemd-sysext could not merge the docker extension.

Root cause

version.Compare passed Debian-style versions like 5+29.4.2-2~debian.13~trixie to hashicorp/go-version, which treats everything after + as SemVer build metadata and excludes it from precedence comparison. Every docker version therefore collapsed to core 5.0.0 and compared equal.

VacuumWithDetails runs after the new symlink is created and keeps the newest InstancesMax versions. With all versions comparing equal, the sort was a no-op and "keep newest N" deleted the freshly downloaded image (which sorted last lexically), leaving the dangling symlink.

Fix

  • version/pattern.go — versions containing ~ or : are routed to a dpkg-compatible comparator (epoch → upstream → revision, with ~ sorting before everything, including end-of-string). The SemVer path is unchanged, so existing prerelease/v-prefix/date behavior is preserved.
  • sysext/manager.goVacuumWithDetails now never removes the active (symlinked) version regardless of sort order. Defense-in-depth that prevents a dangling symlink even if the comparator is wrong.

Tests

  • TestCompare: Debian upstream/epoch/revision/tilde ordering, including the exact 5+29.4.2-2~... vs 5+29.3.1-1~... case.
  • TestVacuumWithDetailsKeepsActiveSymlinkedVersion: reproduces the failure (symlink points at the oldest version) and asserts the active file is never deleted.

Validation

Built the patched binary and ran it against the live repo: docker 29.5.2 downloaded, symlink repaired, and the new image survived vacuum (it pruned the oldest 29.3.0 instead). docker --version29.5.2, merged into /usr.

🤖 Generated with Claude Code

…t version

The version comparator passed Debian versions like
"5+29.4.2-2~debian.13~trixie" to hashicorp/go-version, which treats
everything after "+" as SemVer build metadata and excludes it from
precedence. Every docker version collapsed to "5.0.0" and compared
equal, so Vacuum (which runs after the symlink is created) could not
distinguish new from old and its "keep newest N" deleted the freshly
downloaded image, leaving a dangling docker.raw symlink.

- version: route versions containing "~" or ":" to a dpkg-compatible
  comparator (epoch, then upstream, then revision; "~" sorts before
  everything). SemVer path is unchanged.
- sysext: Vacuum now never removes the active (symlinked) version,
  regardless of sort order, as defense-in-depth against dangling links.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bketelsen bketelsen merged commit 04fa217 into main May 21, 2026
7 checks passed
@bketelsen bketelsen deleted the fix/debian-version-compare branch May 21, 2026 18:08
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